From 3d059b5306130e7e9f66f28e9573a82239c66198 Mon Sep 17 00:00:00 2001 From: xfc Date: Fri, 7 Jul 2023 11:18:53 +0800 Subject: [PATCH] API2.0_init --- Dockerfile | 3 +- TP_API/__init__.py | 2 + TP_API/settings.py | 55 ++-- TP_API/urls.py | 3 +- TP后台接口文档.md | 73 ++++-- app/migrations/0001_initial.py | 40 +++ app/migrations/0002_alter_tp_video_hash.py | 18 ++ app/migrations/0003_alter_tp_video_hash.py | 18 ++ app/migrations/0004_auto_20230703_1718.py | 26 ++ app/migrations/0005_auto_20230703_1727.py | 23 ++ app/migrations/0006_auto_20230704_1105.py | 23 ++ app/models.py | 16 +- app/serializers.py | 35 ++- app/urls.py | 5 +- app/utils.py | 31 +++ app/views.py | 86 ++++++- event/__init__.py | 0 event/admin.py | 5 + event/apps.py | 6 + event/filters.py | 14 ++ event/migrations/0001_initial.py | 27 ++ event/migrations/__init__.py | 0 event/models.py | 13 + event/serializers.py | 8 + event/tests.py | 3 + event/urls.py | 10 + event/views.py | 23 ++ logs/.__debug.lock | 0 mymiddleware/middleware.py | 30 +++ requirements.txt | Bin 700 -> 728 bytes user/__init__.py | 0 user/admin.py | 3 + user/apps.py | 6 + user/migrations/0001_initial.py | 48 ++++ user/migrations/__init__.py | 0 user/models.py | 20 ++ user/tests.py | 278 +++++++++++++++++++++ user/views.py | 3 + uwsgi.ini | 6 +- 39 files changed, 909 insertions(+), 51 deletions(-) create mode 100644 app/migrations/0001_initial.py create mode 100644 app/migrations/0002_alter_tp_video_hash.py create mode 100644 app/migrations/0003_alter_tp_video_hash.py create mode 100644 app/migrations/0004_auto_20230703_1718.py create mode 100644 app/migrations/0005_auto_20230703_1727.py create mode 100644 app/migrations/0006_auto_20230704_1105.py create mode 100644 app/utils.py create mode 100644 event/__init__.py create mode 100644 event/admin.py create mode 100644 event/apps.py create mode 100644 event/filters.py create mode 100644 event/migrations/0001_initial.py create mode 100644 event/migrations/__init__.py create mode 100644 event/models.py create mode 100644 event/serializers.py create mode 100644 event/tests.py create mode 100644 event/urls.py create mode 100644 event/views.py create mode 100644 logs/.__debug.lock create mode 100644 mymiddleware/middleware.py create mode 100644 user/__init__.py create mode 100644 user/admin.py create mode 100644 user/apps.py create mode 100644 user/migrations/0001_initial.py create mode 100644 user/migrations/__init__.py create mode 100644 user/models.py create mode 100644 user/tests.py create mode 100644 user/views.py diff --git a/Dockerfile b/Dockerfile index 58435c5..f5338cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM python:3.7 # 安装netcat -RUN apt-get update && apt install -y netcat +RUN apt-get update # 可选:设置镜像源为国内 COPY pip.conf /root/.pip/pip.conf @@ -26,6 +26,7 @@ RUN sed -i 's/\r//' ./start.sh # 给start.sh可执行权限 RUN chmod +x ./start.sh +RUN chmod 777 -R /home/myproject/logs/ EXPOSE 8000 diff --git a/TP_API/__init__.py b/TP_API/__init__.py index e69de29..063cd2c 100644 --- a/TP_API/__init__.py +++ b/TP_API/__init__.py @@ -0,0 +1,2 @@ +import pymysql +pymysql.install_as_MySQLdb() diff --git a/TP_API/settings.py b/TP_API/settings.py index 3aba9b6..a7e7cb2 100644 --- a/TP_API/settings.py +++ b/TP_API/settings.py @@ -42,6 +42,8 @@ INSTALLED_APPS = [ 'django_filters', 'corsheaders', 'app', + 'event', + 'user', ] MIDDLEWARE = [ @@ -53,6 +55,7 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'mymiddleware.middleware.CheckTokenMiddleware', ] ROOT_URLCONF = 'TP_API.urls' @@ -79,10 +82,21 @@ WSGI_APPLICATION = 'TP_API.wsgi.application' # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': BASE_DIR / 'db.sqlite3', +# } +# } + DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'tp_test', + 'USER': 'root', + 'PASSWORD': 'Xfc980516', + 'HOST': '127.0.0.1', + 'PORT': '3306', } } @@ -138,7 +152,7 @@ CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_HEADERS = ["*"] -CORS_ORIGIN_WHITELIST = () +# CORS_ORIGIN_WHITELIST = ('*',) # 对应的发送的请求的跨域 CORS_ALLOW_METHODS = ( 'DELETE', @@ -159,6 +173,15 @@ REST_FRAMEWORK = { 'DATE_FORMAT': "%Y-%m-%d", 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], 'EXCEPTION_HANDLER': 'app.exception.exception_handler', + # 'DEFAULT_AUTHENTICATION_CLASSES': ( + # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 第一种jwt方式 + # # 'rest_framework.authentication.SessionAuthentication', # 第二种session方式 + # # 'rest_framework.authentication.BasicAuthentication', # 第三种Django的基本方式 + # ), + # + # 'DEFAULT_PERMISSION_CLASSES': ( + # 'rest_framework.permissions.IsAuthenticated', + # ), } # 配置日志 @@ -169,7 +192,7 @@ LOGGING = { 'formatters': { # 定义日志的格式 'standard': { 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' - '[%(levelname)s][%(message)s]' + '[%(levelname)s]%(message)s' }, 'simple': { 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' @@ -191,24 +214,24 @@ LOGGING = { 'formatter': 'simple', # 'filename': os.path.join(BASE_LOG_DIR, "tpservice.log") }, - # 'file': { - # 'level': 'INFO', - # 'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler', # window多进程 需要 pip install concurrent-log-handler - # 'filename': os.path.join(BASE_DIR, "logs/debug.log"), # 日志文件 - # 'backupCount': 10, # 保留的最大文件数,超过则删除日期最早的 - # 'maxBytes': 1024, # 文件大小 - # 'formatter': 'standard', - # 'encoding': 'utf-8', - # }, 'file': { 'level': 'INFO', - 'class': 'cloghandler.ConcurrentRotatingFileHandler', # linux多进程 需要 pip install ConcurrentLogHandler + 'class': 'concurrent_log_handler.ConcurrentRotatingFileHandler', # window多进程 需要 pip install concurrent-log-handler 'filename': os.path.join(BASE_DIR, "logs/debug.log"), # 日志文件 'backupCount': 10, # 保留的最大文件数,超过则删除日期最早的 - 'maxBytes': 1024, # 文件大小 + 'maxBytes': 1024 * 1024 * 10, # 文件大小 'formatter': 'standard', 'encoding': 'utf-8', }, + # 'file': { + # 'level': 'INFO', + # 'class': 'cloghandler.ConcurrentRotatingFileHandler', # linux多进程 需要 pip install ConcurrentLogHandler + # 'filename': os.path.join(BASE_DIR, "logs/debug.log"), # 日志文件 + # 'backupCount': 10, # 保留的最大文件数,超过则删除日期最早的 + # 'maxBytes': 1024 * 1024 * 10, # 文件大小 + # 'formatter': 'standard', + # 'encoding': 'utf-8', + # }, }, 'loggers': { # 日志实例 记录器 'mylogger': { # 默认的logger应用如下配置 @@ -218,3 +241,5 @@ LOGGING = { }, }, } + +AUTH_USER_MODEL = 'user.UserProfile' diff --git a/TP_API/urls.py b/TP_API/urls.py index 6c2d041..240ec51 100644 --- a/TP_API/urls.py +++ b/TP_API/urls.py @@ -23,7 +23,8 @@ from .settings import MEDIA_ROOT urlpatterns = [ path('admin/', admin.site.urls), - path('api/', include('app.urls')) + path('api/', include('app.urls')), + path('event/', include('event.urls')), ] diff --git a/TP后台接口文档.md b/TP后台接口文档.md index 0ee9fc8..a334e90 100644 --- a/TP后台接口文档.md +++ b/TP后台接口文档.md @@ -29,23 +29,24 @@ - 返回值: - | 参数名 | 参数值 | 参数类型 | 描述说明 | - | ------------- | ------------------------------------------------- | -------- | ------------------------ | - | uid | 1 | int | 自增 | - | video_hash | 38fb463b135fa12534104f85492cc6f1 | string | 视频哈希值 | - | record_time | 2023-05-26 13:09:05 | string | 记录仪时间 | - | police_id | 00000001 | string | 警号 | - | event_type | 1 | string | 事件类型 | - | is_violation | true | bool | 是否违规 | - | small_image | http://192.168.0.47:8000/media/images/0000609.jpg | string | 缩略图 | - | relative_time | 4.0 | float | 相对时间 | - | video_dir | http://192.168.0.47:8000/media/video/B1.MP4 | string | 视频地址 | - | car_number | 苏a045689 | string | 车牌号 | - | ai_analysis | 违规 | string | 分析结果 | - | add_time | 2023-05-31 18:42:15 | string | 记录添加时间(自动添加) | - | update_time | 2023-05-31 18:42:15 | string | 记录更新时间(自动添加) | - | is_display | true | bool | 是否展示(自动添加) | - + | 参数名 | 参数值 | 参数类型 | 描述说明 | + | ---------------- | ------------------------------------------------- | -------- |--------------| + | uid | 1 | int | 自增 | + | video_hash | 38fb463b135fa12534104f85492cc6f1 | string | 视频哈希值 | + | record_time | 2023-05-26 13:09:05 | string | 记录仪时间 | + | police_id | 00000001 | string | 警号 | + | event_type | 1 | string | 事件类型/车辆违法原因 | + | is_violation | true | bool | 执法人员是否违规 | + | small_image | http://192.168.0.47:8000/media/images/0000609.jpg | string | 缩略图 | + | relative_time | 4.0 | float | 相对时间 | + | video_dir | http://192.168.0.47:8000/media/video/B1.MP4 | string | 视频地址 | + | car_number | 苏a045689 | string | 车牌号 | + | ai_analysis | 违规 | string | 执法人员违规行为 | + | add_time | 2023-05-31 18:42:15 | string | 记录添加时间(自动添加) | + | update_time | 2023-05-31 18:42:15 | string | 记录更新时间(自动添加) | + | is_display | true | bool | 是否展示(自动添加) | + | is_illegal | True | bool | 行人是否违法 | + @@ -238,3 +239,41 @@ + +
+ +登录 + +- 请求方式:POST + +- 请求链接:http://192.168.10.13:8000/api/login + +- 请求body: + + ```json + { + "username": "xfc", + "password": "Xfc980516" + } + ``` + | 参数名 | 参数值 | 是否必填 | 参数类型 | 描述说明 | + |----------|------------| -------- |--------|------| + | username | xfc | 是 | string | 用户名 | + | password | Xfc980516 | 是 | string | 密码 | + +- 备注:无 + +- 返回值: + + | 参数名 | 参数值 | 参数类型 | 描述说明 | + |-------------|-----------------------------------------|--------|--------------| + | success | True | bool | 成功 | + | msg | 登录成功 | string | 返回信息 | + | data | | dict | 返回数据 | + | username | xfc | string | 用户名 | + | roles | [] | list | 角色列表 | + | accessToken | eyJhbGciOiJIUzI1NiIsInR5cCI6 | string | token值 | + | expires | Wed Jul 5 16:03:31 2023 | string | token过期时间 | + + +
\ No newline at end of file diff --git a/app/migrations/0001_initial.py b/app/migrations/0001_initial.py new file mode 100644 index 0000000..77d01fe --- /dev/null +++ b/app/migrations/0001_initial.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.19 on 2023-06-30 16:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='TP', + fields=[ + ('uid', models.AutoField(primary_key=True, serialize=False)), + ('video_hash', models.CharField(max_length=50, verbose_name='视频哈希')), + ('record_time', models.DateTimeField(verbose_name='记录仪时间')), + ('police_id', models.CharField(blank=True, max_length=50, null=True, verbose_name='警号')), + ('event_type', models.CharField(max_length=50, verbose_name='事件类型/车辆违法原因')), + ('is_violation', models.BooleanField(default=False, verbose_name='执法人员是否违规')), + ('small_image', models.CharField(max_length=100, verbose_name='缩略图')), + ('relative_time', models.FloatField(verbose_name='相对时间')), + ('video_dir', models.CharField(max_length=100, verbose_name='视频路径')), + ('car_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='车牌号')), + ('ai_analysis', models.CharField(blank=True, max_length=255, null=True, verbose_name='分析结果')), + ('add_time', models.DateTimeField(auto_now_add=True, verbose_name='加入时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ('is_display', models.BooleanField(default=True, verbose_name='是否显示')), + ('vehicle_color', models.CharField(blank=True, max_length=256, null=True, verbose_name='车辆颜色')), + ('violation_reason', models.CharField(default='无', max_length=512, verbose_name='执法人员违规原因')), + ('is_illegal', models.BooleanField(default=False, verbose_name='车辆是否违法')), + ], + options={ + 'db_table': 'app_tp', + 'ordering': ['-uid'], + }, + ), + ] diff --git a/app/migrations/0002_alter_tp_video_hash.py b/app/migrations/0002_alter_tp_video_hash.py new file mode 100644 index 0000000..9c69326 --- /dev/null +++ b/app/migrations/0002_alter_tp_video_hash.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2023-06-30 16:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='tp', + name='video_hash', + field=models.CharField(default='', max_length=50, verbose_name='视频哈希'), + ), + ] diff --git a/app/migrations/0003_alter_tp_video_hash.py b/app/migrations/0003_alter_tp_video_hash.py new file mode 100644 index 0000000..6d926c4 --- /dev/null +++ b/app/migrations/0003_alter_tp_video_hash.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.19 on 2023-06-30 16:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0002_alter_tp_video_hash'), + ] + + operations = [ + migrations.AlterField( + model_name='tp', + name='video_hash', + field=models.CharField(max_length=50, verbose_name='视频哈希'), + ), + ] diff --git a/app/migrations/0004_auto_20230703_1718.py b/app/migrations/0004_auto_20230703_1718.py new file mode 100644 index 0000000..e104322 --- /dev/null +++ b/app/migrations/0004_auto_20230703_1718.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.19 on 2023-07-03 17:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0003_alter_tp_video_hash'), + ] + + operations = [ + migrations.RemoveField( + model_name='tp', + name='vehicle_color', + ), + migrations.RemoveField( + model_name='tp', + name='violation_reason', + ), + migrations.AlterField( + model_name='tp', + name='ai_analysis', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='执法人员违规行为'), + ), + ] diff --git a/app/migrations/0005_auto_20230703_1727.py b/app/migrations/0005_auto_20230703_1727.py new file mode 100644 index 0000000..64c229f --- /dev/null +++ b/app/migrations/0005_auto_20230703_1727.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.19 on 2023-07-03 17:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0004_auto_20230703_1718'), + ] + + operations = [ + migrations.AlterField( + model_name='tp', + name='is_illegal', + field=models.BooleanField(blank=True, null=True, verbose_name='车辆是否违法'), + ), + migrations.AlterField( + model_name='tp', + name='is_violation', + field=models.BooleanField(blank=True, null=True, verbose_name='执法人员是否违规'), + ), + ] diff --git a/app/migrations/0006_auto_20230704_1105.py b/app/migrations/0006_auto_20230704_1105.py new file mode 100644 index 0000000..3eac640 --- /dev/null +++ b/app/migrations/0006_auto_20230704_1105.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.19 on 2023-07-04 11:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0005_auto_20230703_1727'), + ] + + operations = [ + migrations.AlterField( + model_name='tp', + name='event_type', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='事件类型/车辆违法原因'), + ), + migrations.AlterField( + model_name='tp', + name='video_hash', + field=models.CharField(max_length=1024, verbose_name='视频哈希'), + ), + ] diff --git a/app/models.py b/app/models.py index e280453..8d654b1 100644 --- a/app/models.py +++ b/app/models.py @@ -1,19 +1,21 @@ from django.db import models # Create your models here. + + class TP(models.Model): # uid uid = models.AutoField(primary_key=True) # 视频哈希 - video_hash = models.CharField(max_length=50, verbose_name='视频哈希') + video_hash = models.CharField(max_length=1024, verbose_name='视频哈希') # 记录仪时间 record_time = models.DateTimeField(verbose_name='记录仪时间') # 警号 police_id = models.CharField(max_length=50, null=True, blank=True, verbose_name='警号') # 警号可为空可不传 # 事件类型 - event_type = models.CharField(max_length=50, verbose_name='事件类型') + event_type = models.CharField(max_length=50, verbose_name='事件类型/车辆违法原因', null=True, blank=True) # 还没迁移 # 是否违规 - is_violation = models.BooleanField(verbose_name='是否违规') + is_violation = models.BooleanField(verbose_name='执法人员是否违规', null=True, blank=True) # 缩略图 small_image = models.CharField(max_length=100, verbose_name='缩略图') # 相对时间 @@ -23,7 +25,7 @@ class TP(models.Model): # 车牌号 car_number = models.CharField(max_length=50, verbose_name='车牌号', null=True, blank=True) # 车牌可为空可不传 # 分析结果 - ai_analysis = models.CharField(max_length=255, verbose_name='分析结果', null=True, blank=True) # 分析结果可为空可不传 + ai_analysis = models.CharField(max_length=255, verbose_name='执法人员违规行为', null=True, blank=True) # 分析结果可为空可不传 # 加入时间 add_time = models.DateTimeField(auto_now_add=True, verbose_name='加入时间') # 更新时间 @@ -31,12 +33,10 @@ class TP(models.Model): # 是否显示 is_display = models.BooleanField(default=True, verbose_name='是否显示') + is_illegal = models.BooleanField(verbose_name='车辆是否违法', null=True, blank=True) + class Meta: db_table = "app_tp" # 排序 uid倒序 ordering = ['-uid'] - - - - diff --git a/app/serializers.py b/app/serializers.py index 78bf8f4..fabae95 100644 --- a/app/serializers.py +++ b/app/serializers.py @@ -1,9 +1,11 @@ +import datetime + from rest_framework import serializers from django_filters.rest_framework import FilterSet import django_filters from app.models import TP - - +import logging +logger = logging.getLogger('mylogger') class SerialMyModel(serializers.ModelSerializer): class Meta: model = TP @@ -22,14 +24,35 @@ class SerialFilter(FilterSet): event_type = django_filters.CharFilter(field_name='event_type', lookup_expr='icontains') # 记录时间范围查询 start_time = django_filters.DateTimeFilter(field_name='record_time', lookup_expr='gte') - end_time = django_filters.DateTimeFilter(field_name='record_time', lookup_expr='lte') + # end_time = django_filters.DateTimeFilter(field_name='record_time', lookup_expr='lte') + end_time = django_filters.DateTimeFilter(field_name='record_time', method='time_range_filter') + # todo bug: start_time 和 end_time同一天的情况 done + violation = django_filters.NumberFilter(field_name='is_violation', method='is_violation_query') + violation_type = django_filters.CharFilter(field_name='ai_analysis', lookup_expr='icontains') + + def time_range_filter(self, queryset, name, value): + """ + @params queryset: 为TP.objects.all()返回的合集, 即视图类中的的queryset + @params name: 为field_name + @params value: 为前端通过end_time字段传过来的值 + """ + + return queryset.filter(record_time__lte=value + datetime.timedelta(days=1)) + + def is_violation_query(self, queryset, name, value): + if value == 2: + return queryset + elif value == 1: + return queryset.filter(is_violation=True) + elif value == 0: + return queryset.filter(is_violation=False) + else: + return queryset.filter(is_violation=True) class Meta: # 指定模型 models = TP # 指定需要模糊查询的字段 - fields = ("record_time", "police_id", "event_type",) - - + fields = ("record_time", "police_id", "event_type", "ai_analysis",) diff --git a/app/urls.py b/app/urls.py index 1e43fe6..e519fbe 100644 --- a/app/urls.py +++ b/app/urls.py @@ -14,7 +14,7 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ # from django.contrib import admin -from django.urls import path, include +from django.urls import path, include, re_path from rest_framework.routers import DefaultRouter from app import views @@ -23,4 +23,7 @@ router.register('', views.ModelQuery) urlpatterns = [ path('', include(router.urls)), + path('add_user', views.RegisterLoginViewSet.as_view({"post": "tp_register"})), + path('login', views.LoginView.as_view({"post": "tp_login"})), + path('events', views.ModelQuery.as_view({"get": "query_event"})), ] diff --git a/app/utils.py b/app/utils.py new file mode 100644 index 0000000..3fe31f4 --- /dev/null +++ b/app/utils.py @@ -0,0 +1,31 @@ +import time +import datetime +import jwt +from django.conf import settings +from rest_framework.response import Response +from django.contrib.auth.models import User + + +def generate_token(user): + """ + :param user: 用户对象 + :return: 生成的token + """ + payload = { + 'user_id': user.id, + 'username': user.username, + 'exp': datetime.datetime.utcnow() + datetime.timedelta(weeks=1) # token过期时间 1week + } + token = jwt.encode(payload=payload, key=settings.SECRET_KEY, algorithm='HS256') + return token + + +def decode_token_exp_time(token): + try: + res_dict = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256']) + exp = res_dict.get('exp') + exp_time = time.ctime(exp) + return exp_time + except Exception as e: + return None + diff --git a/app/views.py b/app/views.py index 7b3c4d8..b6a33ca 100644 --- a/app/views.py +++ b/app/views.py @@ -1,4 +1,8 @@ +import logging + +from django.db.models import Count from django.shortcuts import render +from django.views.decorators.http import require_http_methods # Create your views here. from rest_framework import viewsets @@ -6,11 +10,16 @@ from rest_framework.response import Response from app.models import TP from app.serializers import SerialMyModel, SerialFilter from app.pagination import MyPageNumberPagination +from user.models import UserProfile +from django.contrib.auth import authenticate, login, logout +from .utils import generate_token, decode_token_exp_time + +logger = logging.getLogger('mylogger') class ModelQuery(viewsets.ModelViewSet): # 查询类 - queryset = TP.objects.all().order_by("-uid") # 按照uid倒序 + queryset = TP.objects.all().order_by("-uid") # 按照uid倒序 # 序列化类 serializer_class = SerialMyModel # 分页类 @@ -18,3 +27,78 @@ class ModelQuery(viewsets.ModelViewSet): # 条件筛选 filterset_class = SerialFilter + + def query_event(self, request, *args, **kwargs): + res = TP.objects.values('event_type').annotate(count=Count('event_type')).order_by('-count') + result = list(res) + data = dict() + for index, item in enumerate(result, 1): + data[index] = item.get('event_type') + response = { + 'success': True, + 'msg': '查询成功', + 'data': data + } + return Response(response) + + +class RegisterLoginViewSet(viewsets.ModelViewSet): + def tp_register(self, request, *args, **kwargs): + """注册 POST""" + # 判断用户是否为管理员 + # user = request.user + # if not user.is_superuser: + # return Response({'msg': '您不是管理员,无权限添加成员'}) + data = request.data + username = data.get('username') + password = data.get('password') + try: + if UserProfile.objects.filter(username=username).first(): + return Response({'msg': '该用户名已存在,请换一个'}) + UserProfile.objects.create_user(username=username, password=password) + return Response({'msg': '注册成功'}) + except Exception as e: + logger.info(e) + response = { + "msg": f'注册失败, 原因:{e}' + } + return Response(response) + + +class LoginView(viewsets.ModelViewSet): + def tp_login(self, request, *args, **kwargs): + """登录 POST""" + data = request.data + username = data.get('username') + password = data.get('password') + login_user = authenticate(username=username, password=password) + if login_user and login_user.is_active: + # 生成token + token = generate_token(login_user) + response = { + 'success': True, + 'msg': '登录成功', + 'data': { + 'username': login_user.username, + 'roles': ['admin'] if login_user.is_superuser else ['common'], + 'accessToken': token, + 'expires': decode_token_exp_time(token) + } + } + return Response(response) + else: + response = { + 'success': False, + 'msg': '登录失败', + 'data': { + 'username': None, + 'roles': [], + 'accessToken': None, + 'expires': None + } + } + return Response(response) + + def tp_logout(self, request, *args, **kwargs): + """登出 GET""" + pass diff --git a/event/__init__.py b/event/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/event/admin.py b/event/admin.py new file mode 100644 index 0000000..557811a --- /dev/null +++ b/event/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +# Register your models here. +from event.models import Event +admin.site.register(Event) diff --git a/event/apps.py b/event/apps.py new file mode 100644 index 0000000..49fdd6a --- /dev/null +++ b/event/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class EventConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'event' diff --git a/event/filters.py b/event/filters.py new file mode 100644 index 0000000..f4d30fe --- /dev/null +++ b/event/filters.py @@ -0,0 +1,14 @@ +import django_filters +from django_filters.rest_framework import FilterSet + +from event.models import Event + + +class EventFilter(FilterSet): + event_name = django_filters.CharFilter(field_name='event_name', lookup_expr='icontains') + + class Meta: + # 指定模型 + models = Event + # 指定需要模糊查询的字段 + fields = ("event_name",) diff --git a/event/migrations/0001_initial.py b/event/migrations/0001_initial.py new file mode 100644 index 0000000..ebe90a7 --- /dev/null +++ b/event/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.19 on 2023-06-30 16:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Event', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_name', models.CharField(default='', max_length=256, verbose_name='事件名')), + ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ], + options={ + 'db_table': 'tp_event', + 'ordering': ['-id'], + }, + ), + ] diff --git a/event/migrations/__init__.py b/event/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/event/models.py b/event/models.py new file mode 100644 index 0000000..4126726 --- /dev/null +++ b/event/models.py @@ -0,0 +1,13 @@ +from django.db import models + +# Create your models here. + + +class Event(models.Model): + event_name = models.CharField(max_length=256, default='', verbose_name='事件名') + create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') + update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间') + + class Meta: + db_table = 'tp_event' + ordering = ['-id'] diff --git a/event/serializers.py b/event/serializers.py new file mode 100644 index 0000000..d93aab7 --- /dev/null +++ b/event/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers +from event.models import Event + + +class EventSerializer(serializers.ModelSerializer): + class Meta: + model = Event + fields = "__all__" diff --git a/event/tests.py b/event/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/event/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/event/urls.py b/event/urls.py new file mode 100644 index 0000000..13eaef9 --- /dev/null +++ b/event/urls.py @@ -0,0 +1,10 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from event import views + +router = DefaultRouter() +router.register('', views.EventViewSet) + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/event/views.py b/event/views.py new file mode 100644 index 0000000..a8f93af --- /dev/null +++ b/event/views.py @@ -0,0 +1,23 @@ +from django.shortcuts import render +from rest_framework import viewsets + +from app.pagination import MyPageNumberPagination +from event.filters import EventFilter +from event.models import Event +from event.serializers import EventSerializer + + +# Create your views here. + +class EventViewSet(viewsets.ModelViewSet): + # 查询集 + queryset = Event.objects.all().order_by('-id') + # 序列化器 + serializer_class = EventSerializer + # 分页器 + pagination_class = MyPageNumberPagination + # 过滤器 + filterset_class = EventFilter + + + diff --git a/logs/.__debug.lock b/logs/.__debug.lock new file mode 100644 index 0000000..e69de29 diff --git a/mymiddleware/middleware.py b/mymiddleware/middleware.py new file mode 100644 index 0000000..81c96ab --- /dev/null +++ b/mymiddleware/middleware.py @@ -0,0 +1,30 @@ +import jwt +from django.conf import settings +from user.models import UserProfile +from django.http import HttpResponse, JsonResponse +from django.utils.deprecation import MiddlewareMixin + +import logging + +from rest_framework.response import Response + +logger = logging.getLogger('mylogger') +class CheckTokenMiddleware(MiddlewareMixin): + def process_request(self, request): + # todo 登录时不需要校验token + path_info = request.path_info + if path_info.endswith('login'): + return + my_auth = request.META.get('HTTP_TOKEN') + if not my_auth: + return JsonResponse(data={'msg': '非法请求,请求头中未携带token'}, status=201) + try: + token = my_auth.split(' ')[1] + res_dict = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256']) + except Exception as e: + logger.info(e) + return JsonResponse({'msg': f'非法token,{e}'}, status=201) + # request.user = UserProfile.objects.filter(id=res_dict.get('user_id')).first() + # logger.info(res_dict) + return + diff --git a/requirements.txt b/requirements.txt index f9945863bb065820f8166cc03eafcd03078bf328..b71aa2742694d9e8f2f6de9a1c9ff199d5523424 100644 GIT binary patch delta 36 ocmdnPdV_Vt9wr%H1}=sIhDwGkhH{1y23sIBV$fqS2Vw&T0HgQ@?EnA( delta 7 Ocmcb?x`%bc9wq<{R|5?I diff --git a/user/__init__.py b/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user/admin.py b/user/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/user/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/user/apps.py b/user/apps.py new file mode 100644 index 0000000..36cce4c --- /dev/null +++ b/user/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UserConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'user' diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py new file mode 100644 index 0000000..1f0eb57 --- /dev/null +++ b/user/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# Generated by Django 3.2.19 on 2023-06-30 16:19 + +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='UserProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('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')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('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')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('organization', models.CharField(blank=True, max_length=256, null=True, verbose_name='组织')), + ('gender', models.IntegerField(choices=[(0, '女'), (1, '男')], default=1, verbose_name='性别')), + ('phone_number', models.CharField(blank=True, max_length=12, null=True, verbose_name='手机号')), + ('head_sculpture', models.CharField(blank=True, max_length=512, null=True, verbose_name='头像')), + ('status', models.IntegerField(default=0, verbose_name='状态')), + ('recorder_number', models.CharField(blank=True, max_length=256, null=True, verbose_name='执法记录仪编号')), + ('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')), + ('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')), + ], + options={ + 'db_table': 'tp_user', + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/user/migrations/__init__.py b/user/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user/models.py b/user/models.py new file mode 100644 index 0000000..2024c99 --- /dev/null +++ b/user/models.py @@ -0,0 +1,20 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models + +# Create your models here. + + +class UserProfile(AbstractUser): + GENDER = ((0, '女'), (1, '男')) + organization = models.CharField(max_length=256, verbose_name='组织', null=True, blank=True) + gender = models.IntegerField(choices=GENDER, default=1, verbose_name='性别') + phone_number = models.CharField(max_length=12, verbose_name='手机号', null=True, blank=True) + head_sculpture = models.CharField(max_length=512, verbose_name='头像', blank=True, null=True) + status = models.IntegerField(verbose_name='状态', default=0) + recorder_number = models.CharField(max_length=256, verbose_name='执法记录仪编号', null=True, blank=True) + + class Meta: + db_table = 'tp_user' + + + diff --git a/user/tests.py b/user/tests.py new file mode 100644 index 0000000..5a31890 --- /dev/null +++ b/user/tests.py @@ -0,0 +1,278 @@ +from django.test import TestCase + +# Create your tests here. + +l1 = [ + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-14 15:43:25", + "police_id": "8888888", + "event_type": "卡车", + "is_violation": False, + "small_image": "http://192.168.10.28:8000/media/images/B2_006.png", + "relative_time": 6.0, + "video_dir": "http://192.168.10.28:8000/media/videos/B2.mp4", + "car_number": "赣A·98980", + "ai_analysis": "", + "add_time": "2023-06-12 09:17:45", + "update_time": "2023-06-27 02:36:51", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-14 15:43:24", + "police_id": "8888888", + "event_type": "卡车", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/B2_004.png", + "relative_time": 4.0, + "video_dir": "http://192.168.10.28:8000/media/videos/B2.mp4", + "car_number": "赣A·98980", + "ai_analysis": "", + "add_time": "2023-06-12 09:16:41", + "update_time": "2023-06-27 02:36:51", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2023-05-14 17:27:40", + "police_id": "", + "event_type": "推搡", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/shoving_005_1.png", + "relative_time": 9.0, + "video_dir": "http://192.168.10.28:8000/media/videos/shoving.mp4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 07:13:24", + "update_time": "2023-06-27 02:36:51", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2023-05-14 17:27:40", + "police_id": "", + "event_type": "推搡", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/shoving_009_1.png", + "relative_time": 9.0, + "video_dir": "http://192.168.10.28:8000/media/videos/shoving.mp4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 07:13:19", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2023-05-14 17:27:40", + "police_id": "", + "event_type": "推搡", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/shoving_009.png", + "relative_time": 9.0, + "video_dir": "http://192.168.10.28:8000/media/videos/shoving.mp4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 07:12:34", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2023-05-14 17:27:40", + "police_id": "", + "event_type": "推搡", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/shoving_005.png", + "relative_time": 5.0, + "video_dir": "http://192.168.10.28:8000/media/videos/shoving.mp4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 07:10:03", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-14 17:27:40", + "police_id": "8888888", + "event_type": "未戴头盔", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G2_018_1.png", + "relative_time": 18.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G2.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:53:50", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-14 17:27:40", + "police_id": "", + "event_type": "未戴头盔", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G2_018.png", + "relative_time": 18.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G2.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:53:26", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 07:42:48", + "police_id": "0890151", + "event_type": "未戴头盔以及载人", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G1_024_1.png", + "relative_time": 24.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:52:12", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 07:42:48", + "police_id": "0890151", + "event_type": "未戴头盔以及载人", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G1_024.png", + "relative_time": 24.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:51:33", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 07:42:46", + "police_id": "0890151", + "event_type": "未戴头盔以及载人", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G1_022.png", + "relative_time": 22.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:50:53", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 07:42:28", + "police_id": "0890151", + "event_type": "未戴头盔以及载人", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G1_004.png", + "relative_time": 4.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:49:36", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 07:44:05", + "police_id": "0890151", + "event_type": "未戴头盔", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G1_141_1.png", + "relative_time": 101.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:48:34", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 07:44:05", + "police_id": "0890151", + "event_type": "未戴头盔", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G1_141.jpg", + "relative_time": 141.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:46:45", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 07:42:26", + "police_id": "0890151", + "event_type": "未戴头盔以及载人", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/G1_002.png", + "relative_time": 2.0, + "video_dir": "http://192.168.10.28:8000/media/videos/G1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:45:39", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 10:36:13", + "police_id": "6666666", + "event_type": "未戴头盔", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/B1_056.png", + "relative_time": 56.0, + "video_dir": "http://192.168.10.28:8000/media/videos/B1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:44:04", + "update_time": "2023-06-27 02:36:52", + "is_display": True + }, + { + "video_hash": "vbhdrbvcw", + "record_time": "2022-07-15 10:36:13", + "police_id": "6666666", + "event_type": "未戴头盔", + "is_violation": True, + "small_image": "http://192.168.10.28:8000/media/images/056.png", + "relative_time": 56.0, + "video_dir": "http://192.168.10.28:8000/media/videos/B1.MP4", + "car_number": "", + "ai_analysis": "", + "add_time": "2023-06-12 06:18:40", + "update_time": "2023-06-27 02:36:52", + "is_display": True + } +] + +l2 = [] + +from app.models import TP + + +def import_data(data): + try: + for item in data: + TP.objects.create(**item) + return + except Exception as e: + print(e) + return + +if __name__ == '__main__': + import_data(l1) diff --git a/user/views.py b/user/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/user/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/uwsgi.ini b/uwsgi.ini index da33f84..372b6ba 100644 --- a/uwsgi.ini +++ b/uwsgi.ini @@ -9,6 +9,7 @@ chdir=%(base)/%(project) module=TP_API.wsgi:application master=True processes=2 +threads=4 http=0.0.0.0:8000 @@ -35,4 +36,7 @@ memory-report = true reload-mercy = 10 #设置工作进程使用虚拟内存超过N MB就回收重启 -reload-on-as= 1024 \ No newline at end of file +reload-on-as= 1024 + +# 序列化接受的内容,如果可能的话 +thunder-lock=true \ No newline at end of file