commit 75fc321db777091da08152398e29c66edf56855e Author: kongfp Date: Fri Jul 7 15:33:49 2023 +0800 init project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab115d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +.DS_Store +node_modules +/dist +.idea +__pycache__ + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*.log +db.sqlite3 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f5338cf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# 建立 python 3.7环境 +FROM python:3.7 + +# 安装netcat +RUN apt-get update + +# 可选:设置镜像源为国内 +COPY pip.conf /root/.pip/pip.conf + +# 容器内创建 myproject 文件夹 +ENV APP_HOME=/home/myproject +RUN mkdir -p $APP_HOME +WORKDIR $APP_HOME + +# 将当前目录加入到工作目录中(. 表示当前目录) +ADD . $APP_HOME + +# 更新pip版本 +RUN /usr/local/bin/python -m pip install --upgrade pip + +# 安装项目依赖 +RUN pip install -r requirements.txt + +# 移除\r in windows +RUN sed -i 's/\r//' ./start.sh + +# 给start.sh可执行权限 +RUN chmod +x ./start.sh +RUN chmod 777 -R /home/myproject/logs/ + +EXPOSE 8000 + +# 数据迁移,并使用uwsgi启动服务 +ENTRYPOINT /bin/bash ./start.sh \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..86c5099 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# XZNSH_Api + +XZNSH后台 \ No newline at end of file diff --git a/XZNSH_API/__init__.py b/XZNSH_API/__init__.py new file mode 100644 index 0000000..063cd2c --- /dev/null +++ b/XZNSH_API/__init__.py @@ -0,0 +1,2 @@ +import pymysql +pymysql.install_as_MySQLdb() diff --git a/XZNSH_API/asgi.py b/XZNSH_API/asgi.py new file mode 100644 index 0000000..1b83a78 --- /dev/null +++ b/XZNSH_API/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for XZNSH_API project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'XZNSH_API.settings') + +application = get_asgi_application() diff --git a/XZNSH_API/settings.py b/XZNSH_API/settings.py new file mode 100644 index 0000000..421671b --- /dev/null +++ b/XZNSH_API/settings.py @@ -0,0 +1,245 @@ +""" +Django settings for XZNSH_API project. + +Generated by 'django-admin startproject' using Django 3.2.19. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.2/ref/settings/ +""" + +from pathlib import Path +import os + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-kqm6r(-!q-emw5c!!@dc)9l^hm@m#4!)-d7ecq85&u^5leu5)_' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False + +ALLOWED_HOSTS = ["*"] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'django_filters', + 'corsheaders', + 'app', + 'event', + 'user', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'mymiddleware.middleware.CheckTokenMiddleware', +] + +ROOT_URLCONF = 'XZNSH_API.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'XZNSH_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.mysql', + 'NAME': 'xznsh_test', + 'USER': 'root', + 'PASSWORD': '123abc', + 'HOST': '127.0.0.1', + 'PORT': '3306', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.2/topics/i18n/ + +LANGUAGE_CODE = 'zh-hans' + +TIME_ZONE = 'Asia/Shanghai' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = False + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.2/howto/static-files/ + +STATIC_URL = '/static/' + +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') + +# Default primary key field type +# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# 解决跨域 +CORS_ALLOW_CREDENTIALS = True +CORS_ORIGIN_ALLOW_ALL = True +CORS_ALLOW_HEADERS = ["*"] + +# CORS_ORIGIN_WHITELIST = ('*',) +# 对应的发送的请求的跨域 +CORS_ALLOW_METHODS = ( + 'DELETE', + 'GET', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', + 'VIEW', +) + +USE_L10N = False +DATE_FORMAT = 'Y-m-d' +DATETIME_FORMAT = 'Y-m-d H:M:S' + +REST_FRAMEWORK = { + 'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S", + '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', + # ), +} + +# 配置日志 +LOG_DIR = os.path.join(BASE_DIR, 'logs') +LOGGING = { + 'version': 1, # 保留字 + 'disable_existing_loggers': False, # 是否禁用已经存在的日志实例 + 'formatters': { # 定义日志的格式 + 'standard': { + 'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' + '[%(levelname)s]%(message)s' + }, + 'simple': { + 'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' + }, + 'collect': { + 'format': '%(message)s' + } + }, + 'filters': { # 定义日志的过滤器 + 'require_debug_true': { + '()': 'django.utils.log.RequireDebugTrue', + }, + }, + 'handlers': { # 日志处理程序 + 'console': { + 'level': 'DEBUG', + 'filters': ['require_debug_true'], # 只有在Django debug为True时才在屏幕打印日志 + 'class': 'logging.StreamHandler', + '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 * 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应用如下配置 + 'handlers': ['console'], + 'level': 'DEBUG', + 'propagate': True, # 是否向上一级logger实例传递日志信息 + }, + }, +} + +AUTH_USER_MODEL = 'user.UserProfile' diff --git a/XZNSH_API/urls.py b/XZNSH_API/urls.py new file mode 100644 index 0000000..13592fd --- /dev/null +++ b/XZNSH_API/urls.py @@ -0,0 +1,33 @@ +"""XZNSH_Api URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include, re_path +from django.views.static import serve +from django.conf.urls.static import static +from django.conf import settings +from .settings import MEDIA_ROOT + + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include('app.urls')), + path('event/', include('event.urls')), +] + + +urlpatterns += [ + re_path(r'^media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}), +] diff --git a/XZNSH_API/wsgi.py b/XZNSH_API/wsgi.py new file mode 100644 index 0000000..25446d1 --- /dev/null +++ b/XZNSH_API/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for XZNSH_API project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'XZNSH_API.settings') + +application = get_wsgi_application() diff --git a/XZNSH后台接口文档.md b/XZNSH后台接口文档.md new file mode 100644 index 0000000..344712d --- /dev/null +++ b/XZNSH后台接口文档.md @@ -0,0 +1,279 @@ + + + + + + +# XZNSH后台接口文档 +
+条件查询 + +- 请求方式:GET + +- 请求链接:http://127.0.0.1:8002/api/ + +- 请求参数: + + | 参数名 | 参数值 | 是否必填 | 参数类型 | 描述说明 | + | ----------- |---------------------| -------- |--------| ---------- | + | record_time | 2023-05-26 13:09:05 | 否 | string | 记录仪时间 | + | police_id | 00000001 | 否 | string | 警号 | + | event_type | 0 | 否 | string | 事件类型 | + +- 可选参数: + + | 参数名 | 参数值 | 是否必填 | 参数类型 | 描述说明 | + | --------- | ------ | -------- | -------- | -------------------- | + | page | 1 | 否 | string | 页码 | + | page_size | 20 | 否 | string | 页面大小(每页条数) | + +- 返回值: + + | 参数名 | 参数值 | 参数类型 | 描述说明 | + | ---------------- | ------------------------------------------------- | -------- |--------------| + | 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 | 行人是否违法 | + + + + +
+ +
+查询所有 + +- 请求方式:GET + +- 请求链接:http://127.0.0.1:8002/api/ + +- 可选参数: + + | 参数名 | 参数值 | 是否必填 | 参数类型 | 描述说明 | + | --------- | ------ | -------- | -------- | -------------------- | + | page | 1 | 否 | string | 页码 | + | page_size | 20 | 否 | string | 页面大小(每页条数) | + +- 返回值 + + | 参数名 | 参数值 | 参数类型 | 描述说明 | + | ------------- | ------------------------------------------------- | -------- | ------------------------ | + | 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 | 是否展示(自动添加) | + + +
+ +
+新增数据 + +- 请求方式:POST + +- 请求链接:http://127.0.0.1:8002/api/ + +- 请求body: + + ```json + { + "video_hash": "vbhdrbvcw", + "record_time": "2023-05-26 13:09:05", + "police_id": "00000002", + "event_type": "1", + "is_violation": true, + "small_image": "nvikefrooiwer", + "relative_time": 4.0, + "video_dir": "/d/test", + "car_number": "苏a045689", + "ai_analysis": "违规" + } + ``` + + | 参数名 | 参数值 | 是否必填 | 参数类型 | 描述说明 | + | ------------- |---------------------| -------- |----------| ------ | +| video_hash | vbhdrbvcw | 是 | string | 视频哈希 | + | record_time | 2023-05-26 13:09:05 | 是 | datetime | 记录仪时间 | + | police_id | 00000002 | 是 | string | 警号 | + | event_type | 1 | 是 | string | 事件类型 | + | is_violation | true | 是 | bool | 是否违规 | + | small_image | nvikefrooiwer | 是 | string | 缩略图 | + | relative_time | 4.0 | 是 | int | 相对时间 | + | video_dir | /d/test | 是 | string | 视频路径 | + | car_number | 苏a045689 | 是 | string | 车牌号 | + | ai_analysis | 违规 | 是 | string | 分析结果 | + +- 返回值 + + | 参数名 | 参数值 | 参数类型 | 描述说明 | + | ------------- | ------------------------------------------------- | -------- | ------------------------ | + | 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 | 是否展示(自动添加) | + + +
+ +
+修改数据 + +- 请求方式:put + +- 请求链接:http://127.0.0.1:8002/api/20/ + +- 请求body: + + ```json + { + "video_hash": "vbhdrbvcw", + "record_time": "2023-05-26 13:09:05", + "police_id": "00000002", + "event_type": "1", + "is_violation": true, + "small_image": "nvikefrooiwer", + "relative_time": 4.0, + "video_dir": "/d/test", + "car_number": "苏a045689", + "ai_analysis": "违规" + } + ``` + + | 参数名 | 参数值 | 是否必填 | 参数类型 | 描述说明 | + | ------------- |---------------------| -------- |-----|-------| +| video_hash | vbhdrbvcw | 是 | string | 视频哈希 | + | record_time | 2023-05-26 13:09:05 | 是 | string | 记录仪时间 | + | police_id | 00000002 | 是 | string | 警号 | + | event_type | 1 | 是 | string | 事件类型 | + | is_violation | true | 是 | bool | 是否违规 | + | small_image | nvikefrooiwer | 是 | string | 缩略图 | + | relative_time | 4.0 | 是 | float | 相对时间 | + | video_dir | /d/test | 是 | string | 视频路径 | + | car_number | 苏a045689 | 是 | string | 车牌号 | + | ai_analysis | 违规 | 是 | string | 分析结果 | + +- 备注:操作会修改uid为20的数据 + +- 返回值: + + | 参数名 | 参数值 | 参数类型 | 描述说明 | + | ------------- | ------------------------------------------------- | -------- | ---------------------- | + | 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 | 是否展示(自动添加) | + + +
+ +
+ +删除数据 + +- 请求方式:delete + +- 请求链接:http://127.0.0.1:8002/api/20/ + +- 备注:操作会删除uid为20的数据 + +- 返回值: + + | 参数名 | 参数值 | 参数类型 | 描述说明 | + | ------------- | ------------------------------------------------- | -------- | ------------------------ | + | 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 | 是否展示(自动添加) | + + +
+ +
+ +登录 + +- 请求方式: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/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/admin.py b/app/admin.py new file mode 100644 index 0000000..a932cee --- /dev/null +++ b/app/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +# Register your models here. +from app.models import TP +admin.site.register(TP) diff --git a/app/apps.py b/app/apps.py new file mode 100644 index 0000000..ab972ce --- /dev/null +++ b/app/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class App01Config(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'app' diff --git a/app/exception.py b/app/exception.py new file mode 100644 index 0000000..8ca9a8a --- /dev/null +++ b/app/exception.py @@ -0,0 +1,23 @@ +import logging +from rest_framework.views import exception_handler as drf_exception_handler # drf原生处理异常函数取别名 +from rest_framework.views import Response +from rest_framework import status + + +logger = logging.getLogger('mylogger') + + +def exception_handler(exc, context): + # drf的exception_handler做基础处理 + response = drf_exception_handler(exc, context) + # 为空,进行自定义二次处理 + logger.error(str(exc)) + if response is None: + # print(exc) # 错误原因 + # print(context) # 错误信息 + # print('%s - %s - %s' % (context['view'], context['request'].method, exc)) + return Response({ + 'detail': '服务器错误' + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True) + return response + diff --git a/app/migrations/__init__.py b/app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..8d654b1 --- /dev/null +++ b/app/models.py @@ -0,0 +1,42 @@ +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=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='事件类型/车辆违法原因', null=True, blank=True) # 还没迁移 + # 是否违规 + is_violation = models.BooleanField(verbose_name='执法人员是否违规', null=True, blank=True) + # 缩略图 + 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(max_length=50, 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='加入时间') + # 更新时间 + update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间') + # 是否显示 + 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/pagination.py b/app/pagination.py new file mode 100644 index 0000000..b60b4ab --- /dev/null +++ b/app/pagination.py @@ -0,0 +1,9 @@ +from rest_framework.pagination import PageNumberPagination + + +class MyPageNumberPagination(PageNumberPagination): + page_size = 10 + page_query_param = "page" + page_size_query_param = "page_size" + max_page_size = 100 + diff --git a/app/serializers.py b/app/serializers.py new file mode 100644 index 0000000..fabae95 --- /dev/null +++ b/app/serializers.py @@ -0,0 +1,58 @@ +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 + fields = "__all__" + + +class SerialFilter(FilterSet): + """ + 过滤器,支持模糊查询 + record_time,日期时间格式,如 2023-01-01 00:00:00 + police_id,支持模糊匹配 + event_type,支持模糊匹配 + """ + record_time = django_filters.DateTimeFilter(field_name='record_time', lookup_expr='icontains') + police_id = django_filters.CharFilter(field_name='police_id', lookup_expr='icontains') + 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', 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", "ai_analysis",) + + diff --git a/app/tests.py b/app/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/urls.py b/app/urls.py new file mode 100644 index 0000000..eddd084 --- /dev/null +++ b/app/urls.py @@ -0,0 +1,29 @@ +"""XZNSH_Api URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +# from django.contrib import admin +from django.urls import path, include, re_path +from rest_framework.routers import DefaultRouter +from app import views + +router = DefaultRouter() +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 new file mode 100644 index 0000000..b6a33ca --- /dev/null +++ b/app/views.py @@ -0,0 +1,104 @@ +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 +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倒序 + # 序列化类 + serializer_class = SerialMyModel + # 分页类 + pagination_class = MyPageNumberPagination + + # 条件筛选 + 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/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..bbdca0c --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'XZNSH_API.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/media/images/.gitkeep b/media/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/media/video/.gitkeep b/media/video/.gitkeep 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/pip.conf b/pip.conf new file mode 100644 index 0000000..c969f0d --- /dev/null +++ b/pip.conf @@ -0,0 +1,6 @@ +[global] +index-url = https://pypi.tuna.tsinghua.edu.cn/simple +[install] +trusted-host = https://pypi.tuna.tsinghua.edu.cn +[list] +format=columns \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..829da45 Binary files /dev/null and b/requirements.txt differ diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..f01c80d --- /dev/null +++ b/start.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +python manage.py makemigrations&& +python manage.py migrate&& +uwsgi --ini /home/myproject/uwsgi.ini&& +echo 'TP项目启动完成' +tail -f /dev/null + +exec "$@" \ No newline at end of file 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 new file mode 100644 index 0000000..a84b5fd --- /dev/null +++ b/uwsgi.ini @@ -0,0 +1,42 @@ +[uwsgi] + +project=myproject +uid=www-data +gid=www-data +base=/home + +chdir=%(base)/%(project) +module=XZNSH_API.wsgi:application +master=True +processes=2 +threads=4 + +http=0.0.0.0:8000 + +chown-socket=%(uid):www-data +chmod-socket=664 + +vacuum=True +max-requests=5000 + +pidfile=%(base)/%(project)/logs/%(project)-master.pid +daemonize=%(base)/%(project)/logs/%(project)-uwsgi.log + +#设置一个请求的超时时间(秒),如果一个请求超过了这个时间,则请求被丢弃 +harakiri = 60 +post buffering = 8192 +buffer-size= 65535 +#当一个请求被harakiri杀掉会,会输出一条日志 +harakiri-verbose = true + +#开启内存使用情况报告 +memory-report = true + +#设置平滑的重启(直到处理完接收到的请求)的长等待时间(秒) +reload-mercy = 10 + +#设置工作进程使用虚拟内存超过N MB就回收重启 +reload-on-as= 1024 + +# 序列化接受的内容,如果可能的话 +thunder-lock=true \ No newline at end of file