├── .gitignore ├── .isort.cfg ├── LICENSE ├── README.md ├── api ├── __init__.py └── apps │ ├── __init__.py │ ├── abnormal │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── filters.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ └── views.py │ ├── core │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── decrators.py │ ├── middleware.py │ ├── migrations │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── pagination.py │ ├── serializers.py │ ├── tests.py │ ├── validators.py │ └── views.py │ ├── inspection │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── filters.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ └── views.py │ ├── notices │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── filters.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ └── views.py │ ├── reptile │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── epidemic_data.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── utils │ │ ├── __init__.py │ │ ├── common.py │ │ ├── getSign.py │ │ └── hash.py │ └── views.py │ ├── users │ ├── Inconsolata.otf │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── avatar_generator.py │ ├── filters.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── signals.py │ ├── tests.py │ └── views.py │ └── vaccinate │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── filters.py │ ├── migrations │ ├── 0001_initial.py │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ └── views.py ├── config ├── __init__.py ├── api_router.py ├── asgi.py ├── settings │ ├── __init__.py │ ├── common.py │ ├── local.py │ └── production.py ├── urls.py └── wsgi.py ├── manage.py ├── poetry.lock ├── pyproject.toml ├── screenshots ├── data.png ├── echarts.png ├── index.png ├── inspection.png └── users.png └── setup.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile=hug 3 | src_paths=isort,test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 jkc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # epidemic 2 | 基于Django、Django Rest framework、Vue的前后端分离的社区疫情管理系统 3 | 4 | # 系统功能 5 | - 用户管理(只有管理员有权限) 6 | - 用户注册 7 | - 用户登录 8 | - 修改用户信息 9 | - 删除用户 10 | - 修改密码 11 | - 权限管理 12 | 13 | - 首页数据展示 14 | - 国内疫情数据展示 15 | - 国内疫情新闻 16 | - 近30日的感染人数(柱状图) 17 | - 中高风险地区(饼图) 18 | 19 | - 通知信息(只有管理员有权限) 20 | - 新增通知 21 | - 查看筛选通知 22 | - 修改通知 23 | - 删除通知 24 | 25 | - 检查记录 26 | - 新增检查记录 27 | - 查看筛选检查记录 28 | - 修改检查记录 29 | - 删除检查记录 30 | 31 | - 异常记录 32 | - 新增异常记录 33 | - 查看筛选异常记录 34 | - 修改异常记录 35 | - 删除异常记录 36 | 37 | - 接种记录 38 | - 新增接种记录 39 | - 查看筛选接种记录 40 | - 修改接种记录 41 | - 删除接种记录 42 | 43 | 44 | # 系统界面 45 | - 登录页 46 | ![登录页](./screenshots/index.png) 47 | - 用户管理 48 | ![用户管理](./screenshots/users.png) 49 | - 疫情数据 50 | ![数据页](./screenshots/data.png) 51 | - echarts表格 52 | ![表格](./screenshots/echarts.png) 53 | - 检查记录 54 | ![检查记录](./screenshots/inspection.png) 55 | 56 | # 技术分析 57 | 选用Django就是为了快速开发,省事方便。 58 | 59 | ## 接口文档 60 | 采用drf-spectacular内置的swagger api 61 | 62 | ## 用户模块 63 | 模型:采用Django原生的AbstractUser模型,添加了level和avatar2个字段 64 | 65 | 认证:采用第三方库simplejwt生成token,序列化用的djoser中的序列化 66 | 67 | 权限:采用Django原生的IsAuthenticated 68 | 69 | 筛选:采用第三方库django-filter 70 | 71 | ## 爬虫 72 | (爬取国内疫情数据,都是公开免费) 73 | 74 | 获取国内疫情新闻:从天行数据API接口获取 75 | 76 | 获取低中高风险地区:从别人那提供的接口,不知何时会失效,不怕麻烦也可以从聚合数据那获取(ps:由于大量for循环整合数结构,整体请求时间在2s左右,前端渲染很慢,后续优化) 77 | 78 | 获取近30天内的疫情数据:从腾讯免费疫情接口获取 79 | 80 | ## 检查模块 81 | 检查记录的增删改查 82 | 83 | ## 通知模块 84 | 通知记录的增删改查 85 | 86 | ## 异常模块 87 | 异常记录的增删改查 88 | 89 | ## 接种模块 90 | 接种模块的增删改查 91 | 92 | # Django原生部署 93 | 94 | ## 1.安装环境poetry 95 | [安装poetry](https://www.cnblogs.com/jiakecong/p/15430012.html) 96 | 97 | ## 2.拉取代码 98 | ``` 99 | cd ~ # 环境当前用户home目录 100 | git clone https://github.com/s649821022/epidemic.git 101 | ``` 102 | 103 | ## 3.在poetry环境中安装依赖 104 | ``` 105 | poetry install 106 | ``` 107 | - 安装mysqlclient报错,可以输入下面2条命令 108 | ``` 109 | PATH="$PATH":/usr/local/mysql/bin/ 110 | mysql_config 111 | ``` 112 | 113 | ## 4.创建对应的数据库 114 | ``` 115 | vim epidemic/config/settings/local.py #查看本地配置文件 116 | # 然后找到数据库配置 117 | DATABASES = { 118 | "default": { 119 | "ENGINE": "django.db.backends.mysql", 120 | "NAME": "epidemic", 121 | "USER": "root", 122 | "PASSWORD": "123456", 123 | "HOST": "127.0.0.1", 124 | "PORT": 3306, 125 | } 126 | } 127 | ``` 128 | 创建的数据库信息和配置文件需要保持一样. 129 | 130 | ## 5.执行Django数据库迁移脚本 131 | ``` 132 | cd epidemic # 进入epidemic根目录 133 | python manage.py makemigrations # 生成数据库迁移脚本 134 | python manage.py migrate # 执行数据库迁移脚本 135 | ``` 136 | 137 | ## 6.启动Django服务 138 | ``` 139 | 默认就是本机的8000端口启动,只能本机访问 140 | python manage.py runserver 141 | ``` 142 | 143 | ## 7.前端部署 144 | ![前端部署](https://github.com/s649821022/epidemicweb) 145 | 前端部署拉取代码,执行以下命令 146 | ``` 147 | npm install # 安装依赖环境 148 | npm run serve # 启动命令 149 | ``` 150 | 151 | -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/__init__.py -------------------------------------------------------------------------------- /api/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/__init__.py -------------------------------------------------------------------------------- /api/apps/abnormal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/abnormal/__init__.py -------------------------------------------------------------------------------- /api/apps/abnormal/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps/abnormal/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AbnormalConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "abnormal" 7 | -------------------------------------------------------------------------------- /api/apps/abnormal/filters.py: -------------------------------------------------------------------------------- 1 | from django_filters import rest_framework as filters 2 | 3 | from .models import Abnormal 4 | 5 | 6 | class AbnormalFilter(filters.FilterSet): 7 | user = filters.CharFilter(field_name="user_id__username", lookup_expr="icontains") 8 | 9 | class Meta: 10 | model = Abnormal 11 | fields = ["user"] 12 | -------------------------------------------------------------------------------- /api/apps/abnormal/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-09-19 00:51 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django.utils.timezone 7 | import model_utils.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Abnormal', 21 | fields=[ 22 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('created_at', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created_at')), 24 | ('modified_at', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified_at')), 25 | ('detail', models.CharField(max_length=125, verbose_name='检查详情')), 26 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='abnormal', to=settings.AUTH_USER_MODEL, verbose_name='记录员')), 27 | ], 28 | options={ 29 | 'db_table': 'abnormal', 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /api/apps/abnormal/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/abnormal/migrations/__init__.py -------------------------------------------------------------------------------- /api/apps/abnormal/models.py: -------------------------------------------------------------------------------- 1 | from core.models import TimeStampedModel 2 | from django.contrib.auth import get_user_model 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | User = get_user_model() 7 | 8 | 9 | class Abnormal(TimeStampedModel): 10 | detail = models.CharField(_("检查详情"), max_length=125) 11 | user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="记录员", related_name="abnormal") 12 | 13 | class Meta: 14 | db_table = "abnormal" 15 | -------------------------------------------------------------------------------- /api/apps/abnormal/serializers.py: -------------------------------------------------------------------------------- 1 | from abnormal.models import Abnormal 2 | from rest_framework import serializers 3 | from users.serializers import UserSerializer 4 | 5 | 6 | class AbnormalSerializer(serializers.ModelSerializer): 7 | user = UserSerializer(read_only=True) 8 | 9 | class Meta: 10 | model = Abnormal 11 | fields = [ 12 | "id", 13 | "user", 14 | "detail", 15 | "created_at", 16 | "modified_at", 17 | ] 18 | 19 | 20 | class AbnormalCreateSerializer(serializers.ModelSerializer): 21 | user = serializers.ReadOnlyField(source="user.username") 22 | 23 | class Meta: 24 | model = Abnormal 25 | fields = [ 26 | "id", 27 | "detail", 28 | "user", 29 | ] 30 | -------------------------------------------------------------------------------- /api/apps/abnormal/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/apps/abnormal/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from rest_framework.permissions import IsAuthenticated 3 | 4 | from .filters import AbnormalFilter 5 | from .models import Abnormal 6 | from .serializers import AbnormalCreateSerializer, AbnormalSerializer 7 | 8 | 9 | class AbnormalViewSet(viewsets.ModelViewSet): 10 | filter_class = AbnormalFilter 11 | 12 | def get_queryset(self): 13 | queryset = Abnormal.objects.all().order_by("-created_at") 14 | return queryset 15 | 16 | def get_permissions(self): 17 | if self.action in {"create", "partial_update"}: 18 | return [IsAuthenticated()] 19 | return super().get_permissions() 20 | 21 | def get_serializer_class(self): 22 | if self.action in {"retrieve", "list"}: 23 | return AbnormalSerializer 24 | elif self.action in {"create", "partial_update"}: 25 | return AbnormalCreateSerializer 26 | 27 | def perform_create(self, serializer): 28 | serializer.save(user=self.request.user) 29 | -------------------------------------------------------------------------------- /api/apps/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/core/__init__.py -------------------------------------------------------------------------------- /api/apps/core/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin # noqa F405 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps/core/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CoreConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "api.apps.core" 7 | -------------------------------------------------------------------------------- /api/apps/core/decrators.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/core/decrators.py -------------------------------------------------------------------------------- /api/apps/core/middleware.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/core/middleware.py -------------------------------------------------------------------------------- /api/apps/core/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/core/migrations/__init__.py -------------------------------------------------------------------------------- /api/apps/core/mixins.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/core/mixins.py -------------------------------------------------------------------------------- /api/apps/core/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils.translation import gettext_lazy as _ 3 | from model_utils.fields import AutoCreatedField, AutoLastModifiedField 4 | 5 | 6 | class TimeStampedModel(models.Model): 7 | created_at = AutoCreatedField(_("created_at")) # 创建时间 8 | modified_at = AutoLastModifiedField(_("modified_at")) # 修改时间 9 | 10 | class Meta: 11 | abstract = True # 抽象类,不在数据库中创建此表 12 | -------------------------------------------------------------------------------- /api/apps/core/pagination.py: -------------------------------------------------------------------------------- 1 | from rest_framework.pagination import CursorPagination, PageNumberPagination 2 | 3 | 4 | class MyPageNumberPagination(PageNumberPagination): 5 | """ 6 | 普通分页,数据量越大性能越差 7 | """ 8 | 9 | # 默认页面展示的条数 10 | page_size = 10 11 | # 前端访问url需要添加 ?page=页码 12 | page_query_param = "page" 13 | # 用户自定义返回的条数,格式?size=页数 14 | page_size_query_param = "size" 15 | # 用户自定义返回的条数最大限制,数值超过20也只展示20条 16 | max_page_size = 20 17 | 18 | 19 | class MyCursorPagination(CursorPagination): 20 | """ 21 | Cursor 光标分页 性能高,安全 22 | """ 23 | 24 | page_size = 10 25 | ordering = "-update_time" 26 | page_size_query_param = "size" 27 | max_page_size = 20 28 | -------------------------------------------------------------------------------- /api/apps/core/serializers.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/core/serializers.py -------------------------------------------------------------------------------- /api/apps/core/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase # noqa F405 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/apps/core/validators.py: -------------------------------------------------------------------------------- 1 | # take from https://gist.github.com/jrosebr1/2140738 2 | 3 | # @brief 4 | # Performs file upload validation for django. The original version implemented 5 | # by dokterbob had some problems with determining the correct mimetype and 6 | # determining the size of the file uploaded (at least within my Django application 7 | # that is). 8 | 9 | # @author dokterbob 10 | # @author jrosebr1 11 | 12 | import mimetypes 13 | from os.path import splitext 14 | 15 | from django.core.exceptions import ValidationError 16 | from django.template.defaultfilters import filesizeformat 17 | from django.utils.deconstruct import deconstructible 18 | from django.utils.translation import gettext_lazy as _ 19 | 20 | 21 | @deconstructible 22 | class FileValidator(object): 23 | """ 24 | Validator for files, checking the size, extension and mimetype. 25 | 26 | Initialization parameters: 27 | allowed_extensions: iterable with allowed file extensions 28 | ie. ('txt', 'doc') 29 | allowd_mimetypes: iterable with allowed mimetypes 30 | ie. ('image/png', ) 31 | min_size: minimum number of bytes allowed 32 | ie. 100 33 | max_size: maximum number of bytes allowed 34 | ie. 24*1024*1024 for 24 MB 35 | 36 | Usage example:: 37 | 38 | MyModel(models.Model): 39 | myfile = FileField(validators=FileValidator(max_size=24*1024*1024), ...) 40 | 41 | """ 42 | 43 | extension_message = _("Extension '%(extension)s' not allowed. " "Allowed extensions are: '%(allowed_extensions)s.'") 44 | mime_message = _("MIME type '%(mimetype)s' is not valid. " "Allowed types are: %(allowed_mimetypes)s.") 45 | min_size_message = _("The current file %(size)s, which is too small. " "The minumum file size is %(allowed_size)s.") 46 | max_size_message = _("The current file %(size)s, which is too large. " "The maximum file size is %(allowed_size)s.") 47 | 48 | def __init__(self, *args, **kwargs): 49 | self.allowed_extensions = kwargs.pop("allowed_extensions", None) 50 | self.allowed_mimetypes = kwargs.pop("allowed_mimetypes", None) 51 | self.min_size = kwargs.pop("min_size", 0) 52 | self.max_size = kwargs.pop("max_size", None) 53 | 54 | def __call__(self, value): 55 | """ 56 | Check the extension, content type and file size. 57 | """ 58 | 59 | # Check the extension 60 | ext = splitext(value.name)[1][1:].lower() 61 | if self.allowed_extensions and ext not in self.allowed_extensions: 62 | message = self.extension_message % { 63 | "extension": ext, 64 | "allowed_extensions": ", ".join(self.allowed_extensions), 65 | } 66 | 67 | raise ValidationError(message) 68 | 69 | # Check the content type 70 | mimetype = mimetypes.guess_type(value.name)[0] 71 | if self.allowed_mimetypes and mimetype not in self.allowed_mimetypes: 72 | message = self.mime_message % { 73 | "mimetype": mimetype, 74 | "allowed_mimetypes": ", ".join(self.allowed_mimetypes), 75 | } 76 | 77 | raise ValidationError(message) 78 | 79 | # Check the file size 80 | filesize = len(value) 81 | if self.max_size and filesize > self.max_size: 82 | message = self.max_size_message % { 83 | "size": filesizeformat(filesize), 84 | "allowed_size": filesizeformat(self.max_size), 85 | } 86 | 87 | raise ValidationError(message) 88 | 89 | elif filesize < self.min_size: 90 | message = self.min_size_message % { 91 | "size": filesizeformat(filesize), 92 | "allowed_size": filesizeformat(self.min_size), 93 | } 94 | 95 | raise ValidationError(message) 96 | -------------------------------------------------------------------------------- /api/apps/core/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.views import exception_handler as drf_exception_handler 2 | 3 | 4 | def exception_handler(exc, context): 5 | response = drf_exception_handler(exc, context) 6 | if response is None: 7 | return 8 | 9 | data = response.data 10 | 11 | # ["error message"] 12 | if isinstance(data, list): 13 | return response 14 | 15 | # {"detail": "error message"} 16 | if data is not None and "detail" in data: 17 | response.data = [data["detail"]] 18 | 19 | """ 20 | { 21 | "url": [ 22 | "This field is required." 23 | ] 24 | } 25 | """ 26 | return response 27 | -------------------------------------------------------------------------------- /api/apps/inspection/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/inspection/__init__.py -------------------------------------------------------------------------------- /api/apps/inspection/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps/inspection/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class InspectionConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "inspection" 7 | -------------------------------------------------------------------------------- /api/apps/inspection/filters.py: -------------------------------------------------------------------------------- 1 | from django_filters import rest_framework as filters 2 | 3 | from .models import Inspection 4 | 5 | 6 | class InspectionFilter(filters.FilterSet): 7 | location = filters.CharFilter(field_name="location", lookup_expr="icontains") 8 | result = filters.CharFilter(field_name="result", lookup_expr="icontains") 9 | 10 | class Meta: 11 | model = Inspection 12 | fields = ["location", "result"] 13 | -------------------------------------------------------------------------------- /api/apps/inspection/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-09-19 00:51 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django.utils.timezone 7 | import model_utils.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Inspection', 21 | fields=[ 22 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('created_at', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created_at')), 24 | ('modified_at', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified_at')), 25 | ('location', models.CharField(max_length=64, verbose_name='检查地点')), 26 | ('result', models.CharField(max_length=2, verbose_name='检查结果')), 27 | ('detail', models.CharField(max_length=125, verbose_name='检查详情')), 28 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inspection', to=settings.AUTH_USER_MODEL, verbose_name='记录员')), 29 | ], 30 | options={ 31 | 'db_table': 'inspection', 32 | }, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /api/apps/inspection/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/inspection/migrations/__init__.py -------------------------------------------------------------------------------- /api/apps/inspection/models.py: -------------------------------------------------------------------------------- 1 | from core.models import TimeStampedModel 2 | from django.contrib.auth import get_user_model 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | User = get_user_model() 7 | 8 | 9 | class Inspection(TimeStampedModel): 10 | location = models.CharField(_("检查地点"), max_length=64) 11 | result = models.CharField(_("检查结果"), max_length=2) 12 | detail = models.CharField(_("检查详情"), max_length=125) 13 | user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="记录员", related_name="inspection") 14 | 15 | class Meta: 16 | db_table = "inspection" 17 | -------------------------------------------------------------------------------- /api/apps/inspection/serializers.py: -------------------------------------------------------------------------------- 1 | from inspection.models import Inspection 2 | from rest_framework import serializers 3 | from users.serializers import UserSerializer 4 | 5 | 6 | class InspectionSerializer(serializers.ModelSerializer): 7 | user = UserSerializer(read_only=True) 8 | 9 | class Meta: 10 | model = Inspection 11 | fields = [ 12 | "id", 13 | "location", 14 | "result", 15 | "detail", 16 | "user", 17 | "created_at", 18 | "modified_at", 19 | ] 20 | 21 | 22 | class InspectionCreateSerializer(serializers.ModelSerializer): 23 | user = serializers.ReadOnlyField(source="user.username") 24 | 25 | class Meta: 26 | model = Inspection 27 | fields = ["id", "location", "result", "detail", "user"] 28 | -------------------------------------------------------------------------------- /api/apps/inspection/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/apps/inspection/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import mixins, status, viewsets 2 | from rest_framework.permissions import IsAuthenticated 3 | 4 | from .filters import InspectionFilter 5 | from .models import Inspection 6 | from .serializers import InspectionCreateSerializer, InspectionSerializer 7 | 8 | 9 | class InspectionViewSet(viewsets.ModelViewSet): 10 | filter_class = InspectionFilter 11 | 12 | def get_queryset(self): 13 | queryset = Inspection.objects.all().order_by("-created_at") 14 | return queryset 15 | 16 | def get_permissions(self): 17 | if self.action in {"create", "partial_update"}: 18 | return [IsAuthenticated()] 19 | return super().get_permissions() 20 | 21 | def get_serializer_class(self): 22 | if self.action in {"retrieve", "list"}: 23 | return InspectionSerializer 24 | elif self.action in {"create", "partial_update"}: 25 | return InspectionCreateSerializer 26 | 27 | def perform_create(self, serializer): 28 | serializer.save(user=self.request.user) 29 | -------------------------------------------------------------------------------- /api/apps/notices/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/notices/__init__.py -------------------------------------------------------------------------------- /api/apps/notices/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps/notices/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NoticesConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "notices" 7 | -------------------------------------------------------------------------------- /api/apps/notices/filters.py: -------------------------------------------------------------------------------- 1 | from django_filters import rest_framework as filters 2 | 3 | from .models import Notices 4 | 5 | 6 | class NoticesFilter(filters.FilterSet): 7 | title = filters.CharFilter(field_name="title", lookup_expr="icontains") 8 | 9 | class Meta: 10 | model = Notices 11 | fields = ["title"] 12 | -------------------------------------------------------------------------------- /api/apps/notices/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-09-19 00:51 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | import model_utils.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Notices', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('created_at', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created_at')), 21 | ('modified_at', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified_at')), 22 | ('title', models.CharField(max_length=32, verbose_name='通知标题')), 23 | ('detail', models.CharField(max_length=125, verbose_name='检查详情')), 24 | ], 25 | options={ 26 | 'db_table': 'notices', 27 | }, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /api/apps/notices/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/notices/migrations/__init__.py -------------------------------------------------------------------------------- /api/apps/notices/models.py: -------------------------------------------------------------------------------- 1 | from core.models import TimeStampedModel 2 | from django.contrib.auth import get_user_model 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | User = get_user_model() 7 | 8 | 9 | class Notices(TimeStampedModel): 10 | title = models.CharField(_("通知标题"), max_length=32) 11 | detail = models.CharField(_("检查详情"), max_length=125) 12 | 13 | class Meta: 14 | db_table = "notices" 15 | -------------------------------------------------------------------------------- /api/apps/notices/serializers.py: -------------------------------------------------------------------------------- 1 | from notices.models import Notices 2 | from rest_framework import serializers 3 | 4 | 5 | class NoticesListSerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = Notices 8 | fields = ["id", "created_at", "modified_at", "title", "detail"] 9 | 10 | 11 | class NoticesCreateSerializer(serializers.ModelSerializer): 12 | class Meta: 13 | model = Notices 14 | fields = ["id", "title", "detail"] 15 | -------------------------------------------------------------------------------- /api/apps/notices/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/apps/notices/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status, viewsets 2 | from rest_framework.permissions import IsAuthenticated 3 | 4 | from .filters import NoticesFilter 5 | from .models import Notices 6 | from .serializers import NoticesCreateSerializer, NoticesListSerializer 7 | 8 | 9 | class NoticesViewSet(viewsets.ModelViewSet): 10 | filter_class = NoticesFilter 11 | 12 | def get_queryset(self): 13 | queryset = Notices.objects.all().order_by("-created_at") 14 | return queryset 15 | 16 | def get_permissions(self): 17 | if self.action in {"create", "partial_update"}: 18 | return [IsAuthenticated()] 19 | return super().get_permissions() 20 | 21 | def get_serializer_class(self): 22 | if self.action in {"retrieve", "list"}: 23 | return NoticesListSerializer 24 | elif self.action in {"create", "partial_update"}: 25 | return NoticesCreateSerializer 26 | -------------------------------------------------------------------------------- /api/apps/reptile/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/reptile/__init__.py -------------------------------------------------------------------------------- /api/apps/reptile/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps/reptile/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ReptileConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "reptile" 7 | -------------------------------------------------------------------------------- /api/apps/reptile/epidemic_data.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from reptile.utils.common import epidemic_url 3 | 4 | 5 | def get_epidemic_data(): 6 | """ 7 | 获取疫情数据和相关的疫情新闻 8 | """ 9 | latest_data = {} 10 | resp = requests.get(epidemic_url).json() 11 | if resp and resp["code"] == 200: 12 | desc_dict = resp["newslist"][0]["desc"] 13 | latest_data.update(desc_dict) 14 | latest_data.pop("id") 15 | latest_data.pop("foreignStatistics") 16 | latest_data.pop("globalStatistics") 17 | latest_data.pop("createTime") 18 | latest_data.pop("modifyTime") 19 | return latest_data 20 | else: 21 | return "爬取数据失败,网络异常或接口次数已用完" 22 | 23 | 24 | def get_epidemic_news(): 25 | """ 26 | 获取疫情有关的新闻 27 | """ 28 | latest_news = {} 29 | resp = requests.get(epidemic_url).json() 30 | if resp and resp["code"] == 200: 31 | latest_news["news"] = resp["newslist"][0]["news"] 32 | return latest_news 33 | else: 34 | return "爬取数据失败,网络异常或接口次数已用完" 35 | 36 | 37 | def get_risk_area(): 38 | """ 39 | 获取低中高风险地区数据 40 | """ 41 | rsp = requests.get("https://diqu.gezhong.vip/api.php") 42 | res = rsp.json() 43 | area_dict = {"high_area": [], "medium_area": [], "low_area": []} 44 | low_risk = [] 45 | medium_risk = [] 46 | high_risk = [] 47 | if res and res["msg"] == "查询成功": 48 | hcount = res["data"]["hcount"] 49 | mcount = res["data"]["mcount"] 50 | lcount = res["data"]["lcount"] 51 | for data in res["data"]["highlist"]: 52 | if data["communitys"]: 53 | for i in data["communitys"]: 54 | high_risk.append({"xArea": i}) 55 | for data in res["data"]["middlelist"]: 56 | if data["communitys"]: 57 | for i in data["communitys"]: 58 | medium_risk.append({"xArea": i}) 59 | for data in res["data"]["lowlist"]: 60 | if data["communitys"]: 61 | for i in data["communitys"]: 62 | low_risk.append({"xArea": i}) 63 | else: 64 | return "查询失败" 65 | area_dict["high_area"] = high_risk 66 | area_dict["medium_area"] = medium_risk 67 | area_dict["low_area"] = low_risk 68 | area_dict["hcount"] = hcount 69 | area_dict["mcount"] = mcount 70 | area_dict["lcount"] = lcount 71 | return area_dict 72 | 73 | 74 | def get_latest_infected_number(): 75 | """ 76 | 获取最近30天的感染人数 77 | """ 78 | url = "https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=chinaDayList,chinaDayAddList,cityStatis,provinceCompare" 79 | resp = requests.get(url) 80 | data = resp.json() 81 | number_data = { 82 | "date": [], 83 | "confirm": [], 84 | "nowConfirm": [], 85 | "noInfect": [], 86 | "dead": [], 87 | "heal": [], 88 | } 89 | resp = data["data"]["chinaDayList"][30:] 90 | for item in resp: 91 | number_data["date"].append(item["date"]) 92 | number_data["confirm"].append(int(item["confirm"])) 93 | number_data["nowConfirm"].append(int(item["nowConfirm"])) 94 | number_data["noInfect"].append(int(item["noInfect"])) 95 | number_data["dead"].append(int(item["dead"])) 96 | number_data["heal"].append(int(item["heal"])) 97 | return number_data 98 | 99 | 100 | if __name__ == "__main__": 101 | # print(get_epidemic_data()) 102 | (get_risk_area()) 103 | # print(get_latest_infected_number()) 104 | -------------------------------------------------------------------------------- /api/apps/reptile/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-09-19 00:51 2 | 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | import model_utils.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Reptile', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('created_at', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created_at')), 21 | ('modified_at', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified_at')), 22 | ('currentConfirmedCount', models.PositiveIntegerField(default=0, verbose_name='现存确诊人数')), 23 | ('confirmedCount', models.PositiveIntegerField(default=0, verbose_name='累计确诊人数')), 24 | ('suspectedCount', models.PositiveIntegerField(default=0, verbose_name='累计境外确诊人数')), 25 | ('curedCount', models.PositiveIntegerField(default=0, verbose_name='累计治愈人数')), 26 | ('deadCount', models.PositiveIntegerField(default=0, verbose_name='累计死亡人数')), 27 | ('seriousCount', models.PositiveIntegerField(default=0, verbose_name='当前无症状人数')), 28 | ('suspectedIncr', models.PositiveIntegerField(default=0, verbose_name='新增境外输入人数')), 29 | ('currentConfirmedIncr', models.PositiveIntegerField(default=0, verbose_name='相比昨天现存确诊人数')), 30 | ('confirmedIncr', models.PositiveIntegerField(default=0, verbose_name='\t相比昨天累计确诊人数')), 31 | ('curedIncr', models.PositiveIntegerField(default=0, verbose_name='相比昨天新增治愈人数')), 32 | ('deadIncr', models.PositiveIntegerField(default=0, verbose_name='相比昨天新增死亡人数')), 33 | ('seriousIncr', models.IntegerField(default=0, verbose_name='相比昨天现存无症状人数')), 34 | ('yesterdayConfirmedCountIncr', models.PositiveIntegerField(default=0, verbose_name='相比昨天新增累计确诊人数')), 35 | ('yesterdaySuspectedCountIncr', models.PositiveIntegerField(default=0, verbose_name='相比昨天境外输入确诊人数')), 36 | ('highDangerCount', models.PositiveIntegerField(default=0, verbose_name='国内高风险地区个数')), 37 | ('midDangerCount', models.PositiveIntegerField(default=0, verbose_name='国内中风险地区个数')), 38 | ], 39 | options={ 40 | 'db_table': 'reptile', 41 | }, 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /api/apps/reptile/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/reptile/migrations/__init__.py -------------------------------------------------------------------------------- /api/apps/reptile/models.py: -------------------------------------------------------------------------------- 1 | from core.models import TimeStampedModel 2 | from django.db import models 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | 6 | class Reptile(TimeStampedModel): 7 | """ 8 | 疫情数据统计,数据从网上爬取 9 | 由于数据都是正整数,所以使用PositiveIntegerField 10 | """ 11 | 12 | currentConfirmedCount = models.PositiveIntegerField(_("现存确诊人数"), default=0) 13 | confirmedCount = models.PositiveIntegerField(_("累计确诊人数"), default=0) 14 | suspectedCount = models.PositiveIntegerField(_("累计境外确诊人数"), default=0) 15 | curedCount = models.PositiveIntegerField(_("累计治愈人数"), default=0) 16 | deadCount = models.PositiveIntegerField(_("累计死亡人数"), default=0) 17 | seriousCount = models.PositiveIntegerField(_("当前无症状人数"), default=0) 18 | suspectedIncr = models.PositiveIntegerField(_("新增境外输入人数"), default=0) 19 | currentConfirmedIncr = models.PositiveIntegerField(_("相比昨天现存确诊人数"), default=0) 20 | confirmedIncr = models.PositiveIntegerField(_(" 相比昨天累计确诊人数"), default=0) 21 | curedIncr = models.PositiveIntegerField(_("相比昨天新增治愈人数"), default=0) 22 | deadIncr = models.PositiveIntegerField(_("相比昨天新增死亡人数"), default=0) 23 | seriousIncr = models.IntegerField(_("相比昨天现存无症状人数"), default=0) 24 | yesterdayConfirmedCountIncr = models.PositiveIntegerField(_("相比昨天新增累计确诊人数"), default=0) 25 | yesterdaySuspectedCountIncr = models.PositiveIntegerField(_("相比昨天境外输入确诊人数"), default=0) 26 | highDangerCount = models.PositiveIntegerField(_("国内高风险地区个数"), default=0) 27 | midDangerCount = models.PositiveIntegerField(_("国内中风险地区个数"), default=0) 28 | 29 | class Meta: 30 | db_table = "reptile" 31 | -------------------------------------------------------------------------------- /api/apps/reptile/serializers.py: -------------------------------------------------------------------------------- 1 | from reptile.models import Reptile 2 | from rest_framework import serializers 3 | 4 | 5 | class ReptileListSerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = Reptile 8 | fields = [ 9 | "currentConfirmedCount", 10 | "confirmedCount", 11 | "suspectedCount", 12 | "curedCount", 13 | "deadCount", 14 | "seriousCount", 15 | "suspectedIncr", 16 | "currentConfirmedIncr", 17 | "confirmedIncr", 18 | "curedIncr", 19 | "deadIncr", 20 | "seriousIncr", 21 | "yesterdayConfirmedCountIncr", 22 | "yesterdaySuspectedCountIncr", 23 | "highDangerCount", 24 | "midDangerCount", 25 | "created_at", 26 | "modified_at", 27 | ] 28 | read_only_fields = [ 29 | "currentConfirmedCount", 30 | "confirmedCount", 31 | "suspectedCount", 32 | "curedCount", 33 | "deadCount", 34 | "seriousCount", 35 | "suspectedIncr", 36 | "currentConfirmedIncr", 37 | "confirmedIncr", 38 | "curedIncr", 39 | "deadIncr", 40 | "seriousIncr", 41 | "yesterdayConfirmedCountIncr", 42 | "yesterdaySuspectedCountIncr", 43 | "highDangerCount", 44 | "midDangerCount", 45 | "created_at", 46 | "modified_at", 47 | ] 48 | -------------------------------------------------------------------------------- /api/apps/reptile/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/apps/reptile/utils/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | # @Time :2022/5/24 10:12 4 | # @Author :jkc 5 | # @File :__init__.py.py 6 | -------------------------------------------------------------------------------- /api/apps/reptile/utils/common.py: -------------------------------------------------------------------------------- 1 | epidemic_url = "http://api.tianapi.com/ncov/index?key=ca3715f60118d321c01bf88c7a9e11e1" 2 | -------------------------------------------------------------------------------- /api/apps/reptile/utils/getSign.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | import requests 4 | from reptile.utils.hash import my_md5 5 | 6 | 7 | def getSign(body=None, key="e38174983350a531e3183a8a7eefe325"): 8 | if body is None: 9 | body = {"appid": 17919} 10 | body_copy = copy.deepcopy(body) 11 | # 将参数的value值都转成str类型 12 | body_str = dict([(x, str(y)) for x, y in body_copy.items()]) 13 | # 列表生成式,生成key=value格式,将参数拼接 14 | strData = ["".join(i) for i in body_str.items()] 15 | # 以首字母进行排序 16 | strData.append(key) 17 | strData.sort() 18 | # 用=!=进行拼接 19 | strJoinData = "".join(strData) 20 | # 通过md5进行加密 21 | sign = my_md5(strJoinData) 22 | body["sign"] = sign 23 | return body 24 | 25 | 26 | if __name__ == "__main__": 27 | body = getSign() 28 | rsp = requests.get("https://grnx.api.storeapi.net/api/94/219", params=body) 29 | print(rsp.json()) 30 | res = rsp.json()["retdata"] 31 | medium_risk = [] 32 | high_risk = [] 33 | for data in res: 34 | area_list = data["dangerousAreas"]["subList"] 35 | if len(area_list) != 0: 36 | for area in area_list: 37 | if area["level"] == "中风险": 38 | medium_risk.append(area["xArea"]) 39 | elif area["level"] == "高风险": 40 | high_risk.append(area["xArea"]) 41 | print(medium_risk) 42 | print(high_risk) 43 | -------------------------------------------------------------------------------- /api/apps/reptile/utils/hash.py: -------------------------------------------------------------------------------- 1 | from hashlib import md5 2 | 3 | 4 | def my_md5(msg): 5 | """ 6 | md5加密算法 7 | :param msg: 需要加密的字符串 8 | :return: 返回加密后的字符串 9 | """ 10 | h1 = md5() 11 | h1.update(msg.encode("utf-8")) 12 | return h1.hexdigest().lower() 13 | -------------------------------------------------------------------------------- /api/apps/reptile/views.py: -------------------------------------------------------------------------------- 1 | from drf_spectacular.utils import extend_schema 2 | from rest_framework import mixins, status, viewsets 3 | from rest_framework.decorators import action 4 | from rest_framework.permissions import AllowAny 5 | from rest_framework.response import Response 6 | 7 | from .epidemic_data import get_epidemic_data, get_latest_infected_number, get_risk_area, get_epidemic_news 8 | from .models import Reptile 9 | from .serializers import ReptileListSerializer 10 | 11 | 12 | class ReptileViewSet(viewsets.GenericViewSet): 13 | permission_classes = [AllowAny] 14 | serializer_class = None 15 | 16 | def get_queryset(self): 17 | queryset = Reptile.objects.all() 18 | return queryset 19 | 20 | def get_serializer_class(self): 21 | if self.action == "init": 22 | return ReptileListSerializer 23 | return super().get_serializer_class() 24 | 25 | @extend_schema(summary="get_epidemic_news") 26 | @action(["GET"], detail=False, url_name="getNews", url_path="getNews") 27 | def getNews(self, request, *args, **kwargs): 28 | """ 29 | 拉取疫情新闻 30 | """ 31 | data = get_epidemic_news() 32 | return Response(data=data, status=status.HTTP_200_OK) 33 | 34 | @extend_schema( 35 | summary="init_epidemic_data", 36 | responses={201: ReptileListSerializer}, 37 | ) 38 | @action( 39 | ["POST"], 40 | detail=False, 41 | url_path="init", 42 | url_name="init", 43 | serializer_class=ReptileListSerializer, 44 | ) 45 | def init(self, request, *args, **kwargs): 46 | """ 47 | 初始化疫情数据 48 | """ 49 | Reptile.objects.all().delete() 50 | data = get_epidemic_data() 51 | Reptile.objects.create(**data) 52 | return Response(data=data, status=status.HTTP_201_CREATED) 53 | 54 | @extend_schema(summary="get_area_list") 55 | @action( 56 | ["GET"], 57 | detail=False, 58 | url_path="getArea", 59 | url_name="getArea", 60 | ) 61 | def getArea(self, request, *args, **kwargs): 62 | """ 63 | 获取低中高风险地区数据 64 | """ 65 | data = get_risk_area() 66 | return Response(data=data, status=status.HTTP_200_OK) 67 | 68 | @extend_schema(summary="get_latest_infected_number") 69 | @action( 70 | ["GET"], 71 | detail=False, 72 | url_path="getRiskNumber", 73 | url_name="getRiskNumber", 74 | ) 75 | def getRiskNumber(self, request, *args, **kwargs): 76 | """ 77 | 获取最近30日的感染人数 78 | """ 79 | data = get_latest_infected_number() 80 | return Response(data=data, status=status.HTTP_200_OK) 81 | -------------------------------------------------------------------------------- /api/apps/users/Inconsolata.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/users/Inconsolata.otf -------------------------------------------------------------------------------- /api/apps/users/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/users/__init__.py -------------------------------------------------------------------------------- /api/apps/users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin # noqa F405 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "users" 7 | -------------------------------------------------------------------------------- /api/apps/users/avatar_generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Taking from https://github.com/maethor/avatar-generator. 5 | """ 6 | 7 | import os 8 | from io import BytesIO 9 | from random import randint, seed 10 | 11 | from PIL import Image, ImageDraw, ImageFont 12 | 13 | __all__ = ["AvatarGenerator"] 14 | 15 | 16 | class AvatarGenerator: 17 | FONT_COLOR = (255, 255, 255) 18 | MIN_RENDER_SIZE = 100 19 | 20 | @classmethod 21 | def generate(cls, string, size=100, filetype="PNG"): 22 | """ 23 | Generates a squared avatar with random background color. 24 | :param size: size of the avatar, in pixels 25 | :param string: string to be used to print text and seed the random 26 | :param filetype: the file format of the image (i.e. JPEG, PNG) 27 | """ 28 | render_size = max(size, cls.MIN_RENDER_SIZE) 29 | image = Image.new("RGB", (render_size, render_size), cls._background_color(string)) 30 | draw = ImageDraw.Draw(image) 31 | font = cls._font(render_size) 32 | text = cls._text(string) 33 | draw.text( 34 | cls._text_position(render_size, text, font), 35 | text, 36 | fill=cls.FONT_COLOR, 37 | font=font, 38 | ) 39 | stream = BytesIO() 40 | image = image.resize((size, size), Image.ANTIALIAS) 41 | image.save(stream, format=filetype, optimize=True) 42 | return stream.getvalue() 43 | 44 | @staticmethod 45 | def _background_color(s): 46 | """ 47 | Generate a random background color. 48 | Brighter colors are dropped, because the text is white. 49 | :param s: Seed used by the random generator 50 | (same seed will produce the same color). 51 | """ 52 | seed(s) 53 | r = v = b = 255 54 | while r + v + b > 255 * 2: 55 | r = randint(0, 255) 56 | v = randint(0, 255) 57 | b = randint(0, 255) 58 | return r, v, b 59 | 60 | @staticmethod 61 | def _font(size): 62 | """ 63 | Returns a PIL ImageFont instance. 64 | :param size: size of the avatar, in pixels 65 | """ 66 | # path = pathlib.Path(".").joinpath("Inconsolata.otf") 67 | path = os.path.join(os.path.dirname(__file__), "Inconsolata.otf") 68 | return ImageFont.truetype(path, size=int(0.8 * size)) 69 | 70 | @staticmethod 71 | def _text(string): 72 | """ 73 | Returns the text to draw. 74 | """ 75 | return string[0].upper() 76 | 77 | @staticmethod 78 | def _text_position(size, text, font): 79 | """ 80 | Returns the left-top point where the text should be positioned. 81 | """ 82 | width, height = font.getsize(text) 83 | left = (size - width) / 2.0 84 | # I just don't know why 5.5, but it seems to be the good ratio 85 | top = (size - height) / 5.5 86 | return left, top 87 | -------------------------------------------------------------------------------- /api/apps/users/filters.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django_filters import rest_framework as filters 3 | 4 | User = get_user_model() 5 | 6 | 7 | class UserFilter(filters.FilterSet): 8 | username = filters.CharFilter(field_name="username", lookup_expr="icontains") 9 | 10 | class Meta: 11 | model = User 12 | fields = ["username"] 13 | -------------------------------------------------------------------------------- /api/apps/users/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-19 06:57 2 | 3 | import api.apps.core.validators 4 | import django.contrib.auth.models 5 | import django.contrib.auth.validators 6 | import django.utils.timezone 7 | import users.models 8 | from django.db import migrations, models 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | ('auth', '0012_alter_user_first_name_max_length'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='User', 22 | fields=[ 23 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('password', models.CharField(max_length=128, verbose_name='password')), 25 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 26 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 27 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 28 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), 29 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 30 | ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), 31 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 32 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 33 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 34 | ('level', models.CharField(choices=[(0, '普通用户'), (1, '管理员')], default=0, max_length=50, verbose_name='角色')), 35 | ('avatar', models.ImageField(blank=True, upload_to=users.models.user_avatar_path, validators=[api.apps.core.validators.FileValidator(allowed_extensions=['png', 'jpg', 'jpeg'], max_size=2097152)], verbose_name='avatar')), 36 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), 37 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), 38 | ], 39 | options={ 40 | 'verbose_name': '用户表', 41 | 'verbose_name_plural': '用户表', 42 | 'db_table': 'user', 43 | }, 44 | managers=[ 45 | ('objects', django.contrib.auth.models.UserManager()), 46 | ], 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /api/apps/users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/users/migrations/__init__.py -------------------------------------------------------------------------------- /api/apps/users/models.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from api.apps.core.validators import FileValidator 4 | from django.contrib.auth.models import AbstractUser 5 | from django.core.files.base import ContentFile 6 | from django.db import models 7 | from django.utils.translation import gettext_lazy as _ 8 | from imagekit.models import ImageSpecField 9 | from pilkit.processors import ResizeToFill 10 | from users.avatar_generator import AvatarGenerator 11 | 12 | 13 | def user_avatar_path(instance, filename): 14 | return os.path.join("users", "avatars", instance.username, filename) 15 | 16 | 17 | class User(AbstractUser): 18 | AVATAR_MAX_SIZE = 2 * 1024 * 1024 19 | AVATAR_ALLOWED_EXTENSIONS = ["png", "jpg", "jpeg"] 20 | AVATAR_DEFAULT_FILENAME = "default.jpeg" 21 | LEVEL_TYPE = ((0, "普通用户"), (1, "管理员")) 22 | level = models.CharField(max_length=50, verbose_name="角色", choices=LEVEL_TYPE, default=0, null=False) 23 | avatar = models.ImageField( 24 | _("avatar"), 25 | upload_to=user_avatar_path, 26 | validators=[FileValidator(max_size=AVATAR_MAX_SIZE, allowed_extensions=AVATAR_ALLOWED_EXTENSIONS)], 27 | blank=True, 28 | ) 29 | avatar_thumbnail = ImageSpecField( 30 | source="avatar", 31 | processors=[ResizeToFill(70, 70)], 32 | format="jpeg", 33 | options={"quality": 100}, 34 | ) 35 | 36 | class Meta: 37 | db_table = "user" 38 | verbose_name = "用户表" 39 | verbose_name_plural = verbose_name 40 | 41 | def save(self, *args, **kwargs): 42 | if not self.pk: 43 | if not self.avatar: 44 | self.set_default_avatar() 45 | super(User, self).save(*args, **kwargs) 46 | 47 | def set_default_avatar(self): 48 | avatar_byte_array = AvatarGenerator.generate(self.username) 49 | self.avatar.save( 50 | self.AVATAR_DEFAULT_FILENAME, 51 | ContentFile(avatar_byte_array), 52 | save=False, 53 | ) 54 | -------------------------------------------------------------------------------- /api/apps/users/serializers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from djoser.serializers import ( 3 | SetPasswordRetypeSerializer, 4 | UserCreatePasswordRetypeSerializer, 5 | ) 6 | from rest_framework import serializers 7 | from rest_framework_simplejwt.serializers import TokenObtainPairSerializer 8 | 9 | User = get_user_model() 10 | 11 | 12 | class RegisterSerializer(UserCreatePasswordRetypeSerializer): 13 | email = serializers.EmailField() 14 | 15 | 16 | class UserSerializer(serializers.ModelSerializer): 17 | avatar_url = serializers.ImageField(source="avatar_thumbnail") 18 | 19 | class Meta: 20 | model = User 21 | fields = ["id", "username", "level", "avatar_url", "email", "last_login", "is_superuser"] 22 | read_only_fields = ["id", "level", "username", "last_login", "is_superuser"] 23 | 24 | 25 | class TokenSerializer(TokenObtainPairSerializer): 26 | def validate(self, attrs): 27 | data = super().validate(attrs) 28 | data["user"] = self.user.username 29 | data["is_superuser"] = self.user.is_superuser 30 | return data 31 | 32 | 33 | class UserInfoSerializer(serializers.ModelSerializer): 34 | class Meta: 35 | model = User 36 | exclude = ["password"] 37 | 38 | 39 | class PasswordSerializer(SetPasswordRetypeSerializer): 40 | def set_password(self): 41 | new_password = self.validated_data["new_password"] 42 | user = self.context["request"].user 43 | print(user) 44 | user.set_password(new_password) 45 | user.save() 46 | -------------------------------------------------------------------------------- /api/apps/users/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | # New user has registered. Args: user, request. 4 | user_registered = Signal() 5 | user_password_changed = Signal() 6 | user_activated = Signal() 7 | -------------------------------------------------------------------------------- /api/apps/users/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase # noqa F405 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/apps/users/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.utils.timezone import now 3 | from drf_spectacular.utils import extend_schema 4 | from rest_framework import mixins, status, viewsets 5 | from rest_framework.decorators import action 6 | from rest_framework.permissions import AllowAny, IsAuthenticated 7 | from rest_framework.response import Response 8 | from rest_framework_simplejwt.authentication import JWTAuthentication 9 | from rest_framework_simplejwt.serializers import TokenRefreshSerializer 10 | from users import signals 11 | from users.filters import UserFilter 12 | from users.serializers import ( 13 | PasswordSerializer, 14 | RegisterSerializer, 15 | TokenSerializer, 16 | UserInfoSerializer, 17 | UserSerializer, 18 | ) 19 | 20 | User = get_user_model() 21 | jwt_auth = JWTAuthentication() 22 | 23 | 24 | class AuthViewSet( 25 | mixins.RetrieveModelMixin, 26 | mixins.ListModelMixin, 27 | mixins.UpdateModelMixin, 28 | mixins.DestroyModelMixin, 29 | viewsets.GenericViewSet, 30 | ): 31 | authentication_classes = [] 32 | serializer_class = None 33 | permission_classes = [AllowAny] 34 | filter_class = UserFilter 35 | 36 | def get_queryset(self): 37 | queryset = User.objects.all() 38 | return queryset 39 | 40 | def get_serializer_class(self): 41 | if self.action == "register": 42 | return RegisterSerializer 43 | elif self.action == "login": 44 | return TokenSerializer 45 | elif self.action == "userinfo": 46 | return UserInfoSerializer 47 | elif self.action == "retrieve": 48 | return UserSerializer 49 | elif self.action == "list": 50 | return UserSerializer 51 | elif self.action == "partial_update": 52 | return UserSerializer 53 | elif self.action == "change_password": 54 | return PasswordSerializer 55 | return super().get_serializer_class() 56 | 57 | @extend_schema( 58 | summary="Register", 59 | responses={201: UserSerializer}, 60 | ) 61 | @action( 62 | ["POST"], 63 | detail=False, 64 | url_path="register", 65 | url_name="register", 66 | serializer_class=RegisterSerializer, 67 | ) 68 | def register(self, request): 69 | """用户注册""" 70 | serializer = self.get_serializer(data=request.data) 71 | serializer.is_valid(raise_exception=True) 72 | user = serializer.save() 73 | user.is_active = True 74 | user.save() 75 | signals.user_registered.send(sender=self.__class__, user=user, request=self.request) 76 | output_serializer = UserSerializer(instance=user, context={"request": request}) 77 | return Response( 78 | data=output_serializer.data, 79 | status=status.HTTP_201_CREATED, 80 | ) 81 | 82 | @extend_schema(summary="Login", request=TokenSerializer, responses={200: TokenRefreshSerializer}) 83 | @action( 84 | ["POST"], 85 | detail=False, 86 | url_path="login", 87 | url_name="login", 88 | serializer_class=TokenSerializer, 89 | ) 90 | def login(self, request): 91 | """用户登录""" 92 | serializer = self.get_serializer(data=request.data) 93 | serializer.is_valid(raise_exception=True) 94 | user = serializer.user 95 | user.last_login = now() 96 | user.save() 97 | return Response(serializer.validated_data, status=status.HTTP_200_OK) 98 | 99 | @extend_schema( 100 | summary="Refresh_token", 101 | request=TokenRefreshSerializer, 102 | ) 103 | @action( 104 | ["POST"], 105 | detail=False, 106 | url_name="refresh_token", 107 | url_path="refresh_token", 108 | serializer_class=TokenRefreshSerializer, 109 | ) 110 | def refresh_token(self, request): 111 | """刷新token""" 112 | serializer = self.get_serializer(data=request.data) 113 | serializer.is_valid(raise_exception=True) 114 | return Response(serializer.validated_data, status=status.HTTP_200_OK) 115 | 116 | @extend_schema(summary="Logout") 117 | @action( 118 | ["POST"], 119 | detail=False, 120 | url_name="logout", 121 | url_path="logout", 122 | authentication_classes=[JWTAuthentication], 123 | permission_classes=[IsAuthenticated], 124 | ) 125 | def logout(self, request): 126 | """退出登录""" 127 | header = jwt_auth.get_header(request) 128 | token = jwt_auth.get_raw_token(header).decode() 129 | validated_token = jwt_auth.get_validated_token(token) 130 | jwt_auth.get_user(validated_token) 131 | return Response(status=status.HTTP_200_OK) 132 | 133 | @extend_schema(summary="UserInfo") 134 | @action( 135 | ["GET"], 136 | detail=False, 137 | url_path="userinfo", 138 | url_name="userinfo", 139 | authentication_classes=[JWTAuthentication], 140 | permission_classes=[IsAuthenticated], 141 | ) 142 | def userinfo(self, request): 143 | """获取当前登录人的信息""" 144 | user = User.objects.get(username=request.user) 145 | output_serializer = UserInfoSerializer(user) 146 | return Response(status=status.HTTP_200_OK, data=output_serializer.data) 147 | 148 | @extend_schema( 149 | summary="change_password", 150 | request=PasswordSerializer, 151 | ) 152 | @action( 153 | ["POST"], 154 | detail=False, 155 | url_name="change_password", 156 | url_path="change_password", 157 | authentication_classes=[JWTAuthentication], 158 | permission_classes=[IsAuthenticated], 159 | ) 160 | def change_password(self, request): 161 | serializer = self.get_serializer(data=request.data) 162 | serializer.is_valid(raise_exception=True) 163 | serializer.set_password() 164 | signals.user_password_changed.send(sender=request.user.__class__, request=request, user=request.user) 165 | return Response(status=status.HTTP_200_OK) 166 | -------------------------------------------------------------------------------- /api/apps/vaccinate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/vaccinate/__init__.py -------------------------------------------------------------------------------- /api/apps/vaccinate/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api/apps/vaccinate/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class VaccinateConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "vaccinate" 7 | -------------------------------------------------------------------------------- /api/apps/vaccinate/filters.py: -------------------------------------------------------------------------------- 1 | from django_filters import rest_framework as filters 2 | 3 | from .models import Vaccinate 4 | 5 | 6 | class VaccinateFilter(filters.FilterSet): 7 | name = filters.CharFilter(field_name="name", lookup_expr="icontains") 8 | card = filters.CharFilter(field_name="card", lookup_expr="icontains") 9 | phone = filters.CharFilter(field_name="phone", lookup_expr="icontains") 10 | address = filters.CharFilter(field_name="address", lookup_expr="icontains") 11 | 12 | class Meta: 13 | model = Vaccinate 14 | fields = ["name", "card", "phone", "address"] 15 | -------------------------------------------------------------------------------- /api/apps/vaccinate/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-06-06 03:33 2 | 3 | import django.db.models.deletion 4 | import django.utils.timezone 5 | import model_utils.fields 6 | from django.conf import settings 7 | from django.db import migrations, models 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Vaccinate', 21 | fields=[ 22 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('created_at', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created_at')), 24 | ('modified_at', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified_at')), 25 | ('name', models.CharField(max_length=20, verbose_name='接种人姓名')), 26 | ('card', models.CharField(max_length=18, verbose_name='身份证号')), 27 | ('phone', models.CharField(max_length=11, verbose_name='联系电话')), 28 | ('address', models.CharField(max_length=64, verbose_name='联系地址')), 29 | ('vaccinateNo', models.CharField(max_length=4, verbose_name='接种次数')), 30 | ('vaccinateTime', models.CharField(max_length=10, verbose_name='接种时间')), 31 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vaccinate', to=settings.AUTH_USER_MODEL, verbose_name='记录员')), 32 | ], 33 | options={ 34 | 'db_table': 'vaccinate', 35 | }, 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /api/apps/vaccinate/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/api/apps/vaccinate/migrations/__init__.py -------------------------------------------------------------------------------- /api/apps/vaccinate/models.py: -------------------------------------------------------------------------------- 1 | from core.models import TimeStampedModel 2 | from django.contrib.auth import get_user_model 3 | from django.db import models 4 | from django.utils.translation import gettext_lazy as _ 5 | 6 | User = get_user_model() 7 | 8 | 9 | class Vaccinate(TimeStampedModel): 10 | name = models.CharField(_("接种人姓名"), max_length=20) 11 | card = models.CharField(_("身份证号"), max_length=18) 12 | phone = models.CharField(_("联系电话"), max_length=11) 13 | address = models.CharField(_("联系地址"), max_length=64) 14 | vaccinateNo = models.CharField(_("接种次数"), max_length=4) 15 | vaccinateTime = models.CharField(_("接种时间"), max_length=10) 16 | user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="记录员", related_name="vaccinate") 17 | 18 | class Meta: 19 | db_table = "vaccinate" 20 | -------------------------------------------------------------------------------- /api/apps/vaccinate/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from users.serializers import UserSerializer 3 | from vaccinate.models import Vaccinate 4 | 5 | 6 | class VaccinateSerializer(serializers.ModelSerializer): 7 | user = UserSerializer(read_only=True) 8 | 9 | class Meta: 10 | model = Vaccinate 11 | fields = [ 12 | "id", 13 | "name", 14 | "card", 15 | "phone", 16 | "address", 17 | "user", 18 | "vaccinateNo", 19 | "vaccinateTime", 20 | "created_at", 21 | "modified_at", 22 | ] 23 | 24 | 25 | class VaccinateCreateSerializer(serializers.ModelSerializer): 26 | user = serializers.ReadOnlyField(source="user.username") 27 | 28 | class Meta: 29 | model = Vaccinate 30 | fields = [ 31 | "id", 32 | "name", 33 | "card", 34 | "phone", 35 | "address", 36 | "user", 37 | "vaccinateNo", 38 | "vaccinateTime", 39 | ] 40 | -------------------------------------------------------------------------------- /api/apps/vaccinate/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /api/apps/vaccinate/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from rest_framework.permissions import IsAuthenticated 3 | 4 | from .filters import VaccinateFilter 5 | from .models import Vaccinate 6 | from .serializers import VaccinateCreateSerializer, VaccinateSerializer 7 | 8 | 9 | class VaccinateViewSet(viewsets.ModelViewSet): 10 | filter_class = VaccinateFilter 11 | 12 | def get_queryset(self): 13 | queryset = Vaccinate.objects.all().order_by("-created_at") 14 | return queryset 15 | 16 | def get_permissions(self): 17 | if self.action in {"create", "partial_update"}: 18 | return [IsAuthenticated()] 19 | return super().get_permissions() 20 | 21 | def get_serializer_class(self): 22 | if self.action in {"retrieve", "list"}: 23 | return VaccinateSerializer 24 | elif self.action in {"create", "partial_update"}: 25 | return VaccinateCreateSerializer 26 | 27 | def perform_create(self, serializer): 28 | serializer.save(user=self.request.user) 29 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/config/__init__.py -------------------------------------------------------------------------------- /config/api_router.py: -------------------------------------------------------------------------------- 1 | import abnormal.views 2 | import inspection.views 3 | import notices.views 4 | import reptile.views 5 | import users.views 6 | import vaccinate.views 7 | from rest_framework_extensions.routers import ExtendedDefaultRouter 8 | 9 | router = ExtendedDefaultRouter() 10 | 11 | router.register("auth", users.views.AuthViewSet, basename="auth") 12 | router.register("reptile", reptile.views.ReptileViewSet, basename="reptile") 13 | router.register("inspection", inspection.views.InspectionViewSet, basename="inspection") 14 | router.register("vaccinate", vaccinate.views.VaccinateViewSet, basename="vaccinate") 15 | router.register("abnormal", abnormal.views.AbnormalViewSet, basename="abnormal") 16 | router.register("notices", notices.views.NoticesViewSet, basename="notices") 17 | 18 | app_name = "api" 19 | urlpatterns = router.urls + [] 20 | -------------------------------------------------------------------------------- /config/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for config project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /config/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/config/settings/__init__.py -------------------------------------------------------------------------------- /config/settings/common.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for config project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.0.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.0/ref/settings/ 11 | """ 12 | import sys 13 | from datetime import timedelta 14 | from pathlib import Path 15 | 16 | import environ 17 | 18 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 19 | # 项目的基本路径 20 | BASE_DIR = Path(__file__).resolve().parent.parent.parent 21 | # APP的基本路径 22 | APPS_DIR = BASE_DIR.joinpath("api/apps") 23 | sys.path.insert(0, str(APPS_DIR)) 24 | env = environ.Env() 25 | 26 | # APPS 27 | # ------------------------------------------------------------------------------ 28 | DJANGO_APPS = [ 29 | "django.contrib.admin", 30 | "django.contrib.auth", 31 | "django.contrib.contenttypes", 32 | "django.contrib.sessions", 33 | "django.contrib.messages", 34 | "django.contrib.staticfiles", 35 | ] 36 | 37 | LOCAL_APPS = [ 38 | "core.apps.CoreConfig", 39 | "users.apps.UsersConfig", 40 | "reptile.apps.ReptileConfig", 41 | "inspection.apps.InspectionConfig", 42 | "vaccinate.apps.VaccinateConfig", 43 | "abnormal.apps.AbnormalConfig", 44 | "notices.apps.NoticesConfig", 45 | ] 46 | 47 | THIRD_PARTY_APPS = [ 48 | "corsheaders", 49 | "djoser", 50 | "django_extensions", 51 | "imagekit", 52 | "rest_framework", 53 | "rest_framework_simplejwt", 54 | "drf_spectacular", 55 | "django_filters", 56 | ] 57 | 58 | # 需要安装的app(Django自带的 + 第三方的 + 本地创建的) 59 | INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS 60 | 61 | 62 | MIDDLEWARE = [ 63 | "django.middleware.security.SecurityMiddleware", 64 | "django.contrib.sessions.middleware.SessionMiddleware", 65 | "corsheaders.middleware.CorsMiddleware", 66 | "django.middleware.common.CommonMiddleware", 67 | # 'django.middleware.csrf.CsrfViewMiddleware', 68 | "django.contrib.auth.middleware.AuthenticationMiddleware", 69 | "django.contrib.messages.middleware.MessageMiddleware", 70 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 71 | ] 72 | 73 | ROOT_URLCONF = "config.urls" 74 | 75 | TEMPLATES = [ 76 | { 77 | "BACKEND": "django.template.backends.django.DjangoTemplates", 78 | "DIRS": [BASE_DIR / "templates"], 79 | "APP_DIRS": True, 80 | "OPTIONS": { 81 | "context_processors": [ 82 | "django.template.context_processors.debug", 83 | "django.template.context_processors.request", 84 | "django.contrib.auth.context_processors.auth", 85 | "django.contrib.messages.context_processors.messages", 86 | ], 87 | }, 88 | }, 89 | ] 90 | 91 | WSGI_APPLICATION = "config.wsgi.application" # Django内置服务器标准 92 | ASGI_APPLICATION = "config.asgi.application" # Django异步服务器标准 93 | 94 | # Password validation 95 | # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators 96 | 97 | AUTH_PASSWORD_VALIDATORS = [ 98 | { 99 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 100 | }, 101 | { 102 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 103 | }, 104 | { 105 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 106 | }, 107 | { 108 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 109 | }, 110 | ] 111 | 112 | 113 | # Internationalization 114 | # https://docs.djangoproject.com/en/4.0/topics/i18n/ 115 | 116 | LANGUAGE_CODE = "zh-hans" 117 | 118 | TIME_ZONE = "Asia/Shanghai" 119 | 120 | USE_I18N = True 121 | 122 | USE_L10N = True 123 | 124 | USE_TZ = True 125 | 126 | 127 | # Static files (CSS, JavaScript, Images) 128 | # https://docs.djangoproject.com/en/4.0/howto/static-files/ 129 | STATIC_ROOT = env.str("DJANGO_STATIC_ROOT", default=str(BASE_DIR / "staticfiles")) 130 | STATIC_URL = "/static/" 131 | 132 | # MEDIA 133 | # ------------------------------------------------------------------------------ 134 | # https://docs.djangoproject.com/en/dev/ref/settings/#media-root 135 | MEDIA_ROOT = env.str("DJANGO_MEDIA_ROOT", str(BASE_DIR / "media")) 136 | # https://docs.djangoproject.com/en/dev/ref/settings/#media-url 137 | MEDIA_URL = "/media/" 138 | 139 | # Default primary key field type 140 | # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field 141 | 142 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 143 | 144 | # 设置默认的user模型 145 | AUTH_USER_MODEL = "users.User" 146 | 147 | # 允许所有域名来访问我们的服务器 148 | CORS_ORIGIN_ALLOW_ALL = True 149 | 150 | # drf-spectacular 151 | # ------------------------------------------------------------------------------ 152 | SPECTACULAR_SETTINGS = { 153 | # path prefix is used for tagging the discovered operations. 154 | # use '/api/v[0-9]' for tagging apis like '/api/v1/albums' with ['albums'] 155 | "SCHEMA_PATH_PREFIX": r"/api/v[0-9]", 156 | } 157 | 158 | # Django REST framework 159 | # ------------------------------------------------------------------------------ 160 | # https://www.django-rest-framework.org/api-guide/settings/ 161 | REST_FRAMEWORK = { 162 | # 自定义异常 163 | "EXCEPTION_HANDLER": "core.views.exception_handler", 164 | # json渲染 165 | "DEFAULT_PARSER_CLASSES": [ 166 | "rest_framework.parsers.JSONParser", 167 | "rest_framework.parsers.FormParser", 168 | "rest_framework.parsers.MultiPartParser", 169 | "rest_framework.parsers.FileUploadParser", 170 | ], 171 | # 自定义的认证类 172 | "DEFAULT_AUTHENTICATION_CLASSES": ("rest_framework_simplejwt.authentication.JWTAuthentication",), 173 | # 使用drf的权限验证 174 | "DEFAULT_PERMISSION_CLASSES": [ 175 | "rest_framework.permissions.IsAuthenticated", 176 | ], 177 | # 自定义分页器 178 | "DEFAULT_PAGINATION_CLASS": "core.pagination.MyPageNumberPagination", 179 | # 过滤器默认后端 180 | "DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",), 181 | # API文档 182 | "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", 183 | "TEST_REQUEST_DEFAULT_FORMAT": "json", 184 | } 185 | 186 | # https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html 187 | SIMPLE_JWT = { 188 | "AUTH_HEADER_TYPES": ("jwt",), 189 | "ACCESS_TOKEN_LIFETIME": timedelta(days=365), 190 | } 191 | 192 | # DRF-extensions 193 | # ------------------------------------------------------------------------------ 194 | # http://chibisov.github.io/drf-extensions/docs/#settings 195 | REST_FRAMEWORK_EXTENSIONS = { 196 | "DEFAULT_PARENT_LOOKUP_KWARG_NAME_PREFIX": "", 197 | } 198 | 199 | # djoser 200 | # ------------------------------------------------------------------------------ 201 | # https://djoser.readthedocs.io/en/latest/settings.html 202 | DJOSER = { 203 | "ACTIVATION_URL": "activate/{uid}/{token}", 204 | "PASSWORD_RESET_CONFIRM_URL": "password/reset/confirm/{uid}/{token}", 205 | "SEND_ACTIVATION_EMAIL": True, 206 | } 207 | -------------------------------------------------------------------------------- /config/settings/local.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | from .common import * # noqa F405 6 | 7 | # SECURITY WARNING: keep the secret key used in production secret! 8 | SECRET_KEY = "django-insecure-cfw$q)uk8q1r&7x&nsc9-*g&lc7*z@-$$jr0+18@b*2fw#g19m" 9 | 10 | # django-extensions 11 | INSTALLED_APPS += [] # noqa F405 12 | 13 | ALLOWED_HOSTS = ["*"] 14 | DEBUG = True 15 | 16 | # Database 17 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 18 | 19 | DATABASES = { 20 | "default": { 21 | "ENGINE": "django.db.backends.mysql", 22 | "NAME": "epidemic", # noqa F405 23 | "USER": "root", 24 | "PASSWORD": "123456", 25 | "HOST": "127.0.0.1", 26 | "PORT": 3306, 27 | } 28 | } 29 | 30 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 31 | 32 | # django-debug-toolbar 33 | # ------------------------------------------------------------------------------ 34 | # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites 35 | INSTALLED_APPS += ["debug_toolbar"] # noqa F405 36 | # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware 37 | MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa F405 38 | # https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config 39 | DEBUG_TOOLBAR_CONFIG = { 40 | "DISABLE_PANELS": ["debug_toolbar.panels.redirects.RedirectsPanel"], 41 | "SHOW_TEMPLATE_CONTEXT": True, 42 | } 43 | # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips 44 | INTERNAL_IPS = ["127.0.0.1", "localhost", "0.0.0.0"] 45 | -------------------------------------------------------------------------------- /config/settings/production.py: -------------------------------------------------------------------------------- 1 | from .common import * # noqa 2 | from .common import env 3 | 4 | SECRET_KEY = env.str("DJANGO_SECRET_KEY") 5 | DEBUG = env.bool("DJANGO_DEBUG", False) 6 | ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=[]) 7 | CORS_ORIGIN_ALLOW_ALL = True 8 | 9 | SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") 10 | 11 | # DATABASES 12 | # ------------------------------------------------------------------------------ 13 | DATABASES = {"default": env.db("DJANGO_DATABASE_DEFAULT_URL")} # noqa F405 14 | DATABASES["default"]["ATOMIC_REQUESTS"] = True # noqa F405 15 | DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa F405 16 | -------------------------------------------------------------------------------- /config/urls.py: -------------------------------------------------------------------------------- 1 | """config URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf import settings 17 | from django.contrib import admin 18 | from django.urls import include, path 19 | from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView 20 | 21 | urlpatterns = [ 22 | path("admin/", admin.site.urls), 23 | path("api/v1/", include("config.api_router")), 24 | ] 25 | 26 | 27 | if settings.DEBUG: 28 | # https://www.django-rest-framework.org/#installation 29 | urlpatterns += [path("api-auth/", include("rest_framework.urls", namespace="rest_framework"))] 30 | 31 | if "debug_toolbar" in settings.INSTALLED_APPS: 32 | import debug_toolbar 33 | 34 | urlpatterns = [ 35 | path("__debug__/", include(debug_toolbar.urls)), 36 | path("schema/", SpectacularAPIView.as_view(), name="schema"), 37 | path( 38 | "swagger/", 39 | SpectacularSwaggerView.as_view(url_name="schema"), 40 | name="swagger", 41 | ), 42 | path( 43 | "redoc/", 44 | SpectacularRedocView.as_view(url_name="schema"), 45 | name="redoc", 46 | ), 47 | ] + urlpatterns 48 | -------------------------------------------------------------------------------- /config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for config project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "asgiref" 3 | version = "3.5.1" 4 | description = "ASGI specs, helper code, and adapters" 5 | category = "main" 6 | optional = false 7 | python-versions = ">=3.7" 8 | 9 | [package.extras] 10 | tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] 11 | 12 | [package.source] 13 | type = "legacy" 14 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 15 | reference = "tsinghua" 16 | 17 | [[package]] 18 | name = "atomicwrites" 19 | version = "1.4.0" 20 | description = "Atomic file writes." 21 | category = "dev" 22 | optional = false 23 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 24 | 25 | [package.source] 26 | type = "legacy" 27 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 28 | reference = "tsinghua" 29 | 30 | [[package]] 31 | name = "attrs" 32 | version = "21.4.0" 33 | description = "Classes Without Boilerplate" 34 | category = "main" 35 | optional = false 36 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 37 | 38 | [package.extras] 39 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 40 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 41 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 42 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] 43 | 44 | [package.source] 45 | type = "legacy" 46 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 47 | reference = "tsinghua" 48 | 49 | [[package]] 50 | name = "backports.zoneinfo" 51 | version = "0.2.1" 52 | description = "Backport of the standard library zoneinfo module" 53 | category = "main" 54 | optional = false 55 | python-versions = ">=3.6" 56 | 57 | [package.extras] 58 | tzdata = ["tzdata"] 59 | 60 | [package.source] 61 | type = "legacy" 62 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 63 | reference = "tsinghua" 64 | 65 | [[package]] 66 | name = "black" 67 | version = "22.3.0" 68 | description = "The uncompromising code formatter." 69 | category = "dev" 70 | optional = false 71 | python-versions = ">=3.6.2" 72 | 73 | [package.dependencies] 74 | click = ">=8.0.0" 75 | mypy-extensions = ">=0.4.3" 76 | pathspec = ">=0.9.0" 77 | platformdirs = ">=2" 78 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 79 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 80 | 81 | [package.extras] 82 | colorama = ["colorama (>=0.4.3)"] 83 | d = ["aiohttp (>=3.7.4)"] 84 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 85 | uvloop = ["uvloop (>=0.15.2)"] 86 | 87 | [package.source] 88 | type = "legacy" 89 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 90 | reference = "tsinghua" 91 | 92 | [[package]] 93 | name = "certifi" 94 | version = "2021.10.8" 95 | description = "Python package for providing Mozilla's CA Bundle." 96 | category = "main" 97 | optional = false 98 | python-versions = "*" 99 | 100 | [package.source] 101 | type = "legacy" 102 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 103 | reference = "tsinghua" 104 | 105 | [[package]] 106 | name = "cffi" 107 | version = "1.15.0" 108 | description = "Foreign Function Interface for Python calling C code." 109 | category = "main" 110 | optional = false 111 | python-versions = "*" 112 | 113 | [package.dependencies] 114 | pycparser = "*" 115 | 116 | [package.source] 117 | type = "legacy" 118 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 119 | reference = "tsinghua" 120 | 121 | [[package]] 122 | name = "charset-normalizer" 123 | version = "2.0.12" 124 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 125 | category = "main" 126 | optional = false 127 | python-versions = ">=3.5.0" 128 | 129 | [package.extras] 130 | unicode_backport = ["unicodedata2"] 131 | 132 | [package.source] 133 | type = "legacy" 134 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 135 | reference = "tsinghua" 136 | 137 | [[package]] 138 | name = "click" 139 | version = "8.1.3" 140 | description = "Composable command line interface toolkit" 141 | category = "dev" 142 | optional = false 143 | python-versions = ">=3.7" 144 | 145 | [package.dependencies] 146 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 147 | 148 | [package.source] 149 | type = "legacy" 150 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 151 | reference = "tsinghua" 152 | 153 | [[package]] 154 | name = "colorama" 155 | version = "0.4.4" 156 | description = "Cross-platform colored terminal text." 157 | category = "dev" 158 | optional = false 159 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 160 | 161 | [package.source] 162 | type = "legacy" 163 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 164 | reference = "tsinghua" 165 | 166 | [[package]] 167 | name = "coreapi" 168 | version = "2.3.3" 169 | description = "Python client library for Core API." 170 | category = "main" 171 | optional = false 172 | python-versions = "*" 173 | 174 | [package.dependencies] 175 | coreschema = "*" 176 | itypes = "*" 177 | requests = "*" 178 | uritemplate = "*" 179 | 180 | [package.source] 181 | type = "legacy" 182 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 183 | reference = "tsinghua" 184 | 185 | [[package]] 186 | name = "coreschema" 187 | version = "0.0.4" 188 | description = "Core Schema." 189 | category = "main" 190 | optional = false 191 | python-versions = "*" 192 | 193 | [package.dependencies] 194 | jinja2 = "*" 195 | 196 | [package.source] 197 | type = "legacy" 198 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 199 | reference = "tsinghua" 200 | 201 | [[package]] 202 | name = "cryptography" 203 | version = "37.0.2" 204 | description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." 205 | category = "main" 206 | optional = false 207 | python-versions = ">=3.6" 208 | 209 | [package.dependencies] 210 | cffi = ">=1.12" 211 | 212 | [package.extras] 213 | docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] 214 | docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] 215 | pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] 216 | sdist = ["setuptools_rust (>=0.11.4)"] 217 | ssh = ["bcrypt (>=3.1.5)"] 218 | test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] 219 | 220 | [package.source] 221 | type = "legacy" 222 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 223 | reference = "tsinghua" 224 | 225 | [[package]] 226 | name = "defusedxml" 227 | version = "0.7.1" 228 | description = "XML bomb protection for Python stdlib modules" 229 | category = "main" 230 | optional = false 231 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 232 | 233 | [package.source] 234 | type = "legacy" 235 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 236 | reference = "tsinghua" 237 | 238 | [[package]] 239 | name = "django" 240 | version = "4.0.4" 241 | description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." 242 | category = "main" 243 | optional = false 244 | python-versions = ">=3.8" 245 | 246 | [package.dependencies] 247 | asgiref = ">=3.4.1,<4" 248 | "backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} 249 | sqlparse = ">=0.2.2" 250 | tzdata = {version = "*", markers = "sys_platform == \"win32\""} 251 | 252 | [package.extras] 253 | argon2 = ["argon2-cffi (>=19.1.0)"] 254 | bcrypt = ["bcrypt"] 255 | 256 | [package.source] 257 | type = "legacy" 258 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 259 | reference = "tsinghua" 260 | 261 | [[package]] 262 | name = "django-appconf" 263 | version = "1.0.5" 264 | description = "A helper class for handling configuration defaults of packaged apps gracefully." 265 | category = "main" 266 | optional = false 267 | python-versions = ">=3.6" 268 | 269 | [package.dependencies] 270 | django = "*" 271 | 272 | [package.source] 273 | type = "legacy" 274 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 275 | reference = "tsinghua" 276 | 277 | [[package]] 278 | name = "django-cors-headers" 279 | version = "3.12.0" 280 | description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)." 281 | category = "main" 282 | optional = false 283 | python-versions = ">=3.7" 284 | 285 | [package.dependencies] 286 | Django = ">=3.2" 287 | 288 | [package.source] 289 | type = "legacy" 290 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 291 | reference = "tsinghua" 292 | 293 | [[package]] 294 | name = "django-debug-toolbar" 295 | version = "3.4.0" 296 | description = "A configurable set of panels that display various debug information about the current request/response." 297 | category = "dev" 298 | optional = false 299 | python-versions = ">=3.7" 300 | 301 | [package.dependencies] 302 | Django = ">=3.2" 303 | sqlparse = ">=0.2.0" 304 | 305 | [package.source] 306 | type = "legacy" 307 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 308 | reference = "tsinghua" 309 | 310 | [[package]] 311 | name = "django-environ" 312 | version = "0.8.1" 313 | description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application." 314 | category = "main" 315 | optional = false 316 | python-versions = ">=3.4,<4" 317 | 318 | [package.extras] 319 | develop = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)", "furo (>=2021.8.17b43,<2021.9.0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] 320 | docs = ["furo (>=2021.8.17b43,<2021.9.0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"] 321 | testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"] 322 | 323 | [package.source] 324 | type = "legacy" 325 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 326 | reference = "tsinghua" 327 | 328 | [[package]] 329 | name = "django-extensions" 330 | version = "3.1.5" 331 | description = "Extensions for Django" 332 | category = "main" 333 | optional = false 334 | python-versions = ">=3.6" 335 | 336 | [package.dependencies] 337 | Django = ">=2.2" 338 | 339 | [package.source] 340 | type = "legacy" 341 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 342 | reference = "tsinghua" 343 | 344 | [[package]] 345 | name = "django-filter" 346 | version = "21.1" 347 | description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." 348 | category = "main" 349 | optional = false 350 | python-versions = ">=3.6" 351 | 352 | [package.dependencies] 353 | Django = ">=2.2" 354 | 355 | [package.source] 356 | type = "legacy" 357 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 358 | reference = "tsinghua" 359 | 360 | [[package]] 361 | name = "django-imagekit" 362 | version = "4.1.0" 363 | description = "Automated image processing for Django models." 364 | category = "main" 365 | optional = false 366 | python-versions = "*" 367 | 368 | [package.dependencies] 369 | django-appconf = {version = "*", markers = "python_version > \"3\""} 370 | pilkit = ">=0.2.0" 371 | six = "*" 372 | 373 | [package.extras] 374 | async = ["django-celery (>=3.0)"] 375 | async_dramatiq = ["django-dramatiq (>=0.4.0)"] 376 | async_rq = ["django-rq (>=0.6.0)"] 377 | 378 | [package.source] 379 | type = "legacy" 380 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 381 | reference = "tsinghua" 382 | 383 | [[package]] 384 | name = "django-model-utils" 385 | version = "4.2.0" 386 | description = "Django model mixins and utilities" 387 | category = "main" 388 | optional = false 389 | python-versions = "*" 390 | 391 | [package.dependencies] 392 | Django = ">=2.0.1" 393 | 394 | [package.source] 395 | type = "legacy" 396 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 397 | reference = "tsinghua" 398 | 399 | [[package]] 400 | name = "django-templated-mail" 401 | version = "1.1.1" 402 | description = "Send emails using Django template system." 403 | category = "main" 404 | optional = false 405 | python-versions = "*" 406 | 407 | [package.source] 408 | type = "legacy" 409 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 410 | reference = "tsinghua" 411 | 412 | [[package]] 413 | name = "django-test-plus" 414 | version = "2.2.0" 415 | description = "django-test-plus provides useful additions to Django's default TestCase" 416 | category = "dev" 417 | optional = false 418 | python-versions = "*" 419 | 420 | [package.source] 421 | type = "legacy" 422 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 423 | reference = "tsinghua" 424 | 425 | [[package]] 426 | name = "djangorestframework" 427 | version = "3.13.1" 428 | description = "Web APIs for Django, made easy." 429 | category = "main" 430 | optional = false 431 | python-versions = ">=3.6" 432 | 433 | [package.dependencies] 434 | django = ">=2.2" 435 | pytz = "*" 436 | 437 | [package.source] 438 | type = "legacy" 439 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 440 | reference = "tsinghua" 441 | 442 | [[package]] 443 | name = "djangorestframework-simplejwt" 444 | version = "4.8.0" 445 | description = "A minimal JSON Web Token authentication plugin for Django REST Framework" 446 | category = "main" 447 | optional = false 448 | python-versions = ">=3.7" 449 | 450 | [package.dependencies] 451 | django = "*" 452 | djangorestframework = "*" 453 | pyjwt = ">=2,<3" 454 | 455 | [package.extras] 456 | dev = ["pytest-watch", "wheel", "twine", "ipython", "cryptography", "pytest-cov", "pytest-django", "pytest-xdist", "pytest", "tox", "flake8", "pep8", "isort", "Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)", "python-jose (==3.0.0)"] 457 | doc = ["Sphinx (>=1.6.5,<2)", "sphinx-rtd-theme (>=0.1.9)"] 458 | lint = ["flake8", "pep8", "isort"] 459 | python-jose = ["python-jose (==3.0.0)"] 460 | test = ["cryptography", "pytest-cov", "pytest-django", "pytest-xdist", "pytest", "tox"] 461 | 462 | [package.source] 463 | type = "legacy" 464 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 465 | reference = "tsinghua" 466 | 467 | [[package]] 468 | name = "djoser" 469 | version = "2.1.0" 470 | description = "REST implementation of Django authentication system." 471 | category = "main" 472 | optional = false 473 | python-versions = ">=3.6.1,<4.0.0" 474 | 475 | [package.dependencies] 476 | asgiref = ">=3.2.10,<4.0.0" 477 | coreapi = ">=2.3.3,<3.0.0" 478 | django-templated-mail = ">=1.1.1,<2.0.0" 479 | djangorestframework-simplejwt = ">=4.3.0,<5.0.0" 480 | social-auth-app-django = ">=4.0.0,<5.0.0" 481 | 482 | [package.extras] 483 | test = ["codecov (>=2.0.16,<3.0.0)", "coverage (>=5.3,<6.0)", "djet (>=0.2.2,<0.3.0)", "pytest (>=6.0.2,<7.0.0)", "pytest-cov (>=2.10.1,<3.0.0)", "pytest-django (>=3.10.0,<4.0.0)", "pytest-pythonpath (>=0.7.3,<0.8.0)"] 484 | 485 | [package.source] 486 | type = "legacy" 487 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 488 | reference = "tsinghua" 489 | 490 | [[package]] 491 | name = "drf-extensions" 492 | version = "0.7.1" 493 | description = "Extensions for Django REST Framework" 494 | category = "main" 495 | optional = false 496 | python-versions = "*" 497 | 498 | [package.dependencies] 499 | djangorestframework = ">=3.9.3" 500 | 501 | [package.source] 502 | type = "legacy" 503 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 504 | reference = "tsinghua" 505 | 506 | [[package]] 507 | name = "drf-spectacular" 508 | version = "0.22.1" 509 | description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework" 510 | category = "main" 511 | optional = false 512 | python-versions = ">=3.6" 513 | 514 | [package.dependencies] 515 | Django = ">=2.2" 516 | djangorestframework = ">=3.10" 517 | inflection = ">=0.3.1" 518 | jsonschema = ">=2.6.0" 519 | PyYAML = ">=5.1" 520 | uritemplate = ">=2.0.0" 521 | 522 | [package.extras] 523 | offline = ["drf-spectacular-sidecar"] 524 | sidecar = ["drf-spectacular-sidecar"] 525 | 526 | [package.source] 527 | type = "legacy" 528 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 529 | reference = "tsinghua" 530 | 531 | [[package]] 532 | name = "factory-boy" 533 | version = "3.2.1" 534 | description = "A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby." 535 | category = "main" 536 | optional = false 537 | python-versions = ">=3.6" 538 | 539 | [package.dependencies] 540 | Faker = ">=0.7.0" 541 | 542 | [package.extras] 543 | dev = ["coverage", "django", "flake8", "isort", "pillow", "sqlalchemy", "mongoengine", "wheel (>=0.32.0)", "tox", "zest.releaser"] 544 | doc = ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] 545 | 546 | [package.source] 547 | type = "legacy" 548 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 549 | reference = "tsinghua" 550 | 551 | [[package]] 552 | name = "faker" 553 | version = "13.11.1" 554 | description = "Faker is a Python package that generates fake data for you." 555 | category = "main" 556 | optional = false 557 | python-versions = ">=3.6" 558 | 559 | [package.dependencies] 560 | python-dateutil = ">=2.4" 561 | 562 | [package.source] 563 | type = "legacy" 564 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 565 | reference = "tsinghua" 566 | 567 | [[package]] 568 | name = "flake8" 569 | version = "4.0.1" 570 | description = "the modular source code checker: pep8 pyflakes and co" 571 | category = "dev" 572 | optional = false 573 | python-versions = ">=3.6" 574 | 575 | [package.dependencies] 576 | mccabe = ">=0.6.0,<0.7.0" 577 | pycodestyle = ">=2.8.0,<2.9.0" 578 | pyflakes = ">=2.4.0,<2.5.0" 579 | 580 | [package.source] 581 | type = "legacy" 582 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 583 | reference = "tsinghua" 584 | 585 | [[package]] 586 | name = "flake8-isort" 587 | version = "4.1.1" 588 | description = "flake8 plugin that integrates isort ." 589 | category = "dev" 590 | optional = false 591 | python-versions = "*" 592 | 593 | [package.dependencies] 594 | flake8 = ">=3.2.1,<5" 595 | isort = ">=4.3.5,<6" 596 | testfixtures = ">=6.8.0,<7" 597 | 598 | [package.extras] 599 | test = ["pytest-cov"] 600 | 601 | [package.source] 602 | type = "legacy" 603 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 604 | reference = "tsinghua" 605 | 606 | [[package]] 607 | name = "idna" 608 | version = "3.3" 609 | description = "Internationalized Domain Names in Applications (IDNA)" 610 | category = "main" 611 | optional = false 612 | python-versions = ">=3.5" 613 | 614 | [package.source] 615 | type = "legacy" 616 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 617 | reference = "tsinghua" 618 | 619 | [[package]] 620 | name = "importlib-resources" 621 | version = "5.7.1" 622 | description = "Read resources from Python packages" 623 | category = "main" 624 | optional = false 625 | python-versions = ">=3.7" 626 | 627 | [package.dependencies] 628 | zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} 629 | 630 | [package.extras] 631 | docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] 632 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] 633 | 634 | [package.source] 635 | type = "legacy" 636 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 637 | reference = "tsinghua" 638 | 639 | [[package]] 640 | name = "inflection" 641 | version = "0.5.1" 642 | description = "A port of Ruby on Rails inflector to Python" 643 | category = "main" 644 | optional = false 645 | python-versions = ">=3.5" 646 | 647 | [package.source] 648 | type = "legacy" 649 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 650 | reference = "tsinghua" 651 | 652 | [[package]] 653 | name = "iniconfig" 654 | version = "1.1.1" 655 | description = "iniconfig: brain-dead simple config-ini parsing" 656 | category = "dev" 657 | optional = false 658 | python-versions = "*" 659 | 660 | [package.source] 661 | type = "legacy" 662 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 663 | reference = "tsinghua" 664 | 665 | [[package]] 666 | name = "isort" 667 | version = "5.10.1" 668 | description = "A Python utility / library to sort Python imports." 669 | category = "dev" 670 | optional = false 671 | python-versions = ">=3.6.1,<4.0" 672 | 673 | [package.extras] 674 | colors = ["colorama (>=0.4.3,<0.5.0)"] 675 | requirements_deprecated_finder = ["pip-api", "pipreqs"] 676 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 677 | plugins = ["setuptools"] 678 | 679 | [package.source] 680 | type = "legacy" 681 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 682 | reference = "tsinghua" 683 | 684 | [[package]] 685 | name = "itypes" 686 | version = "1.2.0" 687 | description = "Simple immutable types for python." 688 | category = "main" 689 | optional = false 690 | python-versions = "*" 691 | 692 | [package.source] 693 | type = "legacy" 694 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 695 | reference = "tsinghua" 696 | 697 | [[package]] 698 | name = "jinja2" 699 | version = "3.1.2" 700 | description = "A very fast and expressive template engine." 701 | category = "main" 702 | optional = false 703 | python-versions = ">=3.7" 704 | 705 | [package.dependencies] 706 | MarkupSafe = ">=2.0" 707 | 708 | [package.extras] 709 | i18n = ["Babel (>=2.7)"] 710 | 711 | [package.source] 712 | type = "legacy" 713 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 714 | reference = "tsinghua" 715 | 716 | [[package]] 717 | name = "jsonschema" 718 | version = "4.5.1" 719 | description = "An implementation of JSON Schema validation for Python" 720 | category = "main" 721 | optional = false 722 | python-versions = ">=3.7" 723 | 724 | [package.dependencies] 725 | attrs = ">=17.4.0" 726 | importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} 727 | pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" 728 | 729 | [package.extras] 730 | format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] 731 | format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] 732 | 733 | [package.source] 734 | type = "legacy" 735 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 736 | reference = "tsinghua" 737 | 738 | [[package]] 739 | name = "markupsafe" 740 | version = "2.1.1" 741 | description = "Safely add untrusted strings to HTML/XML markup." 742 | category = "main" 743 | optional = false 744 | python-versions = ">=3.7" 745 | 746 | [package.source] 747 | type = "legacy" 748 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 749 | reference = "tsinghua" 750 | 751 | [[package]] 752 | name = "mccabe" 753 | version = "0.6.1" 754 | description = "McCabe checker, plugin for flake8" 755 | category = "dev" 756 | optional = false 757 | python-versions = "*" 758 | 759 | [package.source] 760 | type = "legacy" 761 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 762 | reference = "tsinghua" 763 | 764 | [[package]] 765 | name = "mypy-extensions" 766 | version = "0.4.3" 767 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 768 | category = "dev" 769 | optional = false 770 | python-versions = "*" 771 | 772 | [package.source] 773 | type = "legacy" 774 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 775 | reference = "tsinghua" 776 | 777 | [[package]] 778 | name = "mysqlclient" 779 | version = "2.1.0" 780 | description = "Python interface to MySQL" 781 | category = "main" 782 | optional = false 783 | python-versions = ">=3.5" 784 | 785 | [package.source] 786 | type = "legacy" 787 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 788 | reference = "tsinghua" 789 | 790 | [[package]] 791 | name = "oauthlib" 792 | version = "3.2.0" 793 | description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" 794 | category = "main" 795 | optional = false 796 | python-versions = ">=3.6" 797 | 798 | [package.extras] 799 | rsa = ["cryptography (>=3.0.0)"] 800 | signals = ["blinker (>=1.4.0)"] 801 | signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] 802 | 803 | [package.source] 804 | type = "legacy" 805 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 806 | reference = "tsinghua" 807 | 808 | [[package]] 809 | name = "packaging" 810 | version = "21.3" 811 | description = "Core utilities for Python packages" 812 | category = "dev" 813 | optional = false 814 | python-versions = ">=3.6" 815 | 816 | [package.dependencies] 817 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 818 | 819 | [package.source] 820 | type = "legacy" 821 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 822 | reference = "tsinghua" 823 | 824 | [[package]] 825 | name = "pathspec" 826 | version = "0.9.0" 827 | description = "Utility library for gitignore style pattern matching of file paths." 828 | category = "dev" 829 | optional = false 830 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 831 | 832 | [package.source] 833 | type = "legacy" 834 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 835 | reference = "tsinghua" 836 | 837 | [[package]] 838 | name = "pilkit" 839 | version = "2.0" 840 | description = "A collection of utilities and processors for the Python Imaging Libary." 841 | category = "main" 842 | optional = false 843 | python-versions = "*" 844 | 845 | [package.source] 846 | type = "legacy" 847 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 848 | reference = "tsinghua" 849 | 850 | [[package]] 851 | name = "pillow" 852 | version = "9.1.0" 853 | description = "Python Imaging Library (Fork)" 854 | category = "main" 855 | optional = false 856 | python-versions = ">=3.7" 857 | 858 | [package.extras] 859 | docs = ["olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinx-rtd-theme (>=1.0)", "sphinxext-opengraph"] 860 | tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] 861 | 862 | [package.source] 863 | type = "legacy" 864 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 865 | reference = "tsinghua" 866 | 867 | [[package]] 868 | name = "platformdirs" 869 | version = "2.5.2" 870 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 871 | category = "dev" 872 | optional = false 873 | python-versions = ">=3.7" 874 | 875 | [package.extras] 876 | docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] 877 | test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] 878 | 879 | [package.source] 880 | type = "legacy" 881 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 882 | reference = "tsinghua" 883 | 884 | [[package]] 885 | name = "pluggy" 886 | version = "1.0.0" 887 | description = "plugin and hook calling mechanisms for python" 888 | category = "dev" 889 | optional = false 890 | python-versions = ">=3.6" 891 | 892 | [package.extras] 893 | dev = ["pre-commit", "tox"] 894 | testing = ["pytest", "pytest-benchmark"] 895 | 896 | [package.source] 897 | type = "legacy" 898 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 899 | reference = "tsinghua" 900 | 901 | [[package]] 902 | name = "py" 903 | version = "1.11.0" 904 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 905 | category = "dev" 906 | optional = false 907 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 908 | 909 | [package.source] 910 | type = "legacy" 911 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 912 | reference = "tsinghua" 913 | 914 | [[package]] 915 | name = "pycodestyle" 916 | version = "2.8.0" 917 | description = "Python style guide checker" 918 | category = "dev" 919 | optional = false 920 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 921 | 922 | [package.source] 923 | type = "legacy" 924 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 925 | reference = "tsinghua" 926 | 927 | [[package]] 928 | name = "pycparser" 929 | version = "2.21" 930 | description = "C parser in Python" 931 | category = "main" 932 | optional = false 933 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 934 | 935 | [package.source] 936 | type = "legacy" 937 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 938 | reference = "tsinghua" 939 | 940 | [[package]] 941 | name = "pyflakes" 942 | version = "2.4.0" 943 | description = "passive checker of Python programs" 944 | category = "dev" 945 | optional = false 946 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 947 | 948 | [package.source] 949 | type = "legacy" 950 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 951 | reference = "tsinghua" 952 | 953 | [[package]] 954 | name = "pyjwt" 955 | version = "2.4.0" 956 | description = "JSON Web Token implementation in Python" 957 | category = "main" 958 | optional = false 959 | python-versions = ">=3.6" 960 | 961 | [package.extras] 962 | crypto = ["cryptography (>=3.3.1)"] 963 | dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] 964 | docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] 965 | tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] 966 | 967 | [package.source] 968 | type = "legacy" 969 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 970 | reference = "tsinghua" 971 | 972 | [[package]] 973 | name = "pyparsing" 974 | version = "3.0.9" 975 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 976 | category = "dev" 977 | optional = false 978 | python-versions = ">=3.6.8" 979 | 980 | [package.extras] 981 | diagrams = ["railroad-diagrams", "jinja2"] 982 | 983 | [package.source] 984 | type = "legacy" 985 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 986 | reference = "tsinghua" 987 | 988 | [[package]] 989 | name = "pyrsistent" 990 | version = "0.18.1" 991 | description = "Persistent/Functional/Immutable data structures" 992 | category = "main" 993 | optional = false 994 | python-versions = ">=3.7" 995 | 996 | [package.source] 997 | type = "legacy" 998 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 999 | reference = "tsinghua" 1000 | 1001 | [[package]] 1002 | name = "pytest" 1003 | version = "7.1.2" 1004 | description = "pytest: simple powerful testing with Python" 1005 | category = "dev" 1006 | optional = false 1007 | python-versions = ">=3.7" 1008 | 1009 | [package.dependencies] 1010 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 1011 | attrs = ">=19.2.0" 1012 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 1013 | iniconfig = "*" 1014 | packaging = "*" 1015 | pluggy = ">=0.12,<2.0" 1016 | py = ">=1.8.2" 1017 | tomli = ">=1.0.0" 1018 | 1019 | [package.extras] 1020 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 1021 | 1022 | [package.source] 1023 | type = "legacy" 1024 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1025 | reference = "tsinghua" 1026 | 1027 | [[package]] 1028 | name = "pytest-django" 1029 | version = "4.5.2" 1030 | description = "A Django plugin for pytest." 1031 | category = "dev" 1032 | optional = false 1033 | python-versions = ">=3.5" 1034 | 1035 | [package.dependencies] 1036 | pytest = ">=5.4.0" 1037 | 1038 | [package.extras] 1039 | docs = ["sphinx", "sphinx-rtd-theme"] 1040 | testing = ["django", "django-configurations (>=2.0)"] 1041 | 1042 | [package.source] 1043 | type = "legacy" 1044 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1045 | reference = "tsinghua" 1046 | 1047 | [[package]] 1048 | name = "python-dateutil" 1049 | version = "2.8.2" 1050 | description = "Extensions to the standard Python datetime module" 1051 | category = "main" 1052 | optional = false 1053 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 1054 | 1055 | [package.dependencies] 1056 | six = ">=1.5" 1057 | 1058 | [package.source] 1059 | type = "legacy" 1060 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1061 | reference = "tsinghua" 1062 | 1063 | [[package]] 1064 | name = "python3-openid" 1065 | version = "3.2.0" 1066 | description = "OpenID support for modern servers and consumers." 1067 | category = "main" 1068 | optional = false 1069 | python-versions = "*" 1070 | 1071 | [package.dependencies] 1072 | defusedxml = "*" 1073 | 1074 | [package.extras] 1075 | mysql = ["mysql-connector-python"] 1076 | postgresql = ["psycopg2"] 1077 | 1078 | [package.source] 1079 | type = "legacy" 1080 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1081 | reference = "tsinghua" 1082 | 1083 | [[package]] 1084 | name = "pytz" 1085 | version = "2022.1" 1086 | description = "World timezone definitions, modern and historical" 1087 | category = "main" 1088 | optional = false 1089 | python-versions = "*" 1090 | 1091 | [package.source] 1092 | type = "legacy" 1093 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1094 | reference = "tsinghua" 1095 | 1096 | [[package]] 1097 | name = "pyyaml" 1098 | version = "6.0" 1099 | description = "YAML parser and emitter for Python" 1100 | category = "main" 1101 | optional = false 1102 | python-versions = ">=3.6" 1103 | 1104 | [package.source] 1105 | type = "legacy" 1106 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1107 | reference = "tsinghua" 1108 | 1109 | [[package]] 1110 | name = "requests" 1111 | version = "2.27.1" 1112 | description = "Python HTTP for Humans." 1113 | category = "main" 1114 | optional = false 1115 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 1116 | 1117 | [package.dependencies] 1118 | certifi = ">=2017.4.17" 1119 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 1120 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 1121 | urllib3 = ">=1.21.1,<1.27" 1122 | 1123 | [package.extras] 1124 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 1125 | use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] 1126 | 1127 | [package.source] 1128 | type = "legacy" 1129 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1130 | reference = "tsinghua" 1131 | 1132 | [[package]] 1133 | name = "requests-oauthlib" 1134 | version = "1.3.1" 1135 | description = "OAuthlib authentication support for Requests." 1136 | category = "main" 1137 | optional = false 1138 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 1139 | 1140 | [package.dependencies] 1141 | oauthlib = ">=3.0.0" 1142 | requests = ">=2.0.0" 1143 | 1144 | [package.extras] 1145 | rsa = ["oauthlib[signedtoken] (>=3.0.0)"] 1146 | 1147 | [package.source] 1148 | type = "legacy" 1149 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1150 | reference = "tsinghua" 1151 | 1152 | [[package]] 1153 | name = "six" 1154 | version = "1.16.0" 1155 | description = "Python 2 and 3 compatibility utilities" 1156 | category = "main" 1157 | optional = false 1158 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 1159 | 1160 | [package.source] 1161 | type = "legacy" 1162 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1163 | reference = "tsinghua" 1164 | 1165 | [[package]] 1166 | name = "social-auth-app-django" 1167 | version = "4.0.0" 1168 | description = "Python Social Authentication, Django integration." 1169 | category = "main" 1170 | optional = false 1171 | python-versions = "*" 1172 | 1173 | [package.dependencies] 1174 | six = "*" 1175 | social-auth-core = ">=3.3.0" 1176 | 1177 | [package.source] 1178 | type = "legacy" 1179 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1180 | reference = "tsinghua" 1181 | 1182 | [[package]] 1183 | name = "social-auth-core" 1184 | version = "4.2.0" 1185 | description = "Python social authentication made simple." 1186 | category = "main" 1187 | optional = false 1188 | python-versions = ">=3.6" 1189 | 1190 | [package.dependencies] 1191 | cryptography = ">=1.4" 1192 | defusedxml = ">=0.5.0rc1" 1193 | oauthlib = ">=1.0.3" 1194 | PyJWT = ">=2.0.0" 1195 | python3-openid = ">=3.0.10" 1196 | requests = ">=2.9.1" 1197 | requests-oauthlib = ">=0.6.1" 1198 | 1199 | [package.extras] 1200 | all = ["python-jose (>=3.0.0)", "python3-saml (>=1.2.1)", "lxml (<4.7)", "cryptography (>=2.1.1)"] 1201 | allpy3 = ["python-jose (>=3.0.0)", "python3-saml (>=1.2.1)", "lxml (<4.7)", "cryptography (>=2.1.1)"] 1202 | azuread = ["cryptography (>=2.1.1)"] 1203 | openidconnect = ["python-jose (>=3.0.0)"] 1204 | saml = ["python3-saml (>=1.2.1)", "lxml (<4.7)"] 1205 | 1206 | [package.source] 1207 | type = "legacy" 1208 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1209 | reference = "tsinghua" 1210 | 1211 | [[package]] 1212 | name = "sqlparse" 1213 | version = "0.4.2" 1214 | description = "A non-validating SQL parser." 1215 | category = "main" 1216 | optional = false 1217 | python-versions = ">=3.5" 1218 | 1219 | [package.source] 1220 | type = "legacy" 1221 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1222 | reference = "tsinghua" 1223 | 1224 | [[package]] 1225 | name = "testfixtures" 1226 | version = "6.18.5" 1227 | description = "A collection of helpers and mock objects for unit tests and doc tests." 1228 | category = "dev" 1229 | optional = false 1230 | python-versions = "*" 1231 | 1232 | [package.extras] 1233 | build = ["setuptools-git", "wheel", "twine"] 1234 | docs = ["sphinx", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] 1235 | test = ["pytest (>=3.6)", "pytest-cov", "pytest-django", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] 1236 | 1237 | [package.source] 1238 | type = "legacy" 1239 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1240 | reference = "tsinghua" 1241 | 1242 | [[package]] 1243 | name = "tomli" 1244 | version = "2.0.1" 1245 | description = "A lil' TOML parser" 1246 | category = "dev" 1247 | optional = false 1248 | python-versions = ">=3.7" 1249 | 1250 | [package.source] 1251 | type = "legacy" 1252 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1253 | reference = "tsinghua" 1254 | 1255 | [[package]] 1256 | name = "typing-extensions" 1257 | version = "4.2.0" 1258 | description = "Backported and Experimental Type Hints for Python 3.7+" 1259 | category = "dev" 1260 | optional = false 1261 | python-versions = ">=3.7" 1262 | 1263 | [package.source] 1264 | type = "legacy" 1265 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1266 | reference = "tsinghua" 1267 | 1268 | [[package]] 1269 | name = "tzdata" 1270 | version = "2022.1" 1271 | description = "Provider of IANA time zone data" 1272 | category = "main" 1273 | optional = false 1274 | python-versions = ">=2" 1275 | 1276 | [package.source] 1277 | type = "legacy" 1278 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1279 | reference = "tsinghua" 1280 | 1281 | [[package]] 1282 | name = "uritemplate" 1283 | version = "4.1.1" 1284 | description = "Implementation of RFC 6570 URI Templates" 1285 | category = "main" 1286 | optional = false 1287 | python-versions = ">=3.6" 1288 | 1289 | [package.source] 1290 | type = "legacy" 1291 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1292 | reference = "tsinghua" 1293 | 1294 | [[package]] 1295 | name = "urllib3" 1296 | version = "1.26.9" 1297 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1298 | category = "main" 1299 | optional = false 1300 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 1301 | 1302 | [package.extras] 1303 | brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] 1304 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 1305 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 1306 | 1307 | [package.source] 1308 | type = "legacy" 1309 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1310 | reference = "tsinghua" 1311 | 1312 | [[package]] 1313 | name = "zipp" 1314 | version = "3.8.0" 1315 | description = "Backport of pathlib-compatible object wrapper for zip files" 1316 | category = "main" 1317 | optional = false 1318 | python-versions = ">=3.7" 1319 | 1320 | [package.extras] 1321 | docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] 1322 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] 1323 | 1324 | [package.source] 1325 | type = "legacy" 1326 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 1327 | reference = "tsinghua" 1328 | 1329 | [metadata] 1330 | lock-version = "1.1" 1331 | python-versions = "^3.8" 1332 | content-hash = "8d4ae1402f6986d46eaf1924c60913d10306102cb092df981c0cf9f2a770ff8d" 1333 | 1334 | [metadata.files] 1335 | asgiref = [ 1336 | {file = "asgiref-3.5.1-py3-none-any.whl", hash = "sha256:45a429524fba18aba9d512498b19d220c4d628e75b40cf5c627524dbaebc5cc1"}, 1337 | {file = "asgiref-3.5.1.tar.gz", hash = "sha256:fddeea3c53fa99d0cdb613c3941cc6e52d822491fc2753fba25768fb5bf4e865"}, 1338 | ] 1339 | atomicwrites = [ 1340 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 1341 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 1342 | ] 1343 | attrs = [ 1344 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 1345 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 1346 | ] 1347 | "backports.zoneinfo" = [ 1348 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, 1349 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, 1350 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, 1351 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, 1352 | {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, 1353 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, 1354 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, 1355 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, 1356 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, 1357 | {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, 1358 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, 1359 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, 1360 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, 1361 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, 1362 | {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, 1363 | {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, 1364 | ] 1365 | black = [ 1366 | {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, 1367 | {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, 1368 | {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, 1369 | {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, 1370 | {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, 1371 | {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, 1372 | {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, 1373 | {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, 1374 | {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, 1375 | {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, 1376 | {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, 1377 | {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, 1378 | {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, 1379 | {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, 1380 | {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, 1381 | {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, 1382 | {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, 1383 | {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, 1384 | {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, 1385 | {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, 1386 | {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, 1387 | {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, 1388 | {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, 1389 | ] 1390 | certifi = [ 1391 | {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, 1392 | {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, 1393 | ] 1394 | cffi = [ 1395 | {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, 1396 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, 1397 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, 1398 | {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, 1399 | {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, 1400 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, 1401 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, 1402 | {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, 1403 | {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, 1404 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, 1405 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, 1406 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, 1407 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, 1408 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, 1409 | {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, 1410 | {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, 1411 | {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, 1412 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, 1413 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, 1414 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, 1415 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, 1416 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, 1417 | {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, 1418 | {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, 1419 | {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, 1420 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, 1421 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, 1422 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, 1423 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, 1424 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, 1425 | {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, 1426 | {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, 1427 | {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, 1428 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, 1429 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, 1430 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, 1431 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, 1432 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, 1433 | {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, 1434 | {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, 1435 | {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, 1436 | {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, 1437 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, 1438 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, 1439 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, 1440 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, 1441 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, 1442 | {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, 1443 | {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, 1444 | {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, 1445 | ] 1446 | charset-normalizer = [ 1447 | {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, 1448 | {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, 1449 | ] 1450 | click = [ 1451 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 1452 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 1453 | ] 1454 | colorama = [ 1455 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 1456 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 1457 | ] 1458 | coreapi = [ 1459 | {file = "coreapi-2.3.3-py2.py3-none-any.whl", hash = "sha256:bf39d118d6d3e171f10df9ede5666f63ad80bba9a29a8ec17726a66cf52ee6f3"}, 1460 | {file = "coreapi-2.3.3.tar.gz", hash = "sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb"}, 1461 | ] 1462 | coreschema = [ 1463 | {file = "coreschema-0.0.4-py2-none-any.whl", hash = "sha256:5e6ef7bf38c1525d5e55a895934ab4273548629f16aed5c0a6caa74ebf45551f"}, 1464 | {file = "coreschema-0.0.4.tar.gz", hash = "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607"}, 1465 | ] 1466 | cryptography = [ 1467 | {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"}, 1468 | {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3c81599befb4d4f3d7648ed3217e00d21a9341a9a688ecdd615ff72ffbed7336"}, 1469 | {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bd1096476aaac820426239ab534b636c77d71af66c547b9ddcd76eb9c79e004"}, 1470 | {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:31fe38d14d2e5f787e0aecef831457da6cec68e0bb09a35835b0b44ae8b988fe"}, 1471 | {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093cb351031656d3ee2f4fa1be579a8c69c754cf874206be1d4cf3b542042804"}, 1472 | {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b281eab51e1b6b6afa525af2bd93c16d49358404f814fe2c2410058623928c"}, 1473 | {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:0cc20f655157d4cfc7bada909dc5cc228211b075ba8407c46467f63597c78178"}, 1474 | {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f8ec91983e638a9bcd75b39f1396e5c0dc2330cbd9ce4accefe68717e6779e0a"}, 1475 | {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:46f4c544f6557a2fefa7ac8ac7d1b17bf9b647bd20b16decc8fbcab7117fbc15"}, 1476 | {file = "cryptography-37.0.2-cp36-abi3-win32.whl", hash = "sha256:731c8abd27693323b348518ed0e0705713a36d79fdbd969ad968fbef0979a7e0"}, 1477 | {file = "cryptography-37.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:471e0d70201c069f74c837983189949aa0d24bb2d751b57e26e3761f2f782b8d"}, 1478 | {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a68254dd88021f24a68b613d8c51d5c5e74d735878b9e32cc0adf19d1f10aaf9"}, 1479 | {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:a7d5137e556cc0ea418dca6186deabe9129cee318618eb1ffecbd35bee55ddc1"}, 1480 | {file = "cryptography-37.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aeaba7b5e756ea52c8861c133c596afe93dd716cbcacae23b80bc238202dc023"}, 1481 | {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e590dd70642eb2079d280420a888190aa040ad20f19ec8c6e097e38aa29e06"}, 1482 | {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1b9362d34363f2c71b7853f6251219298124aa4cc2075ae2932e64c91a3e2717"}, 1483 | {file = "cryptography-37.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e53258e69874a306fcecb88b7534d61820db8a98655662a3dd2ec7f1afd9132f"}, 1484 | {file = "cryptography-37.0.2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:1f3bfbd611db5cb58ca82f3deb35e83af34bb8cf06043fa61500157d50a70982"}, 1485 | {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:419c57d7b63f5ec38b1199a9521d77d7d1754eb97827bbb773162073ccd8c8d4"}, 1486 | {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:dc26bb134452081859aa21d4990474ddb7e863aa39e60d1592800a8865a702de"}, 1487 | {file = "cryptography-37.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b8398b3d0efc420e777c40c16764d6870bcef2eb383df9c6dbb9ffe12c64452"}, 1488 | {file = "cryptography-37.0.2.tar.gz", hash = "sha256:f224ad253cc9cea7568f49077007d2263efa57396a2f2f78114066fd54b5c68e"}, 1489 | ] 1490 | defusedxml = [ 1491 | {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, 1492 | {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, 1493 | ] 1494 | django = [ 1495 | {file = "Django-4.0.4-py3-none-any.whl", hash = "sha256:07c8638e7a7f548dc0acaaa7825d84b7bd42b10e8d22268b3d572946f1e9b687"}, 1496 | {file = "Django-4.0.4.tar.gz", hash = "sha256:4e8177858524417563cc0430f29ea249946d831eacb0068a1455686587df40b5"}, 1497 | ] 1498 | django-appconf = [ 1499 | {file = "django-appconf-1.0.5.tar.gz", hash = "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"}, 1500 | {file = "django_appconf-1.0.5-py3-none-any.whl", hash = "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d"}, 1501 | ] 1502 | django-cors-headers = [ 1503 | {file = "django-cors-headers-3.12.0.tar.gz", hash = "sha256:5f07e2ff8a95c887698e748588a4a0b2ad0ad1b5a292e2d33132f1253e2a97cb"}, 1504 | {file = "django_cors_headers-3.12.0-py3-none-any.whl", hash = "sha256:39d1d5acb872c1860ecfd88b8572bfbb3a1f201b5685ede951d71fc57c7dfae5"}, 1505 | ] 1506 | django-debug-toolbar = [ 1507 | {file = "django-debug-toolbar-3.4.0.tar.gz", hash = "sha256:ae6bec2c1ce0e6900b0ab0443e1427eb233d8e6f57a84a0b2705eeecb8874e22"}, 1508 | {file = "django_debug_toolbar-3.4.0-py3-none-any.whl", hash = "sha256:42c1c2e9dc05bb57b53d641e3a6d131fc031b92377b34ae32e604a1fe439bb83"}, 1509 | ] 1510 | django-environ = [ 1511 | {file = "django-environ-0.8.1.tar.gz", hash = "sha256:6f0bc902b43891656b20486938cba0861dc62892784a44919170719572a534cb"}, 1512 | {file = "django_environ-0.8.1-py2.py3-none-any.whl", hash = "sha256:42593bee519a527602a467c7b682aee1a051c2597f98c45f4f4f44169ecdb6e5"}, 1513 | ] 1514 | django-extensions = [ 1515 | {file = "django-extensions-3.1.5.tar.gz", hash = "sha256:28e1e1bf49f0e00307ba574d645b0af3564c981a6dfc87209d48cb98f77d0b1a"}, 1516 | {file = "django_extensions-3.1.5-py3-none-any.whl", hash = "sha256:9238b9e016bb0009d621e05cf56ea8ce5cce9b32e91ad2026996a7377ca28069"}, 1517 | ] 1518 | django-filter = [ 1519 | {file = "django-filter-21.1.tar.gz", hash = "sha256:632a251fa8f1aadb4b8cceff932bb52fe2f826dd7dfe7f3eac40e5c463d6836e"}, 1520 | {file = "django_filter-21.1-py3-none-any.whl", hash = "sha256:f4a6737a30104c98d2e2a5fb93043f36dd7978e0c7ddc92f5998e85433ea5063"}, 1521 | ] 1522 | django-imagekit = [ 1523 | {file = "django-imagekit-4.1.0.tar.gz", hash = "sha256:e559aeaae43a33b34f87631a9fa5696455e4451ffa738a42635fde442fedac5c"}, 1524 | {file = "django_imagekit-4.1.0-py2.py3-none-any.whl", hash = "sha256:87e36f8dc1d8745647881f4366ef4965225f048042dacebbee0dcb87425defef"}, 1525 | ] 1526 | django-model-utils = [ 1527 | {file = "django-model-utils-4.2.0.tar.gz", hash = "sha256:e7a95e102f9c9653427eadab980d5d59e1dea972913b9c9e01ac37f86bba0ddf"}, 1528 | {file = "django_model_utils-4.2.0-py3-none-any.whl", hash = "sha256:a768a25c80514e0ad4e4a6f9c02c44498985f36c5dfdea47b5b1e8cf994beba6"}, 1529 | ] 1530 | django-templated-mail = [ 1531 | {file = "django-templated-mail-1.1.1.tar.gz", hash = "sha256:8db807effebb42a532622e2d142dfd453dafcd0d7794c4c3332acb90656315f9"}, 1532 | {file = "django_templated_mail-1.1.1-py3-none-any.whl", hash = "sha256:f7127e1e31d7cad4e6c4b4801d25814d4b8782627ead76f4a75b3b7650687556"}, 1533 | ] 1534 | django-test-plus = [ 1535 | {file = "django-test-plus-2.2.0.tar.gz", hash = "sha256:e8826e9715cd52b8bf2a426cda0a6bb900a45aee5b2d7837fc6d2ca81dabee6f"}, 1536 | {file = "django_test_plus-2.2.0-py3-none-any.whl", hash = "sha256:ca7d8b27df04a9d38667c8f7c6e8df9679d765d3931ffb45b94571a24047a0e9"}, 1537 | ] 1538 | djangorestframework = [ 1539 | {file = "djangorestframework-3.13.1-py3-none-any.whl", hash = "sha256:24c4bf58ed7e85d1fe4ba250ab2da926d263cd57d64b03e8dcef0ac683f8b1aa"}, 1540 | {file = "djangorestframework-3.13.1.tar.gz", hash = "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee"}, 1541 | ] 1542 | djangorestframework-simplejwt = [ 1543 | {file = "djangorestframework_simplejwt-4.8.0-py3-none-any.whl", hash = "sha256:6f09f97cb015265e85d1d02dc6bfc299c72c231eecbe261c5bee5c6b2867f2b4"}, 1544 | {file = "djangorestframework_simplejwt-4.8.0.tar.gz", hash = "sha256:153c973c5c154baf566be431de8527c2bd62557fde7373ebcb0f02b73b28e07a"}, 1545 | ] 1546 | djoser = [ 1547 | {file = "djoser-2.1.0-py3-none-any.whl", hash = "sha256:9590378d59eb3243572bcb6b0a45268a3e31bedddc15235ca248a18c7bc0ffe6"}, 1548 | {file = "djoser-2.1.0.tar.gz", hash = "sha256:3299073aa5822f9ad02bc872b87e719051c07d36cdc87a05b2afdb2c3bad46d1"}, 1549 | ] 1550 | drf-extensions = [ 1551 | {file = "drf-extensions-0.7.1.tar.gz", hash = "sha256:90abfc11a2221e8daf4cd54457e41ed38cd71134678de9622e806193db027db1"}, 1552 | {file = "drf_extensions-0.7.1-py2.py3-none-any.whl", hash = "sha256:007910437e64aa1d5ad6fc47266a4ac4280e31761e6458eb30fcac7494ac7d4e"}, 1553 | ] 1554 | drf-spectacular = [ 1555 | {file = "drf-spectacular-0.22.1.tar.gz", hash = "sha256:866e16ddaae167a1234c76cd8c351161373551db994ce9665b347b32d5daf38b"}, 1556 | {file = "drf_spectacular-0.22.1-py3-none-any.whl", hash = "sha256:17ac5e31e5d6150dd5fa10843b429202f4f38069202acc44394cc5a771de63d9"}, 1557 | ] 1558 | factory-boy = [ 1559 | {file = "factory_boy-3.2.1-py2.py3-none-any.whl", hash = "sha256:eb02a7dd1b577ef606b75a253b9818e6f9eaf996d94449c9d5ebb124f90dc795"}, 1560 | {file = "factory_boy-3.2.1.tar.gz", hash = "sha256:a98d277b0c047c75eb6e4ab8508a7f81fb03d2cb21986f627913546ef7a2a55e"}, 1561 | ] 1562 | faker = [ 1563 | {file = "Faker-13.11.1-py3-none-any.whl", hash = "sha256:c6ff91847d7c820afc0a74d95e824b48aab71ddfd9003f300641e42d58ae886f"}, 1564 | {file = "Faker-13.11.1.tar.gz", hash = "sha256:cad1f69d72a68878cd67855140b6fe3e44c11628971cd838595d289c98bc45de"}, 1565 | ] 1566 | flake8 = [ 1567 | {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, 1568 | {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, 1569 | ] 1570 | flake8-isort = [ 1571 | {file = "flake8-isort-4.1.1.tar.gz", hash = "sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717"}, 1572 | {file = "flake8_isort-4.1.1-py3-none-any.whl", hash = "sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949"}, 1573 | ] 1574 | idna = [ 1575 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 1576 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 1577 | ] 1578 | importlib-resources = [ 1579 | {file = "importlib_resources-5.7.1-py3-none-any.whl", hash = "sha256:e447dc01619b1e951286f3929be820029d48c75eb25d265c28b92a16548212b8"}, 1580 | {file = "importlib_resources-5.7.1.tar.gz", hash = "sha256:b6062987dfc51f0fcb809187cffbd60f35df7acb4589091f154214af6d0d49d3"}, 1581 | ] 1582 | inflection = [ 1583 | {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, 1584 | {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, 1585 | ] 1586 | iniconfig = [ 1587 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 1588 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 1589 | ] 1590 | isort = [ 1591 | {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, 1592 | {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, 1593 | ] 1594 | itypes = [ 1595 | {file = "itypes-1.2.0-py2.py3-none-any.whl", hash = "sha256:03da6872ca89d29aef62773672b2d408f490f80db48b23079a4b194c86dd04c6"}, 1596 | {file = "itypes-1.2.0.tar.gz", hash = "sha256:af886f129dea4a2a1e3d36595a2d139589e4dd287f5cab0b40e799ee81570ff1"}, 1597 | ] 1598 | jinja2 = [ 1599 | {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, 1600 | {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, 1601 | ] 1602 | jsonschema = [ 1603 | {file = "jsonschema-4.5.1-py3-none-any.whl", hash = "sha256:71b5e39324422543546572954ce71c67728922c104902cb7ce252e522235b33f"}, 1604 | {file = "jsonschema-4.5.1.tar.gz", hash = "sha256:7c6d882619340c3347a1bf7315e147e6d3dae439033ae6383d6acb908c101dfc"}, 1605 | ] 1606 | markupsafe = [ 1607 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, 1608 | {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, 1609 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, 1610 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, 1611 | {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, 1612 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, 1613 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, 1614 | {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, 1615 | {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, 1616 | {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, 1617 | {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, 1618 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, 1619 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, 1620 | {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, 1621 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, 1622 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, 1623 | {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, 1624 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, 1625 | {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, 1626 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, 1627 | {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, 1628 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, 1629 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, 1630 | {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, 1631 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, 1632 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, 1633 | {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, 1634 | {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, 1635 | {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, 1636 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, 1637 | {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, 1638 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, 1639 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, 1640 | {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, 1641 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, 1642 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, 1643 | {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, 1644 | {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, 1645 | {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, 1646 | {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, 1647 | ] 1648 | mccabe = [ 1649 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 1650 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 1651 | ] 1652 | mypy-extensions = [ 1653 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 1654 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 1655 | ] 1656 | mysqlclient = [ 1657 | {file = "mysqlclient-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:02c8826e6add9b20f4cb12dcf016485f7b1d6e30356a1204d05431867a1b3947"}, 1658 | {file = "mysqlclient-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b62d23c11c516cedb887377c8807628c1c65d57593b57853186a6ee18b0c6a5b"}, 1659 | {file = "mysqlclient-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2c8410f54492a3d2488a6a53e2d85b7e016751a1e7d116e7aea9c763f59f5e8c"}, 1660 | {file = "mysqlclient-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6279263d5a9feca3e0edbc2b2a52c057375bf301d47da2089c075ff76331d14"}, 1661 | {file = "mysqlclient-2.1.0.tar.gz", hash = "sha256:973235686f1b720536d417bf0a0d39b4ab3d5086b2b6ad5e6752393428c02b12"}, 1662 | ] 1663 | oauthlib = [ 1664 | {file = "oauthlib-3.2.0-py3-none-any.whl", hash = "sha256:6db33440354787f9b7f3a6dbd4febf5d0f93758354060e802f6c06cb493022fe"}, 1665 | {file = "oauthlib-3.2.0.tar.gz", hash = "sha256:23a8208d75b902797ea29fd31fa80a15ed9dc2c6c16fe73f5d346f83f6fa27a2"}, 1666 | ] 1667 | packaging = [ 1668 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 1669 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 1670 | ] 1671 | pathspec = [ 1672 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 1673 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 1674 | ] 1675 | pilkit = [ 1676 | {file = "pilkit-2.0.tar.gz", hash = "sha256:ddb30c2f0198a147e56b151476c3bb9fe045fbfd5b0a0fa2a3148dba62d1559f"}, 1677 | ] 1678 | pillow = [ 1679 | {file = "Pillow-9.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:af79d3fde1fc2e33561166d62e3b63f0cc3e47b5a3a2e5fea40d4917754734ea"}, 1680 | {file = "Pillow-9.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55dd1cf09a1fd7c7b78425967aacae9b0d70125f7d3ab973fadc7b5abc3de652"}, 1681 | {file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66822d01e82506a19407d1afc104c3fcea3b81d5eb11485e593ad6b8492f995a"}, 1682 | {file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5eaf3b42df2bcda61c53a742ee2c6e63f777d0e085bbc6b2ab7ed57deb13db7"}, 1683 | {file = "Pillow-9.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01ce45deec9df310cbbee11104bae1a2a43308dd9c317f99235b6d3080ddd66e"}, 1684 | {file = "Pillow-9.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aea7ce61328e15943d7b9eaca87e81f7c62ff90f669116f857262e9da4057ba3"}, 1685 | {file = "Pillow-9.1.0-cp310-cp310-win32.whl", hash = "sha256:7a053bd4d65a3294b153bdd7724dce864a1d548416a5ef61f6d03bf149205160"}, 1686 | {file = "Pillow-9.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:97bda660702a856c2c9e12ec26fc6d187631ddfd896ff685814ab21ef0597033"}, 1687 | {file = "Pillow-9.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21dee8466b42912335151d24c1665fcf44dc2ee47e021d233a40c3ca5adae59c"}, 1688 | {file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b6d4050b208c8ff886fd3db6690bf04f9a48749d78b41b7a5bf24c236ab0165"}, 1689 | {file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5cfca31ab4c13552a0f354c87fbd7f162a4fafd25e6b521bba93a57fe6a3700a"}, 1690 | {file = "Pillow-9.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed742214068efa95e9844c2d9129e209ed63f61baa4d54dbf4cf8b5e2d30ccf2"}, 1691 | {file = "Pillow-9.1.0-cp37-cp37m-win32.whl", hash = "sha256:c9efef876c21788366ea1f50ecb39d5d6f65febe25ad1d4c0b8dff98843ac244"}, 1692 | {file = "Pillow-9.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:de344bcf6e2463bb25179d74d6e7989e375f906bcec8cb86edb8b12acbc7dfef"}, 1693 | {file = "Pillow-9.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:17869489de2fce6c36690a0c721bd3db176194af5f39249c1ac56d0bb0fcc512"}, 1694 | {file = "Pillow-9.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:25023a6209a4d7c42154073144608c9a71d3512b648a2f5d4465182cb93d3477"}, 1695 | {file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8782189c796eff29dbb37dd87afa4ad4d40fc90b2742704f94812851b725964b"}, 1696 | {file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:463acf531f5d0925ca55904fa668bb3461c3ef6bc779e1d6d8a488092bdee378"}, 1697 | {file = "Pillow-9.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f42364485bfdab19c1373b5cd62f7c5ab7cc052e19644862ec8f15bb8af289e"}, 1698 | {file = "Pillow-9.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3fddcdb619ba04491e8f771636583a7cc5a5051cd193ff1aa1ee8616d2a692c5"}, 1699 | {file = "Pillow-9.1.0-cp38-cp38-win32.whl", hash = "sha256:4fe29a070de394e449fd88ebe1624d1e2d7ddeed4c12e0b31624561b58948d9a"}, 1700 | {file = "Pillow-9.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:c24f718f9dd73bb2b31a6201e6db5ea4a61fdd1d1c200f43ee585fc6dcd21b34"}, 1701 | {file = "Pillow-9.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fb89397013cf302f282f0fc998bb7abf11d49dcff72c8ecb320f76ea6e2c5717"}, 1702 | {file = "Pillow-9.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c870193cce4b76713a2b29be5d8327c8ccbe0d4a49bc22968aa1e680930f5581"}, 1703 | {file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69e5ddc609230d4408277af135c5b5c8fe7a54b2bdb8ad7c5100b86b3aab04c6"}, 1704 | {file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35be4a9f65441d9982240e6966c1eaa1c654c4e5e931eaf580130409e31804d4"}, 1705 | {file = "Pillow-9.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82283af99c1c3a5ba1da44c67296d5aad19f11c535b551a5ae55328a317ce331"}, 1706 | {file = "Pillow-9.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a325ac71914c5c043fa50441b36606e64a10cd262de12f7a179620f579752ff8"}, 1707 | {file = "Pillow-9.1.0-cp39-cp39-win32.whl", hash = "sha256:a598d8830f6ef5501002ae85c7dbfcd9c27cc4efc02a1989369303ba85573e58"}, 1708 | {file = "Pillow-9.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0c51cb9edac8a5abd069fd0758ac0a8bfe52c261ee0e330f363548aca6893595"}, 1709 | {file = "Pillow-9.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a336a4f74baf67e26f3acc4d61c913e378e931817cd1e2ef4dfb79d3e051b481"}, 1710 | {file = "Pillow-9.1.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb1b89b11256b5b6cad5e7593f9061ac4624f7651f7a8eb4dfa37caa1dfaa4d0"}, 1711 | {file = "Pillow-9.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:255c9d69754a4c90b0ee484967fc8818c7ff8311c6dddcc43a4340e10cd1636a"}, 1712 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5a3ecc026ea0e14d0ad7cd990ea7f48bfcb3eb4271034657dc9d06933c6629a7"}, 1713 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5b0ff59785d93b3437c3703e3c64c178aabada51dea2a7f2c5eccf1bcf565a3"}, 1714 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7110ec1701b0bf8df569a7592a196c9d07c764a0a74f65471ea56816f10e2c8"}, 1715 | {file = "Pillow-9.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d79c6f468215d1a8415aa53d9868a6b40c4682165b8cb62a221b1baa47db458"}, 1716 | {file = "Pillow-9.1.0.tar.gz", hash = "sha256:f401ed2bbb155e1ade150ccc63db1a4f6c1909d3d378f7d1235a44e90d75fb97"}, 1717 | ] 1718 | platformdirs = [ 1719 | {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, 1720 | {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, 1721 | ] 1722 | pluggy = [ 1723 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 1724 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 1725 | ] 1726 | py = [ 1727 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 1728 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 1729 | ] 1730 | pycodestyle = [ 1731 | {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, 1732 | {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, 1733 | ] 1734 | pycparser = [ 1735 | {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, 1736 | {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, 1737 | ] 1738 | pyflakes = [ 1739 | {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, 1740 | {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, 1741 | ] 1742 | pyjwt = [ 1743 | {file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"}, 1744 | {file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"}, 1745 | ] 1746 | pyparsing = [ 1747 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, 1748 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, 1749 | ] 1750 | pyrsistent = [ 1751 | {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, 1752 | {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, 1753 | {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, 1754 | {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"}, 1755 | {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"}, 1756 | {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"}, 1757 | {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"}, 1758 | {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"}, 1759 | {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"}, 1760 | {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"}, 1761 | {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"}, 1762 | {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"}, 1763 | {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"}, 1764 | {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"}, 1765 | {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"}, 1766 | {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"}, 1767 | {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"}, 1768 | {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"}, 1769 | {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"}, 1770 | {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, 1771 | {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, 1772 | ] 1773 | pytest = [ 1774 | {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, 1775 | {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, 1776 | ] 1777 | pytest-django = [ 1778 | {file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"}, 1779 | {file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"}, 1780 | ] 1781 | python-dateutil = [ 1782 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 1783 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 1784 | ] 1785 | python3-openid = [ 1786 | {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"}, 1787 | {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"}, 1788 | ] 1789 | pytz = [ 1790 | {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, 1791 | {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, 1792 | ] 1793 | pyyaml = [ 1794 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 1795 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 1796 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 1797 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 1798 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, 1799 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 1800 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 1801 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 1802 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 1803 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 1804 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, 1805 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 1806 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 1807 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 1808 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 1809 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 1810 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, 1811 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 1812 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 1813 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 1814 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 1815 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 1816 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, 1817 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 1818 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 1819 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 1820 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 1821 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 1822 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 1823 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, 1824 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 1825 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 1826 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 1827 | ] 1828 | requests = [ 1829 | {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, 1830 | {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, 1831 | ] 1832 | requests-oauthlib = [ 1833 | {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, 1834 | {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, 1835 | ] 1836 | six = [ 1837 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1838 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1839 | ] 1840 | social-auth-app-django = [ 1841 | {file = "social-auth-app-django-4.0.0.tar.gz", hash = "sha256:2c69e57df0b30c9c1823519c5f1992cbe4f3f98fdc7d95c840e091a752708840"}, 1842 | {file = "social_auth_app_django-4.0.0-py2-none-any.whl", hash = "sha256:df5212370bd250108987c4748419a1a1d0cec750878856c2644c36aaa0fd3e58"}, 1843 | {file = "social_auth_app_django-4.0.0-py3-none-any.whl", hash = "sha256:567ad0e028311541d7dfed51d3bf2c60440a6fd236d5d4d06c5a618b3d6c57c5"}, 1844 | ] 1845 | social-auth-core = [ 1846 | {file = "social-auth-core-4.2.0.tar.gz", hash = "sha256:af6fc7d9ee39f0aa697cd953121add638fc32a49816341a9838a67904c39e034"}, 1847 | {file = "social_auth_core-4.2.0-py3-none-any.whl", hash = "sha256:08fe6645b98950cff9d6970b6704345f821d78729df0164bca0002c624b001e1"}, 1848 | ] 1849 | sqlparse = [ 1850 | {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, 1851 | {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, 1852 | ] 1853 | testfixtures = [ 1854 | {file = "testfixtures-6.18.5-py2.py3-none-any.whl", hash = "sha256:7de200e24f50a4a5d6da7019fb1197aaf5abd475efb2ec2422fdcf2f2eb98c1d"}, 1855 | {file = "testfixtures-6.18.5.tar.gz", hash = "sha256:02dae883f567f5b70fd3ad3c9eefb95912e78ac90be6c7444b5e2f46bf572c84"}, 1856 | ] 1857 | tomli = [ 1858 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 1859 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 1860 | ] 1861 | typing-extensions = [ 1862 | {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, 1863 | {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, 1864 | ] 1865 | tzdata = [ 1866 | {file = "tzdata-2022.1-py2.py3-none-any.whl", hash = "sha256:238e70234214138ed7b4e8a0fab0e5e13872edab3be586ab8198c407620e2ab9"}, 1867 | {file = "tzdata-2022.1.tar.gz", hash = "sha256:8b536a8ec63dc0751342b3984193a3118f8fca2afe25752bb9b7fffd398552d3"}, 1868 | ] 1869 | uritemplate = [ 1870 | {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, 1871 | {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, 1872 | ] 1873 | urllib3 = [ 1874 | {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, 1875 | {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, 1876 | ] 1877 | zipp = [ 1878 | {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, 1879 | {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, 1880 | ] 1881 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "epidemic" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["jkc"] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.8" 9 | Django = "^4.0.4" 10 | django-environ = "^0.8.1" 11 | djoser = "^2.1.0" 12 | drf-spectacular = "^0.22.1" 13 | drf-extensions = "^0.7.1" 14 | django-extensions = "^3.1.5" 15 | django-model-utils = "^4.2.0" 16 | django-imagekit = "^4.1.0" 17 | Pillow = "^9.1.0" 18 | factory-boy = "^3.2.1" 19 | mysqlclient = "^2.1.0" 20 | django-cors-headers = "^3.12.0" 21 | djangorestframework = "^3.13.1" 22 | django-filter = "^21.1" 23 | 24 | [tool.poetry.dev-dependencies] 25 | django-debug-toolbar = "^3.4.0" 26 | isort = "^5.10.1" 27 | black = "^22.3.0" 28 | flake8 = "^4.0.1" 29 | flake8-isort = "^4.1.1" 30 | pytest-django = "^4.5.2" 31 | django-test-plus = "^2.2.0" 32 | pytest = "^7.1.2" 33 | 34 | [build-system] 35 | requires = ["poetry-core>=1.0.0"] 36 | build-backend = "poetry.core.masonry.api" 37 | 38 | [[tool.poetry.source]] 39 | name = "tsinghua" 40 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 41 | 42 | [tool.isort] 43 | profile = "black" 44 | skip = ["migrations"] 45 | 46 | [tool.black] 47 | line-length = 120 48 | target-version = ['py38'] 49 | include = '\.pyi?$' 50 | exclude = ''' 51 | ( 52 | /( 53 | \.eggs # exclude a few common directories in the 54 | | \.git # root of the project 55 | | \.hg 56 | | \.mypy_cache 57 | | \.tox 58 | | \.venv 59 | | _build 60 | | buck-out 61 | | build 62 | | dist 63 | | migrations 64 | )/ 65 | ) 66 | ''' 67 | -------------------------------------------------------------------------------- /screenshots/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/screenshots/data.png -------------------------------------------------------------------------------- /screenshots/echarts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/screenshots/echarts.png -------------------------------------------------------------------------------- /screenshots/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/screenshots/index.png -------------------------------------------------------------------------------- /screenshots/inspection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/screenshots/inspection.png -------------------------------------------------------------------------------- /screenshots/users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s649821022/epidemic/86bd1a315668f6899da0bec10f9d1dabb42852d8/screenshots/users.png -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | extend-ignore = E203 4 | exclude = 5 | .tox, 6 | .git, 7 | */migrations/* 8 | --------------------------------------------------------------------------------