first commit

main
chunquansang 1 year ago
parent 6e359ac369
commit a1ed40a446

@ -0,0 +1,37 @@
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
# 安装vim
RUN apt-get install -y vim
# 安装项目依赖
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

@ -1,2 +1,3 @@
# TP_API_3.0
# TP_Admin
TP后台

@ -0,0 +1,2 @@
import pymysql
pymysql.install_as_MySQLdb()

@ -0,0 +1,16 @@
"""
ASGI config for TP_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', 'TP_API.settings')
application = get_asgi_application()

@ -0,0 +1,263 @@
"""
Django settings for TP_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/
"""
import os
from pathlib import Path
from elasticsearch_dsl import connections
# 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',
'department',
]
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 = 'TP_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 = '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.mysql',
'NAME': os.getenv("MYSQL_DATABASE") or 'tp_11_23',
'USER': os.getenv("MYSQL_USER") or 'root',
'PASSWORD': os.getenv("MYSQL_PASSWORD") or '#Yaxin0504',
'HOST': os.getenv("MYSQL_HOST") or '192.168.10.96',
'PORT': os.getenv("MYSQL_PORT") or '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/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, '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': 'logging.handlers.RotatingFileHandler', # window多进程 需要 pip install concurrent-log-handler
'filename': os.path.join(BASE_DIR, "logs/debug.log"), # 日志文件
'backupCount': 10, # 保留的最大文件数,超过则删除日期最早的
'maxBytes': 1024 * 1024 * 100, # 文件大小
'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', 'file'],
'level': 'DEBUG',
'propagate': True, # 是否向上一级logger实例传递日志信息
},
},
}
AUTH_USER_MODEL = 'user.UserProfile'
# connections.create_connection(
# hosts=['192.168.10.96:9208'],
# http_auth=('elastic', 'changeme')
# )
ES_USER = os.getenv("ES_USER") or "elastic"
ES_PASSWORD = os.getenv("ES_PASSWORD") or "changeme"
ES_IP = os.getenv("ES_IP") or "192.168.10.96:9209"
connections.create_connection(hosts=[f"http://{ES_USER}:{ES_PASSWORD}@{ES_IP}"])
VIDEO_DOWNLOAD_PATH = os.getenv("VIDEO_DOWNLOAD_PATH") or '/home/tp/dsr/TP-Code-AI/src/media/videos'

@ -0,0 +1,35 @@
"""TP_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/tps/', include('app.urls')),
path('event/', include('event.urls')),
path('api/user/', include('user.urls')),
path('api/departments/', include('department.urls')),
]
urlpatterns += [
re_path(r'^media/(?P<path>.*)$', serve, {'document_root': MEDIA_ROOT}),
]

@ -0,0 +1,16 @@
"""
WSGI config for TP_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', 'TP_API.settings')
application = get_wsgi_application()

@ -0,0 +1,279 @@
# TP后台接口文档
<details>
<summary>条件查询</summary>
- 请求方式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 | 行人是否违法 |
</details>
<details>
<summary>查询所有</summary>
- 请求方式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 | 是否展示(自动添加) |
</details>
<details>
<summary>新增数据</summary>
- 请求方式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 | 是否展示(自动添加) |
</details>
<details>
<summary>修改数据</summary>
- 请求方式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 | 是否展示(自动添加) |
</details>
<details>
<summary>删除数据</summary>
- 请求方式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 | 是否展示(自动添加) |
</details>
<details>
<summary>登录</summary>
- 请求方式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过期时间 |
</details>

@ -0,0 +1,5 @@
from django.contrib import admin
# Register your models here.
# from app.models import TP
# admin.site.register(TP)

@ -0,0 +1,6 @@
from django.apps import AppConfig
class App01Config(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'app'

@ -0,0 +1,59 @@
from elasticsearch_dsl import Document, Date, Text, Integer, Nested, Keyword, Object
class VideoIndex(Document):
STATUS = ((0, "未分析"), (1, "分析中"), (2, "分析完成"), (3, "分析失败"))
DOWNLOAD_STATUS = ((0, "未下载"), (1, "下载中"), (2, "下载完成"))
video_name = Text(fields={'keyword': {'type': 'keyword'}}) # 添加一个 keyword 字段以支持精确匹配
video_path = Text(fields={'keyword': {'type': 'keyword'}})
ftp_path = Text(fields={'keyword': {'type': 'keyword'}})
status = Integer()
download_status = Integer()
create_time = Date()
update_time = Date()
video_date = Date()
video_hash = Text(fields={'keyword': {'type': 'keyword'}})
video_status = Integer()
class Index:
name = 'video_index' # 索引名称
class Meta:
db_table = "tp_videos"
class TaskIndex(Document):
STATUS = ((0, "未分析"), (1, "分析中"), (2, "分析完成"), (3, "分析失败"))
taskId = Integer()
analysis_status = Integer()
create_time = Date()
update_time = Date()
error_reason = Text(fields={'keyword': {'type': 'keyword'}})
video_id = Integer()
class Index:
name = 'task_index'
class Meta:
db_table = 'tp_tasks'
class TPResIndex(Document):
relative_time = Date()
exception_type = Text()
abnormal_id = Text()
abnormal_behavior = Text(fields={'keyword': {'type': 'keyword'}})
abnormal_pic = Text()
abnormal_video = Text()
video_id = Integer()
create_time = Date()
update_time = Date()
class Index:
name = 'tpres_index'
class Meta:
db_table = 'tp_res'

@ -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

@ -0,0 +1,67 @@
# Generated by Django 3.2.19 on 2023-09-22 09:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Video',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('video_name', models.CharField(max_length=512, null=True, verbose_name='视频名称')),
('video_path', models.CharField(max_length=512, null=True, verbose_name='视频路径')),
],
options={
'db_table': 'tp_videos',
},
),
migrations.CreateModel(
name='TPRes',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('relative_time', models.FloatField(blank=True, null=True, verbose_name='相对时间')),
('exception_type', models.CharField(max_length=128, null=True, verbose_name='异常类型')),
('abnormal_id', models.CharField(max_length=256, null=True, verbose_name='异常ID')),
('abnormal_behavior', models.CharField(max_length=256, null=True, verbose_name='异常行为')),
('abnormal_pic', models.CharField(max_length=1024, null=True, verbose_name='异常关键帧/图片')),
('abnormal_video', models.CharField(max_length=1024, null=True, verbose_name='异常视频定位/视频')),
('video', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='app.video')),
],
options={
'db_table': 'tp_tp',
},
),
migrations.CreateModel(
name='Person',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('pic_path', models.CharField(max_length=512, null=True, verbose_name='图片路径')),
('pic_quality', models.IntegerField(null=True, verbose_name='图片质量')),
('video', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='app.video')),
],
options={
'db_table': 'tp_person',
},
),
migrations.CreateModel(
name='Car',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('pic_path', models.CharField(max_length=512, null=True, verbose_name='图片路径')),
('pic_quality', models.IntegerField(null=True, verbose_name='图片质量')),
('car_number', models.CharField(max_length=512, null=True, verbose_name='车牌号')),
('video', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='app.video')),
],
options={
'db_table': 'tp_car',
},
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-09-26 14:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='video',
name='status',
field=models.IntegerField(choices=[(0, '未分析'), (1, '分析中'), (2, '分析完成')], default=0, verbose_name='视频状态'),
),
]

@ -0,0 +1,23 @@
# Generated by Django 3.2.19 on 2023-09-28 09:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0002_video_status'),
]
operations = [
migrations.AddField(
model_name='video',
name='create_time',
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间'),
),
migrations.AddField(
model_name='video',
name='update_time',
field=models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间'),
),
]

@ -0,0 +1,43 @@
# Generated by Django 3.2.19 on 2023-10-12 15:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0003_auto_20230928_0952'),
]
operations = [
migrations.AddField(
model_name='video',
name='download_status',
field=models.IntegerField(choices=[(0, '未下载'), (1, '下载中'), (2, '下载完成')], default=0, verbose_name='视频下载状态'),
),
migrations.AddField(
model_name='video',
name='ftp_path',
field=models.CharField(max_length=512, null=True, verbose_name='视频ftp路径'),
),
migrations.AddField(
model_name='video',
name='video_date',
field=models.CharField(max_length=512, null=True, verbose_name='视频所属日期'),
),
migrations.AddField(
model_name='video',
name='video_hash',
field=models.CharField(max_length=512, null=True, unique=True, verbose_name='视频哈希'),
),
migrations.AlterField(
model_name='video',
name='status',
field=models.IntegerField(choices=[(0, '未分析'), (1, '分析中'), (2, '分析完成')], default=0, verbose_name='视频分析状态'),
),
migrations.AlterField(
model_name='video',
name='video_path',
field=models.CharField(max_length=512, null=True, verbose_name='视频本地路径'),
),
]

@ -0,0 +1,23 @@
# Generated by Django 3.2.19 on 2023-10-17 10:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0004_auto_20231012_1512'),
]
operations = [
migrations.AlterField(
model_name='video',
name='status',
field=models.IntegerField(choices=[(0, '未分析'), (1, '分析中'), (2, '分析完成'), (3, '分析失败')], default=0, verbose_name='视频分析状态'),
),
migrations.AlterField(
model_name='video',
name='video_date',
field=models.DateField(max_length=512, null=True, verbose_name='视频所属日期'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-10-17 16:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0005_auto_20231017_1022'),
]
operations = [
migrations.AddField(
model_name='video',
name='error_reason',
field=models.CharField(max_length=512, null=True, unique=True, verbose_name='視頻哈希'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-10-17 17:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0006_video_error_reason'),
]
operations = [
migrations.AlterField(
model_name='video',
name='error_reason',
field=models.CharField(max_length=512, null=True, verbose_name='視頻哈希'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-10-25 13:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0007_alter_video_error_reason'),
]
operations = [
migrations.AlterField(
model_name='video',
name='ftp_path',
field=models.CharField(max_length=512, null=True, unique=True, verbose_name='视频ftp路径'),
),
]

@ -0,0 +1,23 @@
# Generated by Django 3.2.19 on 2023-10-30 11:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0008_alter_video_ftp_path'),
]
operations = [
migrations.AlterField(
model_name='car',
name='pic_quality',
field=models.CharField(max_length=128, null=True, verbose_name='图片质量'),
),
migrations.AlterField(
model_name='person',
name='pic_quality',
field=models.CharField(max_length=128, null=True, verbose_name='图片质量'),
),
]

@ -0,0 +1,27 @@
# Generated by Django 3.2.19 on 2023-11-08 13:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('app', '0009_auto_20231030_1136'),
]
operations = [
migrations.CreateModel(
name='Task',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('analysis_status', models.IntegerField(choices=[(0, '未分析'), (1, '分析中'), (2, '分析完成'), (3, '分析失败')], default=0, verbose_name='视频分析状态')),
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间')),
('video', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='app.video')),
],
options={
'db_table': 'tp_tasks',
},
),
]

@ -0,0 +1,22 @@
# Generated by Django 3.2.19 on 2023-11-09 11:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0010_task'),
]
operations = [
migrations.RemoveField(
model_name='task',
name='id',
),
migrations.AddField(
model_name='task',
name='taskId',
field=models.AutoField(default=None, primary_key=True, serialize=False),
),
]

@ -0,0 +1,22 @@
# Generated by Django 3.2.19 on 2023-11-09 11:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0011_auto_20231109_1109'),
]
operations = [
migrations.RemoveField(
model_name='video',
name='error_reason',
),
migrations.AddField(
model_name='task',
name='error_reason',
field=models.CharField(max_length=512, null=True, verbose_name='错误信息'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-11-17 15:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0012_auto_20231109_1117'),
]
operations = [
migrations.AddField(
model_name='video',
name='video_status',
field=models.IntegerField(choices=[(1, '有效视频'), (2, '无效视频'), (3, '异常视频')], default=1, verbose_name='视频有效/无效'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-11-24 10:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0013_video_video_status'),
]
operations = [
migrations.AddField(
model_name='task',
name='callbackUrl',
field=models.CharField(max_length=512, null=True, verbose_name='回调地址'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.2.19 on 2023-12-13 14:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0014_task_callbackurl'),
]
operations = [
migrations.AlterField(
model_name='task',
name='analysis_status',
field=models.IntegerField(choices=[(100, '未分析'), (101, '分析中'), (102, '分析完成'), (103, "Couldn't open webcam or video"), (104, '视频文件不存在'), (105, '切分视频失败'), (106, '未知异常')], default=100, verbose_name='视频分析状态'),
),
]

@ -0,0 +1,28 @@
# Generated by Django 3.2.25 on 2024-03-14 16:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0015_alter_task_analysis_status'),
]
operations = [
migrations.AlterField(
model_name='video',
name='download_status',
field=models.IntegerField(choices=[(0, '未下载'), (1, '下载中'), (2, '下载完成'), (3, '下载异常')], default=0, verbose_name='视频下载状态'),
),
migrations.AlterField(
model_name='video',
name='video_hash',
field=models.CharField(max_length=512, null=True, verbose_name='视频哈希'),
),
migrations.AlterField(
model_name='video',
name='video_status',
field=models.IntegerField(choices=[(1, '有效视频'), (2, '无效视频'), (3, '异常视频'), (4, '重复视频')], default=1, verbose_name='视频有效/无效'),
),
]

@ -0,0 +1,112 @@
from django.db import models
# Create your models here.
class Video(models.Model):
STATUS = ((0, "未分析"), (1, "分析中"), (2, "分析完成"), (3, "分析失败"))
DOWNLOAD_STATUS = ((0, "未下载"), (1, "下载中"), (2, "下载完成"), (3, "下载异常"))
VIDEO_STATUS = ((1, "有效视频"), (2, "无效视频"), (3, "异常视频"), (4, "重复视频"))
video_name = models.CharField(max_length=512, verbose_name='视频名称', null=True)
video_path = models.CharField(max_length=512, verbose_name='视频本地路径', null=True)
ftp_path = models.CharField(max_length=512, verbose_name='视频ftp路径', null=True, unique=True)
status = models.IntegerField(verbose_name='视频分析状态', choices=STATUS, default=0)
download_status = models.IntegerField(verbose_name='视频下载状态', default=0, choices=DOWNLOAD_STATUS)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', null=True)
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间', null=True)
video_date = models.DateField(verbose_name='视频所属日期', max_length=512, null=True)
video_hash = models.CharField(verbose_name='视频哈希', max_length=512, null=True)
video_status = models.IntegerField(verbose_name='视频有效/无效', choices=VIDEO_STATUS, default=1)
class Meta:
db_table = "tp_videos"
class Task(models.Model):
"""任务表 与视频表一对多?"""
STATUS = ((100, "未分析"), (101, "分析中"), (102, "分析完成"), (103, "Couldn't open webcam or video"), (104, '视频文件不存在'), (105, '切分视频失败'), (106, '未知异常'))
taskId = models.AutoField(primary_key=True, default=None)
analysis_status = models.IntegerField(verbose_name='视频分析状态', choices=STATUS, default=100)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', null=True)
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间', null=True)
video = models.ForeignKey(Video, on_delete=models.CASCADE, null=True)
error_reason = models.CharField(max_length=512, verbose_name='错误信息', null=True)
callbackUrl = models.CharField(max_length=512, verbose_name='回调地址', null=True)
class Meta:
db_table = 'tp_tasks'
# 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='记录仪时间', null=True, blank=True)
# # 设备号
# equipment_id = models.CharField(max_length=64, null=True, blank=True, 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=1024, verbose_name='缩略图', null=True, blank=True)
# # 相对时间
# relative_time = models.FloatField(verbose_name='相对时间', null=True, blank=True)
# # 视频路径
# video_dir = models.CharField(max_length=1024, verbose_name='视频路径')
# # 车牌号
# car_number = models.CharField(max_length=1024, 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)
#
# video = models.ForeignKey(Video, on_delete=models.CASCADE, null=True)
#
# class Meta:
# db_table = "app_tp"
#
# # 排序 uid倒序
# ordering = ['-uid']
class Person(models.Model):
pic_path = models.CharField(max_length=512, verbose_name='图片路径', null=True)
pic_quality = models.CharField(verbose_name='图片质量', null=True, max_length=128)
video = models.ForeignKey(Video, on_delete=models.CASCADE, null=True)
class Meta:
db_table = "tp_person"
class Car(models.Model):
pic_path = models.CharField(max_length=512, verbose_name='图片路径', null=True)
pic_quality = models.CharField(verbose_name='图片质量', null=True, max_length=128)
car_number = models.CharField(max_length=512, verbose_name='车牌号', null=True)
video = models.ForeignKey(Video, on_delete=models.CASCADE, null=True)
class Meta:
db_table = "tp_car"
# 视频内时间、异常类型、异常ID、异常行为、异常关键帧、异常视频定位
class TPRes(models.Model):
relative_time = models.FloatField(verbose_name='相对时间', null=True, blank=True)
exception_type = models.CharField(max_length=128, verbose_name='异常类型', null=True)
abnormal_id = models.CharField(max_length=256, verbose_name='异常ID', null=True)
abnormal_behavior = models.CharField(max_length=256, verbose_name='异常行为', null=True)
abnormal_pic = models.CharField(max_length=1024, verbose_name='异常关键帧/图片', null=True)
abnormal_video = models.CharField(max_length=1024, verbose_name='异常视频定位/视频', null=True)
video = models.ForeignKey(Video, on_delete=models.CASCADE, null=True)
class Meta:
db_table = "tp_tp"

@ -0,0 +1,8 @@
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

@ -0,0 +1,103 @@
import datetime
from rest_framework import serializers
from django_filters.rest_framework import FilterSet
import django_filters
from app.models import TPRes, Video, Task
import logging
logger = logging.getLogger('mylogger')
# class SerialMyModel(serializers.ModelSerializer):
# class Meta:
# model = TP
# fields = "__all__"
#
# def create(self, validated_data):
# instance = TP.objects.create(**validated_data)
# return instance
# def update(self, instance, validated_data):
# instance.video_hash = validated_data.get('video_hash', instance.video_hash)
# instance.record_time = validated_data.get('record_time', instance.record_time)
# instance.equipment_id = validated_data.get('equipment_id', instance.equipment_id)
# instance.police_id = validated_data.get('police_id', instance.police_id)
# instance.event_type = validated_data.get('event_type', instance.event_type)
# instance.is_violation = validated_data.get('is_violation', instance.is_violation)
# instance.small_image = validated_data.get('small_image', instance.small_image)
# instance.relative_time = validated_data.get('relative_time', instance.relative_time)
# instance.video_dir = validated_data.get('video_dir', instance.video_dir)
# instance.car_number = validated_data.get('car_number', instance.car_number)
# instance.ai_analysis = validated_data.get('ai_analysis', instance.ai_analysis)
# instance.is_display = validated_data.get('is_display', instance.is_display)
# instance.is_illegal = validated_data.get('is_illegal', instance.is_illegal)
# instance.save()
#
# return instance
# 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",)
class TPResSerializer(serializers.ModelSerializer):
class Meta:
model = TPRes
fields = "__all__"
# exclude = ('video_id',)
# def create(self, validated_data):
# instance = TPRes.objects.create(**validated_data)
# return instance
class AnalyzedVideoSerializer(serializers.ModelSerializer):
class Meta:
model = Video
fields = ('id', 'video_name', 'video_date', 'video_path', 'video_hash')
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = "__all__"

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,39 @@
"""TP_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('events', views.ModelQuery.as_view({"get": "query_event"})),
# path('', views.TPAPIView.as_view()),
# path('<pk>/', views.TPAPIView.as_view()),
path('upload_video/', views.VideoProcess.as_view({"post": "create", 'get': 'list'})),
path('analysis_video/', views.AnalysisVideo.as_view({"post": "create"})),
path('pics/', views.PicModelViewSet.as_view({'get': 'list'})),
path('', views.TPModelViewSet.as_view({'get': 'list', 'post': 'create'})),
path('get_analyzed_videos/', views.GetAnalyzedVideo.as_view({'post': 'list'})),
path('get_unanalyzed_videos/', views.VideoAnalysis.as_view({'get': 'list'})),
path('update_video_status/', views.UpdateVideoStatus.as_view({'post': 'update'})),
path('tasks/', views.TaskViewSet.as_view({'get': 'list', 'post': 'update'})),
path('download_video/', views.DownloadVideo.as_view({'post': 'create'})),
path('get_video_result/', views.VideoResult.as_view({'get': 'list'})),
]

@ -0,0 +1,73 @@
import time
import datetime
import jwt
import hashlib
from os import path
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
def update_tp_querydict(querydict):
new_querydict = dict()
if 'record_time' in querydict:
new_querydict['record_time__icontains'] = ''.join(querydict.pop('record_time'))
if 'police_id' in querydict:
new_querydict['police_id__icontains'] = ''.join(querydict.pop('police_id'))
if 'event_type' in querydict:
new_querydict['event_type__icontains'] = ''.join(querydict.pop('event_type'))
if 'start_time' in querydict:
new_querydict['record_time__gte'] = ''.join(querydict.pop('start_time'))
if 'end_time' in querydict:
new_querydict['record_time__lte'] = datetime.datetime.strptime(''.join(querydict.pop('end_time')), "%Y-%m-%d") + datetime.timedelta(days=1)
if 'violation' in querydict:
if ''.join(querydict.get('violation')) == '1':
new_querydict['is_violation'] = True
elif ''.join(querydict.get('violation')) == '0':
new_querydict['is_violation'] = False
if 'violation_type' in querydict:
new_querydict['ai_analysis__icontains'] = ''.join(querydict.pop('violation_type'))
return new_querydict
def get_file_hash(filename):
if path.isfile(filename) is False:
return
# make a hash object
h_sha256 = hashlib.sha256()
# open file for reading in binary mode
with open(filename, "rb") as file:
# read file in chunks and update hash
chunk = 0
while chunk != b"":
chunk = file.read(1024)
h_sha256.update(chunk)
# return the hex digest
return h_sha256.hexdigest()

@ -0,0 +1,686 @@
import asyncio
import logging
import os
import subprocess
import sys
import threading
import datetime
import time
from pathlib import Path
import requests
from django.db import transaction
from django.db.models import Count
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
from elasticsearch_dsl import Search, connections
# Create your views here.
from rest_framework import viewsets, status
from rest_framework.response import Response
from app.models import Video, Car, Person, TPRes, Task
from app.serializers import TPResSerializer, AnalyzedVideoSerializer, TaskSerializer
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, update_tp_querydict, get_file_hash
from rest_framework.views import APIView
from django.conf import settings
logger = logging.getLogger('mylogger')
system_video_flag = False
sys.path.append('/TP_922/TP_PMP/src')
def get_data_by_model(model, kwargs):
try:
# 使用.objects.get(pk=id)来获取特定ID的数据
data = model.objects.get(**kwargs)
return data
except model.DoesNotExist:
# 处理未找到数据的情况
return None
# class TPAPIView(APIView):
#
# def get(self, request):
# query_params = dict(request.query_params)
# logger.info(query_params)
# querydict = update_tp_querydict(query_params)
# logger.info(querydict)
# queryset = TP.objects.filter(**querydict)
# paginator = MyPageNumberPagination()
# paginated_queryset = paginator.paginate_queryset(queryset, request)
# serializer = SerialMyModel(paginated_queryset, many=True)
#
# # paginated_data = {
# # 'count': queryset.count(),
# # 'next': self.pagination_class().get_next_link(),
# # 'previous': self.pagination_class().get_previous_link(),
# # 'results': serializer.data
# # }
# # return Response(paginated_data)
# return paginator.get_paginated_response(serializer.data)
#
# def post(self, request, *args, **kwargs):
# data = request.data
# serializer = SerialMyModel(data=data)
# if serializer.is_valid():
# try:
# with transaction.atomic():
# instance = serializer.save() # 保存数据
# logger.info(instance)
# return Response(serializer.data, status=status.HTTP_201_CREATED)
# except Exception as e:
# return Response(str(e), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# else:
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#
# def put(self, request, pk, *args, **kwargs):
# try:
# with transaction.atomic():
# instance = TP.objects.select_for_update().get(uid=pk)
# serializer = SerialMyModel(instance, data=request.data, partial=True)
# if serializer.is_valid():
# serializer.save()
# return Response(serializer.data, status=status.HTTP_200_OK)
#
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# except TP.DoesNotExist:
# return Response("Object not found", status=status.HTTP_404_NOT_FOUND)
# except Exception as e:
# return Response(str(e), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#
# def delete(self, request, pk):
# try:
# with transaction.atomic():
# instance = TP.objects.get(uid=pk)
# instance.delete()
# except TP.DoesNotExist:
# return Response("Object not found", status=status.HTTP_404_NOT_FOUND)
# except Exception as e:
# return Response(str(e), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#
# return Response('删除成功', status=status.HTTP_200_OK)
# 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.exclude(event_type__isnull=True).exclude(event_type__exact='').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': '查询成功',
# 'results': data
# }
# return Response(response)
# 用APIVIEW或者视图函数改到user模块下
# 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 VideoProcess(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
query_params = dict(request.query_params)
logger.info(query_params)
if query_params.get('video_name'):
video_name = query_params.get('video_name')[0]
file_path = os.path.join('/TP_922/TP_PMP/media/video_input/', video_name)
if os.path.exists(file_path):
return Response({'msg': '该视频已存在', 'success': False, "code": 201})
else:
return Response({'msg': '请选择文件上传', 'success': False})
if Video.objects.filter(status=1):
return Response({'msg': '有视频正在分析中,请暂勿上传', 'success': False})
return Response({'success': True})
def create(self, request, *args, **kwargs):
video_obj = request.FILES.get('file')
if not video_obj:
return Response({'msg': '请选中文件', 'success': False})
video_path = os.path.join(r'/TP_922/TP_PMP/media/video_input/', video_obj.name)
logger.info(video_path)
if not video_obj.name.lower().endswith((".mp4", ".avi", ".mkv", ".mov")):
return Response({'msg': '请选择 .mp4, .MP4, .avi, .AVI, .mkv, .MKV, .mov, .MOV视频文件类型', 'success': False})
# 清空Video表
# Video.objects.all().delete()
# 如果有视频正在跑 status=1, 则不行
try:
with transaction.atomic():
video = Video.objects.create(video_name=video_obj.name, video_path=video_path)
except Exception as e:
return Response({'msg': f'上传失败,原因{e}', 'success': False})
# 清空视频
try:
old_files = os.listdir(r'/TP_922/TP_PMP/media/video_input/')
for file in old_files:
file_path = os.path.join(r'/TP_922/TP_PMP/media/video_input/', file)
if os.path.isfile(file_path):
os.remove(file_path)
except Exception as e:
return Response({'msg': f'清空旧视频失败,原因{e}', 'success': False})
try:
with open(video_path, 'wb+') as destination:
for chunk in video_obj.chunks():
destination.write(chunk)
return Response({'msg': '上传成功', 'file_path': video_path, 'success': True})
except Exception as e:
return Response({'msg': f'上传失败,原因{e}', 'success': False})
def analysis_video():
os.chdir('/TP_922/TP_PMP/src')
res = os.system('python main.py')
return res
class AnalysisVideo(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
data = request.data
video_name = data.get('video_name')
if not video_name:
running_video = Video.objects.filter(status=1).first()
if running_video:
return Response({'msg': f'有正在分析中的视频, {running_video.video_name}', 'success': False})
else:
latest_video = Video.objects.filter(status=2).order_by('-update_time').first()
if latest_video:
return Response({'msg': f'最新分析完成的视频 {latest_video.video_name}', 'success': True})
else:
return Response({'msg': '没有正在分析或已经分析完成的视频,请上传', 'success': False})
video_obj = Video.objects.filter(video_name=video_name).first()
if not video_obj:
return Response({'msg': '请正确选择视频文件'})
if video_obj.status == 1:
# if Person.objects.filter(video_id=video_obj.id) and Car.objects.filter(video_id=video_obj.id) and TPRes.objects.filter(video_id=video_obj.id):
if TPRes.objects.filter(video_id=video_obj.id):
video_obj.status = 2
video_obj.update_time = datetime.datetime.now()
video_obj.save()
return Response({'msg': '该视频已分析完毕,可查看结果', 'success': True})
return Response({'msg': '视频正在分析中,请稍等'})
if video_obj.status == 2:
return Response({'msg': '该视频已分析完毕,可查看结果', 'success': True})
# status 为 0
thread = threading.Thread(target=analysis_video)
thread.start()
video_obj.status = 1 # 分析中
video_obj.update_time = datetime.datetime.now()
video_obj.save()
return Response({'msg': '视频开始分析', 'success': True})
class PicModelViewSet(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
query_params = dict(request.query_params)
logger.info(query_params)
if query_params.get('video_name'):
video_name = query_params.get('video_name')[0]
else:
running_video = Video.objects.filter(status=1).first()
if running_video:
return Response({'msg': f'有正在分析中的视频, {running_video.video_name}', 'success': False})
else:
latest_video = Video.objects.filter(status=2).order_by('-update_time').first()
if latest_video:
person_data = list(Person.objects.filter(video_id=latest_video.id).values())
car_data = list(Car.objects.filter(video_id=latest_video.id).values())
data = dict()
data['person'] = person_data
data['car'] = car_data
return Response({'msg': f'最新分析完成的视频 {latest_video.video_name}', 'success': True, 'data': data})
else:
return Response({'msg': '没有正在分析或已经分析完成的视频,请上传', 'success': False})
video_obj = Video.objects.filter(video_name=video_name).first()
if not video_obj:
return Response({'msg': '该视频不存在'})
try:
person_data = list(Person.objects.filter(video_id=video_obj.id).values())
car_data = list(Car.objects.filter(video_id=video_obj.id).values())
data = dict()
data['person'] = person_data
data['car'] = car_data
response = {'msg': '查询成功', 'data': data, 'success': True}
return Response(response)
except Exception as e:
return Response({'msg': f'查询失败,{e}', 'success': False})
class TPModelViewSet(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
query_params = dict(request.query_params)
logger.info(query_params)
if query_params.get('video_name'):
video_name = query_params.get('video_name')[0]
else:
running_video = Video.objects.filter(status=1).first()
if running_video:
return Response({'msg': f'有正在分析中的视频, {running_video.video_name}', 'success': False})
else:
latest_video = Video.objects.filter(status=2).order_by('-update_time').first()
if latest_video:
tp_queryset = TPRes.objects.filter(video_id=latest_video.id).order_by('-id')
paginator = MyPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(tp_queryset, request)
serializer = TPResSerializer(paginated_queryset, many=True)
paginator_response = paginator.get_paginated_response(serializer.data)
response_data = {
'success': True,
'count': paginator_response.data['count'],
'next': paginator_response.data['next'],
'previous': paginator_response.data['previous'],
'results': list(paginator_response.data['results']),
}
return Response(response_data)
else:
return Response({'msg': '没有正在分析或已经分析完成的视频,请上传', 'success': False})
video_obj = Video.objects.filter(video_name=video_name).first()
if not video_obj:
return Response({'msg': '该视频不存在'})
tp_queryset = TPRes.objects.filter(video_id=video_obj.id).order_by('-id')
paginator = MyPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(tp_queryset, request)
serializer = TPResSerializer(paginated_queryset, many=True)
paginator_response = paginator.get_paginated_response(serializer.data)
response_data = {
'success': True,
'count': paginator_response.data['count'],
'next': paginator_response.data['next'],
'previous': paginator_response.data['previous'],
'results': list(paginator_response.data['results']),
}
return Response(response_data)
def create(self, request, *args, **kwargs):
data = request.data
video_name = data.get('video_name')
video_id = data.get('video_id')
video_obj = Video.objects.filter(id=video_id).first()
if not video_obj:
return Response({'msg': '该视频不存在'})
video_obj.video_hash = data.get('video_hash')
video_obj.save()
person_dict = data.get('person')
car_dict = data.get('car')
msg_list = data.get('message')
video_status = data.get('video_status')
try:
with transaction.atomic():
# 暂改
if video_status:
video_obj.video_status = video_status
video_obj.save()
if person_dict:
for values in person_dict.values():
if len(values) == 2:
person_obj = Person.objects.create(pic_path=values[0], pic_quality=values[1], video_id=video_obj.id)
if car_dict:
for values in car_dict.values():
if len(values) == 3:
car_obj = Car.objects.create(pic_path=values[0], pic_quality=values[1], car_number=values[2], video_id=video_obj.id)
if msg_list:
for tp_dict in msg_list:
tp_obj = TPRes.objects.create(relative_time=tp_dict.get('relative_time'), exception_type=tp_dict.get('exception_type'), abnormal_id=tp_dict.get('abnormal_id'), abnormal_behavior=tp_dict.get('abnormal_behavior'), abnormal_pic=tp_dict.get('abnormal_pic'), abnormal_video=data.get('video_dir'), video_id=video_obj.id)
video_obj.status = 2
video_obj.save()
return Response({'msg': '上传成功'})
except Exception as e:
return Response({'msg': f'上传失败,原因{e}'})
class GetAnalyzedVideo(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
data = request.data
logger.info(data)
start_time = data.get('start_time')
end_time = data.get('end_time')
abnormal_behavior = data.get('abnormal_behavior')
page = data.get('page', 1)
page_size = data.get('page_size', 10)
if page: # 判断请求中是否有page和size参数
request.query_params._mutable = True # 让查询参数dict变为可编辑
# query_params该参数会返回请求的查询参数是个dict _mutable属性表示是否可编辑默认是False
request.query_params['page'] = page # 添加page查询参数
if page_size:
request.query_params['page_size'] = page_size # 添加size查询参数
request.query_params._mutable = False
if start_time and end_time:
start_date = datetime.datetime.strptime(start_time, '%Y-%m-%d').date()
end_date = datetime.datetime.strptime(end_time, '%Y-%m-%d').date()
videos = Video.objects.filter(video_date__range=(start_date, end_date), tpres__isnull=False, status=2).distinct()
else:
# videos = Video.objects.all()
videos = Video.objects.filter(tpres__isnull=False, status=2).distinct()
if abnormal_behavior:
videos = videos.filter(tpres__abnormal_behavior__in=abnormal_behavior).distinct()
paginator = MyPageNumberPagination()
# paginator.page = page
# paginator.page_size = page_size
paginated_queryset = paginator.paginate_queryset(videos, request)
serializer = AnalyzedVideoSerializer(paginated_queryset, many=True)
paginator_response = paginator.get_paginated_response(serializer.data)
results = list(paginator_response.data['results'])
for item in results:
video_id = item.get('id')
abnormal_count = TPRes.objects.filter(video_id=video_id).count()
abnormal_performance = list(TPRes.objects.filter(video_id=video_id).values_list('abnormal_behavior', flat=True).distinct())
item['abnormal_count'] = abnormal_count
item['abnormal_performance'] = abnormal_performance
logger.info(paginator_response.data)
response_data = {
'success': True,
'count': paginator_response.data['count'],
'next': paginator_response.data['next'],
'previous': paginator_response.data['previous'],
'results': results,
}
return Response(response_data)
class VideoAnalysis(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
# TODO 替换路径
not_analyzed_videos = Video.objects.filter(status=0, download_status=2)
paginator = MyPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(not_analyzed_videos, request)
serializer = AnalyzedVideoSerializer(paginated_queryset, many=True)
paginator_response = paginator.get_paginated_response(serializer.data)
results = list(paginator_response.data['results'])
for item in results:
item['video_path'] = item.pop('video_path').replace('/home/TP_922/TP_PMP', '..')
response_data = {
'success': True,
'count': paginator_response.data['count'],
'next': paginator_response.data['next'],
'previous': paginator_response.data['previous'],
# 'results': list(paginator_response.data['results']),
'results': results,
}
return Response(response_data)
class UpdateVideoStatus(viewsets.ModelViewSet):
def update(self, request, *args, **kwargs):
data = request.data
video_hash = data.get('video_hash')
video_id = data.get('video_id')
video_status = data.get('video_status')
msg = data.get('msg')
if video_hash:
video_obj = Video.objects.filter(video_hash=video_hash).first()
else:
video_obj = Video.objects.filter(id=video_id).first()
if not video_obj:
return Response({'success': False, 'msg': '該視頻不存在'})
try:
with transaction.atomic():
video_obj.status = video_status
video_obj.error_reason = msg
video_obj.save()
return Response({'success': True, 'msg': '更新成功'})
except Exception as e:
return Response({'success': False, 'msg': f'更新失敗,原因{e}'})
class TaskViewSet(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
task_objs = Task.objects.filter(analysis_status__in=[101, 100], video__download_status=2).exclude(video__video_status=4).order_by("pk")
paginator = MyPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(task_objs, request)
serializer = TaskSerializer(paginated_queryset, many=True)
paginator_response = paginator.get_paginated_response(serializer.data)
results = list(paginator_response.data['results'])
for item in results:
video_id = item.pop('video')
item['video_name'] = Video.objects.filter(id=video_id).first().video_name
item['video_path'] = Video.objects.filter(id=video_id).first().video_path
task_object = Task.objects.filter(video_id=video_id).first()
task_object.analysis_status = 101
task_object.save()
response_data = {
'success': True,
'count': paginator_response.data['count'],
'next': paginator_response.data['next'],
'previous': paginator_response.data['previous'],
'results': results,
}
return Response(response_data)
def create(self, request, *args, **kwargs):
pass
def update(self, request, *args, **kwargs):
data = request.data
logger.info(data)
task_id = data.get('taskId')
analysis_status = data.get('status')
message = data.get('msg')
res = data.get('total_res')
task_obj = Task.objects.filter(taskId=task_id).first()
callbackUrl = task_obj.callbackUrl
if not task_obj:
return Response({'msg': '该任务不存在'})
if analysis_status == 102:
# s = Search(index='tp-log')
# s = s.params(size=1000)
# response = s.execute()
result = list()
# # 遍历结果
# for hit in response:
# data = hit.to_dict().get('data')
# if data.get('taskId') == task_id:
# result.append(data)
if res.get('taskId') == task_id:
result.append(res)
if result and callbackUrl:
latest_data = result[-1]
res = {
'status': 0,
'message': 'success',
'data': {
'taskId': task_id,
'videoStatus': latest_data.get('data').get('videoStatus'),
'invalidReason': latest_data.get('data').get('invalidReason'),
'statistics': latest_data.get('data').get('statistics'),
'result': latest_data.get('data').get('result'),
"resultTime": latest_data.get('data').get('resultTime')
}
}
try:
response = requests.post(callbackUrl, json=res)
# 检查响应状态码
if response.status_code == 200:
logger.info("回调请求发送成功!")
else:
logger.info(f"请求失败,状态码: {response.status_code}")
except requests.RequestException as e:
logger.info("请求发生异常:", e)
try:
with transaction.atomic():
task_obj.analysis_status = analysis_status
task_obj.error_reason = message
task_obj.save()
return Response({'success': True, 'msg': '更改任务状态成功'})
except Exception as e:
return Response({'success': False, 'msg': f'更新失败, {e}'})
video_download_path = settings.VIDEO_DOWNLOAD_PATH
class DownloadVideo(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
data = request.data
fileUrl = data.get('fileUrl')
callbackUrl = data.get('callbackUrl')
filename = fileUrl.split('/')[-1]
download_path = os.path.join(video_download_path, filename)
if os.path.exists(download_path):
file_stem = Path(download_path).stem
file_suffix = Path(download_path).suffix
download_path = os.path.join(video_download_path, f"{file_stem}_{str(time.time()).split('.')[1]}{file_suffix}")
# 新增同一视频直接返回结果
video_obj = Video.objects.filter(ftp_path=fileUrl, download_status=2).last()
if video_obj:
video_id = video_obj.id
task_obj = Task.objects.filter(video_id=video_id).last()
if task_obj:
logger.info(f"该链接{fileUrl}任务已存在,任务结果为{task_obj.analysis_status}")
# 有分析已完成任务
if task_obj.analysis_status == 102:
response = VideoResult.get_data_from_es(taskId=task_obj.taskId)
if response.get("message") == "success":
logger.info(f"es中获取数据taskId: {task_obj.taskId}")
return Response(response)
else:
res = {}
res['status'] = 0000
res['message'] = 'success'
res['data'] = {'taskId': task_obj.taskId}
return Response(res)
# 有未分析或正在分析任务
elif task_obj.analysis_status in (100, 101):
print(222, task_obj.taskId)
res = {}
res['status'] = 0000
res['message'] = 'success'
res['data'] = {'taskId': task_obj.taskId}
return Response(res)
res = {'status': 400, 'message': 'false'}
# video_hash = get_file_hash(download_path)
# if video_hash:
# duplicate_video = Video.objects.filter(video_hash=video_hash).last()
# if duplicate_video:
# task_obj = Task.objects.filter(video_id=duplicate_video.id).last()
# logger.warning(f"当前下载视频{download_path}与视频{duplicate_video.video_name}的hash值相同")
# if task_obj.analysis_status == 102:
# response = VideoResult.get_data_from_es(taskId=task_obj.taskId)
# if response.get("message") == "success":
# return Response(response)
# return Response({'taskId': task_obj.taskId, 'status': 0000, 'message': 'success'})
# else:
# return Response({'taskId': task_obj.taskId, 'status': 0000, 'message': 'success'})
try:
with transaction.atomic():
video_obj = Video.objects.create(video_name=filename, video_path=download_path, status=0, download_status=0, ftp_path=fileUrl)
video_id = video_obj.id
task_obj = Task.objects.create(analysis_status=100, video_id=video_id, callbackUrl=callbackUrl)
taskId = task_obj.taskId
except Exception as e:
logger.info(e)
return Response(res)
res['status'] = 0000
res['message'] = 'success'
res['data'] = {'taskId': taskId}
logger.info(f"当前链接{fileUrl}任务创建成功任务id为{taskId}")
return Response(res)
class VideoResult(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
query_params = dict(request.query_params)
logger.info(query_params)
if query_params.get('taskId'):
taskId = query_params.get('taskId')[0]
taskId = int(taskId) if isinstance(taskId, str) else taskId
else:
return Response({'msg': '请携带taskId'})
task = get_data_by_model(Task, {'taskId': taskId})
if task:
# task = get_data_by_model(Video, {'taskId': taskId})
if task.video.video_status == 4:
duplicate_video = Video.objects.filter(video_hash=task.video.video_hash).last()
if duplicate_video:
task_obj = Task.objects.filter(video_id=duplicate_video.id).last()
taskId = task_obj.taskId
res = self.get_data_from_es(taskId)
return Response(res)
@classmethod
def get_data_from_es(cls, taskId):
task_obj = Task.objects.filter(taskId=taskId).first()
analysis_status = task_obj.analysis_status if task_obj else 106
try:
s = Search(index='tp-log')
s = s.from_dict({
"query": {
"match": {
"data.@fields.taskId": task_obj.taskId
}
},
"size": 1
}).sort({"@timestamp": {"order": "desc"}})
response = s.execute()
if not response:
return {'status': analysis_status, 'message': '无分析结果,请查看状态码'}
data = response.hits[0].to_dict().get('data').get('@fields')
res = {
'status': 0,
'message': 'success',
'data': {
'videoStatus': data.get('data').get('videoStatus'),
'invalidReason': data.get('data').get('invalidReason'),
'statistics': data.get('data').get('statistics'),
'result': data.get('data').get('result'),
"resultTime": data.get('data').get('resultTime'),
"taskId": data.get('taskId'),
}
}
return res
except Exception as e:
return {'status': analysis_status, 'message': f'获取视频解析结果失败,原因{e}'}

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

@ -0,0 +1,6 @@
from django.apps import AppConfig
class DepartmentConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'department'

@ -0,0 +1,27 @@
# Generated by Django 3.2.19 on 2023-09-22 09:47
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Department',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, verbose_name='部门名称')),
('parent_id', models.IntegerField(blank=True, null=True, verbose_name='上级部门id')),
('status', models.IntegerField(default=1, verbose_name='状态')),
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
],
options={
'db_table': 'tp_department',
},
),
]

@ -0,0 +1,14 @@
from django.db import models
# Create your models here.
class Department(models.Model):
name = models.CharField(max_length=128, verbose_name='部门名称')
parent_id = models.IntegerField(verbose_name='上级部门id', null=True, blank=True)
status = models.IntegerField(verbose_name='状态', default=1)
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间', null=True, blank=True)
class Meta:
db_table = 'tp_department'

@ -0,0 +1,9 @@
from rest_framework import serializers
from .models import Department
class DepartmentSerializer(serializers.ModelSerializer):
class Meta:
model = Department
fields = "__all__"

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -0,0 +1,11 @@
from django.urls import path
from .views import DepartmentView
urlpatterns = [
path('', DepartmentView.as_view({'get': 'list', 'post': 'create'})),
path('<pk>/', DepartmentView.as_view({'put': 'update', 'delete': 'destroy'})),
# path('query_depart_users/', DepartmentView.as_view({'post': 'query_department_users'})),
# path('son_departments/', DepartmentView.as_view({'post': 'create_son_department'})),
]

@ -0,0 +1,159 @@
import logging
from django.db import transaction
from django.db.models import Q
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import viewsets
from app.pagination import MyPageNumberPagination
from user.models import UserProfile
from .models import Department
from .serializers import DepartmentSerializer
# Create your views here.
logger = logging.getLogger('mylogger')
class DepartmentView(viewsets.GenericViewSet):
def list(self, request, *args, **kwargs):
data = dict()
try:
parent_departments = list(Department.objects.filter(parent_id__isnull=True).values())
for parent_department in parent_departments:
parent_id = parent_department.get('id')
son_departs = list(Department.objects.filter(parent_id=parent_id).values())
parent_department['children'] = son_departs
data['data'] = parent_departments
return Response(data)
except Exception as e:
return Response({'msg': f'查询失败, {e}'})
def query_department_users(self, request, *args, **kwargs):
res = dict()
data = request.data
department_id = data.get('department_id')
try:
users = list(UserProfile.objects.filter(department_id=department_id).values('id', 'username', 'phone_number', 'status', 'department_id'))
res['data'] = users
return Response(res)
except Exception as e:
return Response({'msg': f'查询失败,原因{e}'})
# def list_son_departments(self, request, *args, **kwargs):
# query_params = dict(request.query_params)
# parent_name = query_params.get('parent_name')
# if isinstance(parent_name, list):
# parent_name = ''.join(parent_name)
# logger.info(parent_name)
# parent_obj = Department.objects.filter(name=parent_name).first()
# if not parent_obj:
# return Response({'msg': '非法请求,该父级部门不存在'})
# parent_id = parent_obj.id
# queryset = Department.objects.filter(parent_id=parent_id).order_by('-id')
# paginator = MyPageNumberPagination()
# paginated_queryset = paginator.paginate_queryset(queryset, request)
# serializer = DepartmentSerializer(paginated_queryset, many=True)
#
# return paginator.get_paginated_response(serializer.data)
# def create(self, request, *args, **kwargs):
# data = request.data
# parent_depart_name = data.get('parent_department')
# if not parent_depart_name:
# return Response({"msg": "请输入父级部门名称"})
# parent_obj = Department.objects.filter(name=parent_depart_name).first()
# if parent_obj:
# return Response({'msg': '该部门已存在,请换一个'})
# try:
# with transaction.atomic():
# new_obj = Department.objects.create(name=parent_depart_name)
# return Response({'msg': '创建父级部门成功'})
# except Exception as e:
# return Response({'msg': f'创建失败,原因{e}'})
# def create(self, request, *args, **kwargs):
# data = request.data
# parent_name = data.get('parent_department_name')
# if not parent_name:
# return Response({'msg': '请输入父级部门名称'})
# depart_name = data.get('department_name')
# try:
# with transaction.atomic():
# if not depart_name:
# old_parent_obj = Department.objects.filter(name=parent_name).first()
# if old_parent_obj:
# return Response({'msg': '该父级部门名称已存在,请换一个'})
# parent_obj = Department.objects.create(name=parent_name)
# return Response({'msg': '创建父级部门成功'})
# else:
# old_parent_obj = Department.objects.filter(name=parent_name).first()
# if not old_parent_obj:
# old_parent_obj = Department.objects.create(name=parent_name)
# old_depart_obj = Department.objects.filter(name=depart_name, parent_id=old_parent_obj.id).first()
# if old_depart_obj:
# return Response({'msg': f'{parent_name}下已有{depart_name}啦,请换一个'})
# new_depart_obj = Department.objects.create(name=depart_name, parent_id=old_parent_obj.id)
# return Response({'msg': '创建子部门成功'})
# except Exception as e:
# return Response({'msg': f'创建失败,{e}'})
def create(self, request, *args, **kwargs):
data = request.data
department_name = data.get('department_name')
department_id = data.get('department_id')
if not department_id or department_id == '':
old_depart_obj = Department.objects.filter(name=department_name, parent_id__isnull=True).first()
if old_depart_obj:
return Response({'msg': f'该父级部门{department_name}已存在'})
depart_obj = Department.objects.create(name=department_name)
return Response({'msg': f'创建父级部门{department_name}成功', 'success': True})
else:
parent_obj = Department.objects.filter(id=department_id, parent_id__isnull=True).first()
if not parent_obj:
return Response({'msg': '非法请求'})
old_son_obj = Department.objects.filter(name=department_name, parent_id=department_id).first()
if old_son_obj:
return Response({'msg': f'该父级部门{parent_obj.name}下已有{old_son_obj.name}'})
son_obj = Department.objects.create(name=department_name, parent_id=department_id)
return Response({'msg': f'创建父级部门{parent_obj.name}{son_obj.name}成功', 'success': True})
# def create_son_department(self, request, *args, **kwargs):
# data = request.data
# parent_name = data.get('parent_name')
# if not parent_name:
# return Response({'msg': '请输入父级部门名称以便创建子部门'})
# parent_obj = Department.objects.filter(name=parent_name).first()
# parent_id = parent_obj.id
# department_name = data.get('department_name')
# if not department_name:
# return Response({'msg': '请输入子部门名称'})
# depart_obj = Department.objects.filter(name=department_name, parent_id=parent_id).first()
# if depart_obj:
# return Response({'msg': f'{parent_name}下已有{department_name},请换一个'})
# try:
# with transaction.atomic():
# Department.objects.create(name=department_name, parent_id=parent_id)
# return Response({'msg': '创建成功'})
# except Exception as e:
# return Response({'msg': f'创建失败,原因{e}'})
def update(self, request, pk, *args, **kwargs):
data = request.data
try:
with transaction.atomic():
depart_obj = Department.objects.filter(id=pk).update(**data)
return Response({'msg': '更新成功', 'success': True})
except Department.DoesNotExist:
return Response({'msg': '该部门不存在'})
except Exception as e:
return Response({'msg': f'更新失败,原因{e}'})
def destroy(self, request, pk, *args, **kwargs):
try:
with transaction.atomic():
depart_obj = Department.objects.filter(id=pk).delete()
return Response({'msg': '删除成功', 'success': True})
except Department.DoesNotExist:
return Response({'msg': '该部门不存在'})
except Exception as e:
return Response({'msg': f'删除失败,原因{e}'})

@ -0,0 +1,33 @@
# 输出查询结果])
from datetime import datetime
from elasticsearch import Elasticsearch
# 连接到 Elasticsearch
client = Elasticsearch("http://localhost:9209", basic_auth=("elastic", "changeme"))
start_date = datetime(2024, 1, 1) # 开始时间
end_date = datetime(2024, 12, 31) # 结束时间
# 构建查询体
query = {
"query": {
"range": {
"@timestamp": {
"gte": start_date.strftime("%Y-%m-%dT%H:%M:%S"),
"lte": end_date.strftime("%Y-%m-%dT%H:%M:%S")
}
}
},
"size": 10000
}
# 执行查询操作
response = client.search(index="tp-log", body=query)
# 打印结果
print("Found {} documents:".format(response['hits']['total']['value']))
result = client.delete_by_query(index="tp-log", body=query)
# 打印删除结果
print("Deleted:", result)

@ -0,0 +1,5 @@
from django.contrib import admin
# Register your models here.
from event.models import Event
admin.site.register(Event)

@ -0,0 +1,6 @@
from django.apps import AppConfig
class EventConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'event'

@ -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",)

@ -0,0 +1,27 @@
# Generated by Django 3.2.19 on 2023-09-22 09:47
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'],
},
),
]

@ -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']

@ -0,0 +1,8 @@
from rest_framework import serializers
from event.models import Event
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = "__all__"

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

@ -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)),
]

@ -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

@ -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', 'TP_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()

@ -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/') or path_info.endswith('nanjing/'):
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

@ -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

Binary file not shown.

@ -0,0 +1,9 @@
#!/bin/bash
echo "Appling database migrations..."
sleep 5
python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000&&
echo 'TP项目启动完成'
tail -f /dev/null
exec "$@"

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

@ -0,0 +1,6 @@
from django.apps import AppConfig
class UserConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'user'

@ -0,0 +1,51 @@
# Generated by Django 3.2.19 on 2023-09-22 09:47
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
('department', '0001_initial'),
]
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=1, verbose_name='状态')),
('recorder_number', models.CharField(blank=True, max_length=256, null=True, verbose_name='执法记录仪编号')),
('department', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='department.department')),
('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()),
],
),
]

@ -0,0 +1,28 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from department.models import Department
# Create your models here.
# class Department(models.Model):
# department_name = models.CharField(max_length=128, verbose_name='部门名称')
# # parent_id = models.IntegerField(verbose_name='上级部门id', null=True, blank=True)
# # status = models.IntegerField(verbose_name='状态', default=1)
#
# class Meta:
# db_table = 'user_department'
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=1)
recorder_number = models.CharField(max_length=256, verbose_name='执法记录仪编号', null=True, blank=True)
department = models.ForeignKey(Department, on_delete=models.CASCADE, null=True)
class Meta:
db_table = 'tp_user'

@ -0,0 +1,12 @@
from rest_framework import serializers
from .models import UserProfile
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
# fields = "__all__"
exclude = ('password',)

@ -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)

@ -0,0 +1,13 @@
from django.urls import path
from .views import UserViewSet
urlpatterns = [
path('', UserViewSet.as_view({"get": "query_users", "post": "create"})),
path('login/', UserViewSet.as_view({"post": "tp_login"})),
path('<pk>/', UserViewSet.as_view({"put": "update_user", "delete": "delete_user"})),
# path('set_user_role', UserViewSet.as_view({"post": "set_user_role"})),
# path('role/', RoleView.as_view()),
# path('role/<pk>/', RoleView.as_view())
]

@ -0,0 +1,13 @@
def update_user_querydict(querydict):
new_querydict = dict()
if 'username' in querydict:
new_querydict['username__icontains'] = ''.join(querydict.pop('username'))
if 'phone_number' in querydict and querydict.get('phone_number') != ['']:
new_querydict['phone_number__icontains'] = ''.join(querydict.pop('phone_number'))
if 'status' in querydict and querydict.get('status') != ['']:
new_querydict['status'] = ''.join(querydict.pop('status'))
return new_querydict

@ -0,0 +1,242 @@
import json
import logging
from django.contrib.auth import authenticate
from django.contrib.auth.hashers import make_password
from django.db import transaction
from django.shortcuts import render
# Create your views here.
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth.models import Group
from app.pagination import MyPageNumberPagination
from app.utils import generate_token, decode_token_exp_time
from department.models import Department
from .models import UserProfile
from .serializers import UserSerializer
from .utils import update_user_querydict
logger = logging.getLogger('mylogger')
class UserViewSet(viewsets.GenericViewSet):
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 and login_user.status == 1:
# 生成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 create(self, request, *args, **kwargs):
"""注册 POST 用户名 手机号 状态 性别 部门"""
data = request.data
username = data.get('username')
if not username:
return Response({'msg': '用户名不能为空'})
phone_number = data.get('phone_number')
gender = data.get('gender')
password = data.get('password', '#Yaxin0504')
# role = data.get('role')
# role_obj = Group.objects.filter(name=role).first()
department_id = data.get('department_id')
department_obj = Department.objects.filter(id=department_id).first()
status = data.get('status', 1)
try:
if UserProfile.objects.filter(username=username).first():
return Response({'msg': '该用户名已存在,请换一个'})
user = UserProfile.objects.create_user(username=username, password=password, department=department_obj, status=status, gender=gender, phone_number=phone_number)
# user.groups.add(role_obj)
# user.save()
return Response({'msg': '添加成功', 'success': True})
except Exception as e:
response = {
"msg": f'添加失败, 原因:{e}'
}
return Response(response)
def query_users(self, request, *args, **kwargs):
"""查询所有用户,默认全部,可根据部门查询"""
query_params = dict(request.query_params)
logger.info(query_params)
querydict = update_user_querydict(query_params)
logger.info(querydict)
if query_params.get('department_id') and query_params.get('department_id') != ['']:
department_id = ''.join(query_params.get('department_id'))
depart_obj = Department.objects.filter(id=department_id).first()
if not depart_obj.parent_id:
# 父级部门
dp_ids = list(Department.objects.filter(parent_id=depart_obj.id).values_list('id', flat=True).distinct())
else:
dp_ids = [depart_obj.id]
queryset = UserProfile.objects.filter(department_id__in=dp_ids, **querydict).order_by('-id')
else:
queryset = UserProfile.objects.filter(**querydict).order_by('-id')
paginator = MyPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = UserSerializer(paginated_queryset, many=True)
paginator_response = paginator.get_paginated_response(serializer.data)
response_data = {
'count': paginator_response.data['count'],
'next': paginator_response.data['next'],
'previous': paginator_response.data['previous'],
'results': list(paginator_response.data['results']),
}
results = response_data.get('results')
res_list = json.loads(json.dumps(results))
for res_dict in res_list:
department_obj = Department.objects.filter(id=res_dict.get('department')).first()
if department_obj:
res_dict['department_name'] = department_obj.name
res_dict['department_id'] = res_dict.pop('department')
else:
res_dict['department_name'] = '暂无部门'
res_dict['department_id'] = '暂无'
response_data['results'] = res_list
return Response(response_data)
# def set_user_role(self, request, *args, **kwargs):
# try:
# user_id = request.data.get('user_id')
# user = UserProfile.objects.get(id=user_id)
# group = Group.objects.get(name=request.data.get('role_name'))
# is_member = group.user_set.filter(id=user.id).exists()
# if is_member:
# return Response({'msg': f'用户{user.username}已经是{group.name}啦'})
# user.groups.add(group)
# user.save()
# return Response({'msg': '修改用户角色成功'})
# except UserProfile.DoesNotExist:
# return Response({'msg': '该用户不存在'})
# except Group.DoesNotExist:
# return Response({'msg': '该角色不存在'})
# except Exception as e:
# return Response({'msg': f'更改用户角色失败: {e}'})
def update_user(self, request, pk, *args, **kwargs):
data = request.data
if 'username' in data:
user_obj = UserProfile.objects.exclude(id=pk).filter(username=data.get('username')).first()
if user_obj:
return Response({'msg': '更新失败,该用户名已存在,请换一个'})
# user_id = data.pop('user_id', None)
if 'password' in data:
# hashed_pwd = make_password(data.get('password'))
data['password'] = make_password(data.pop('password'))
try:
with transaction.atomic():
user = UserProfile.objects.filter(id=pk).update(**data)
return Response({'msg': '更新成功', 'success': True})
except UserProfile.DoesNotExist:
return Response({'msg': '该用户不存在'})
except Exception as e:
return Response({'msg': f'更新失败,原因{e}'})
def delete_user(self, request, pk, *args, **kwargs):
try:
with transaction.atomic():
user = UserProfile.objects.filter(id=pk).delete()
return Response({'msg': '删除成功', 'success': True})
except UserProfile.DoesNotExist:
return Response({'msg': '该用户不存在'})
except Exception as e:
return Response({'msg': f'删除失败,原因{e}'})
# class RoleView(APIView):
# def get(self, request):
# queryset = Group.objects.all().order_by('-id')
# paginator = MyPageNumberPagination()
# paginated_queryset = paginator.paginate_queryset(queryset, request)
#
# results = []
# for group in paginated_queryset:
# results.append({
# 'id': group.id,
# 'name': group.name,
# })
#
# response = {
# 'count': paginator.page.paginator.count,
# 'next': paginator.get_next_link(),
# 'previous': paginator.get_previous_link(),
# 'results': results
# }
#
# return Response(response)
#
# def post(self, request, *args, **kwargs):
# """添加角色"""
# data = request.data
# group_name = data.get('role_name')
# try:
# old_group = Group.objects.filter(name=group_name).first()
# if old_group:
# return Response({"msg": "该角色名已存在"})
# with transaction.atomic():
# group = Group.objects.create(name=group_name)
# group.save()
# return Response({"msg": "创建成功"})
# except Exception as e:
# return Response({"msg": f"创建失败:{e}"})
#
# #
# def put(self, request, pk):
# group_name = request.data.get('role_name')
# try:
# with transaction.atomic(): # 开启事务
# group = Group.objects.get(id=pk)
# group.name = group_name
# group.save()
# return Response({'success': True, 'msg': 'Group updated successfully'})
# except Group.DoesNotExist:
# return Response({'success': False, 'msg': 'Group not found'})
# except Exception as e:
# return Response({'success': False, 'msg': str(e)})
#
# def delete(self, request, pk):
# try:
# with transaction.atomic(): # 开启事务
# group = Group.objects.get(id=pk)
# group.delete()
# return Response({'success': True, 'msg': 'Group deleted successfully'})
# except Group.DoesNotExist:
# return Response({'success': False, 'msg': 'Group not found'})
# except Exception as e:
# return Response({'success': False, 'msg': str(e)})
#
# def modify_permission(self, request, *args, **kwargs):
# pass

@ -0,0 +1,46 @@
[uwsgi]
project=myproject
#uid=www-data
#gid=www-data
base=/home
chdir=%(base)/%(project)
module=TP_API.wsgi:application
master=True
processes=3
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
enable-threads = true
workers = 3

@ -0,0 +1,192 @@
#!/bin/sh
# The MIT License (MIT)
#
# Copyright (c) 2017 Eficode Oy
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
VERSION="2.2.4"
set -- "$@" -- "$TIMEOUT" "$QUIET" "$PROTOCOL" "$HOST" "$PORT" "$result"
TIMEOUT=15
QUIET=0
# The protocol to make the request with, either "tcp" or "http"
PROTOCOL="tcp"
echoerr() {
if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
}
usage() {
exitcode="$1"
cat << USAGE >&2
Usage:
$0 host:port|url [-t timeout] [-- command args]
-q | --quiet Do not output any status messages
-t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout
Defaults to 15 seconds
-v | --version Show the version of this tool
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit "$exitcode"
}
wait_for() {
case "$PROTOCOL" in
tcp)
if ! command -v nc >/dev/null; then
echoerr 'nc command is missing!'
exit 1
fi
;;
http)
if ! command -v wget >/dev/null; then
echoerr 'wget command is missing!'
exit 1
fi
;;
esac
TIMEOUT_END=$(($(date +%s) + TIMEOUT))
while :; do
case "$PROTOCOL" in
tcp)
nc -w 1 -z "$HOST" "$PORT" > /dev/null 2>&1
;;
http)
wget --timeout=1 --tries=1 -q "$HOST" -O /dev/null > /dev/null 2>&1
;;
*)
echoerr "Unknown protocol '$PROTOCOL'"
exit 1
;;
esac
result=$?
if [ $result -eq 0 ] ; then
if [ $# -gt 7 ] ; then
for result in $(seq $(($# - 7))); do
result=$1
shift
set -- "$@" "$result"
done
TIMEOUT=$2 QUIET=$3 PROTOCOL=$4 HOST=$5 PORT=$6 result=$7
shift 7
exec "$@"
fi
exit 0
fi
if [ $TIMEOUT -ne 0 -a $(date +%s) -ge $TIMEOUT_END ]; then
echo "Operation timed out" >&2
exit 1
fi
sleep 1
done
}
while :; do
case "$1" in
http://*|https://*)
HOST="$1"
PROTOCOL="http"
shift 1
;;
*:* )
HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
shift 1
;;
-v | --version)
echo $VERSION
exit
;;
-q | --quiet)
QUIET=1
shift 1
;;
-q-*)
QUIET=0
echoerr "Unknown option: $1"
usage 1
;;
-q*)
QUIET=1
result=$1
shift 1
set -- -"${result#-q}" "$@"
;;
-t | --timeout)
TIMEOUT="$2"
shift 2
;;
-t*)
TIMEOUT="${1#-t}"
shift 1
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
break
;;
--help)
usage 0
;;
-*)
QUIET=0
echoerr "Unknown option: $1"
usage 1
;;
*)
QUIET=0
echoerr "Unknown argument: $1"
usage 1
;;
esac
done
if ! [ "$TIMEOUT" -ge 0 ] 2>/dev/null; then
echoerr "Error: invalid timeout '$TIMEOUT'"
usage 3
fi
case "$PROTOCOL" in
tcp)
if [ "$HOST" = "" ] || [ "$PORT" = "" ]; then
echoerr "Error: you need to provide a host and port to test."
usage 2
fi
;;
http)
if [ "$HOST" = "" ]; then
echoerr "Error: you need to provide a host to test."
usage 2
fi
;;
esac
wait_for "$@"
Loading…
Cancel
Save