From a40b475866f46daf50222d99a6861966fbf71d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=BD=E5=91=A8=E6=98=93?= <2745525394@qq.com> Date: Sun, 9 Nov 2025 22:33:59 +0800 Subject: [PATCH] =?UTF-8?q?owntracks=E9=83=A8=E5=88=86=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E6=BA=90=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DjangoBlog/owntracks/admin.py | 6 +- src/DjangoBlog/owntracks/apps.py | 4 +- .../owntracks/migrations/0001_initial.py | 24 +- ...0002_alter_owntracklog_options_and_more.py | 20 +- src/DjangoBlog/owntracks/models.py | 16 +- src/DjangoBlog/owntracks/owntracks | 0 src/DjangoBlog/owntracks/tests.py | 93 +++--- src/DjangoBlog/owntracks/urls.py | 10 + src/DjangoBlog/owntracks/views.py | 264 ++++++++++-------- 9 files changed, 272 insertions(+), 165 deletions(-) create mode 100644 src/DjangoBlog/owntracks/owntracks diff --git a/src/DjangoBlog/owntracks/admin.py b/src/DjangoBlog/owntracks/admin.py index 655b535..91e6673 100644 --- a/src/DjangoBlog/owntracks/admin.py +++ b/src/DjangoBlog/owntracks/admin.py @@ -1,7 +1,9 @@ +# 导入Django管理后台模块 #zqx: 引入Django的admin模块,用于注册和管理模型 from django.contrib import admin -# Register your models here. - +# 注册你的模型到管理后台(待实现) #zqx: 这是一个占位注释,提示需要注册模型到管理后台 +# 定义OwnTrackLogs模型在Django管理后台中的配置类 #zqx: 创建OwnTrackLogsAdmin类,继承自ModelAdmin,用于配置OwnTrackLog模型在管理后台的行为 class OwnTrackLogsAdmin(admin.ModelAdmin): + # 目前为空,后续可以添加管理后台的自定义配置 #zqx: 当前类体为空,预留空间用于添加管理后台的自定义配置选项 pass diff --git a/src/DjangoBlog/owntracks/apps.py b/src/DjangoBlog/owntracks/apps.py index 1bc5f12..ec8282d 100644 --- a/src/DjangoBlog/owntracks/apps.py +++ b/src/DjangoBlog/owntracks/apps.py @@ -1,5 +1,7 @@ +# 导入Django应用程序配置基类 #zqx: 引入Django的AppConfig基类,用于创建应用配置类 from django.apps import AppConfig - +# 定义owntracks应用的配置类,继承自AppConfig #zqx: 创建OwntracksConfig类,继承自AppConfig,用于配置owntracks应用 class OwntracksConfig(AppConfig): + # 设置应用的名称为'owntracks' #zqx: 设置name属性为'owntracks',指定应用的名称 name = 'owntracks' diff --git a/src/DjangoBlog/owntracks/migrations/0001_initial.py b/src/DjangoBlog/owntracks/migrations/0001_initial.py index 9eee55c..6596a45 100644 --- a/src/DjangoBlog/owntracks/migrations/0001_initial.py +++ b/src/DjangoBlog/owntracks/migrations/0001_initial.py @@ -1,31 +1,51 @@ -# Generated by Django 4.1.7 on 2023-03-02 07:14 +# Generated by Django 4.1.7 on 2023-03-02 07:14 #zqx: 由Django 4.1.7在2023年3月2日7点14分自动生成的迁移文件 +# 导入Django数据库迁移模块和模型模块 #zqx: 引入Django的数据迁移和模型相关模块 from django.db import migrations, models +# 导入Django的时区工具模块 #zqx: 引入Django的时区工具模块,用于处理时间相关功能 import django.utils.timezone +# 定义一个迁移类,继承自Django的Migration基类 #zqx: 定义迁移类,继承自Django的Migration基类 class Migration(migrations.Migration): - + # 标记这是一个初始迁移 #zqx: 设置initial属性为True,标记这是应用的初始迁移 initial = True + # 定义依赖关系,此处为空列表表示没有依赖其他迁移 #zqx: 定义此迁移所依赖的其他迁移,空列表表示无依赖 dependencies = [ ] + # 定义具体的迁移操作 #zqx: 定义本次迁移需要执行的操作列表 operations = [ + # 创建一个新的数据模型 #zqx: 使用CreateModel操作创建新的数据表 migrations.CreateModel( + # 模型名称为'OwnTrackLog' #zqx: 指定要创建的模型名称为OwnTrackLog name='OwnTrackLog', + # 定义模型的字段 #zqx: 定义模型中的各个字段及其属性 fields=[ + # 主键字段,自动创建的BigAutoField类型 #zqx: 定义主键字段,使用BigAutoField类型并设置为自动创建 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + # 用户标识字段,最大长度100的字符字段 #zqx: 定义tid字段,为CharField类型,最大长度100,显示名为"用户" ('tid', models.CharField(max_length=100, verbose_name='用户')), + # 纬度字段,浮点数类型 #zqx: 定义lat字段,为FloatField类型,显示名为"纬度" ('lat', models.FloatField(verbose_name='纬度')), + # 经度字段,浮点数类型 #zqx: 定义lon字段,为FloatField类型,显示名为"经度" ('lon', models.FloatField(verbose_name='经度')), + # 创建时间字段,默认值为当前时区时间 #zqx: 定义created_time字段,为DateTimeField类型,默认值为当前时区时间,显示名为"创建时间" ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), ], + # 模型的元数据选项配置 #zqx: 配置模型的元数据选项 options={ + # 单数形式的可读名称 #zqx: 设置模型在单数情况下的可读名称为"OwnTrackLogs" 'verbose_name': 'OwnTrackLogs', + # 复数形式的可读名称 #zqx: 设置模型在复数情况下的可读名称为"OwnTrackLogs" 'verbose_name_plural': 'OwnTrackLogs', + # 数据查询时的默认排序方式,按创建时间升序排列 #zqx: 设置查询结果默认按照created_time字段升序排列 'ordering': ['created_time'], + # 定义获取最新记录时使用的字段 #zqx: 设置获取最新记录时使用created_time字段作为判断依据 'get_latest_by': 'created_time', }, ), ] + + diff --git a/src/DjangoBlog/owntracks/migrations/0002_alter_owntracklog_options_and_more.py b/src/DjangoBlog/owntracks/migrations/0002_alter_owntracklog_options_and_more.py index b4f8dec..433840c 100644 --- a/src/DjangoBlog/owntracks/migrations/0002_alter_owntracklog_options_and_more.py +++ b/src/DjangoBlog/owntracks/migrations/0002_alter_owntracklog_options_and_more.py @@ -1,22 +1,30 @@ # Generated by Django 4.2.5 on 2023-09-06 13:19 +# Generated by Django 4.2.5 on 2023-09-06 13:19 #zqx: 由Django 4.2.5在2023年9月6日13点19分自动生成的迁移文件 +# 导入Django数据库迁移模块 #zqx: 引入Django的数据迁移模块 from django.db import migrations +# 定义数据库迁移类,继承自Django的Migration基类 #zqx: 定义迁移类,继承自Django的Migration基类 class Migration(migrations.Migration): - + # 定义迁移依赖关系,此迁移依赖于owntracks应用的0001_initial迁移文件 #zqx: 设置此迁移依赖于owntracks应用的0001_initial迁移 dependencies = [ ('owntracks', '0001_initial'), ] + # 定义具体的迁移操作列表 #zqx: 定义本次迁移需要执行的操作列表 operations = [ + # 修改OwnTrackLog模型的选项配置 #zqx: 使用AlterModelOptions操作修改OwnTrackLog模型的选项配置 migrations.AlterModelOptions( - name='owntracklog', - options={'get_latest_by': 'creation_time', 'ordering': ['creation_time'], 'verbose_name': 'OwnTrackLogs', 'verbose_name_plural': 'OwnTrackLogs'}, + name='owntracklog', #zqx: 指定要修改选项的模型名称为owntracklog + # 更新模型选项,将get_latest_by和ordering中的字段名从created_time改为creation_time #zqx: 更新模型选项,将get_latest_by和ordering字段从created_time改为creation_time,保持verbose_name和verbose_name_plural不变 + options={'get_latest_by': 'creation_time', 'ordering': ['creation_time'], 'verbose_name': 'OwnTrackLogs', + 'verbose_name_plural': 'OwnTrackLogs'}, ), + # 重命名模型字段 #zqx: 使用RenameField操作重命名模型字段 migrations.RenameField( - model_name='owntracklog', - old_name='created_time', - new_name='creation_time', + model_name='owntracklog', #zqx: 指定要重命名字段的模型名称为owntracklog + old_name='created_time', #zqx: 指定原字段名为created_time + new_name='creation_time', #zqx: 指定新字段名为creation_time ), ] diff --git a/src/DjangoBlog/owntracks/models.py b/src/DjangoBlog/owntracks/models.py index 760942c..05bfebb 100644 --- a/src/DjangoBlog/owntracks/models.py +++ b/src/DjangoBlog/owntracks/models.py @@ -1,20 +1,32 @@ +# 导入Django数据库模型模块 #zqx: 引入Django的models模块,用于定义数据库模型 from django.db import models +# 从Django时区工具中导入now函数,用于获取当前时间 #zqx: 从django.utils.timezone导入now函数,用于设置默认时间值 from django.utils.timezone import now +# Create your models here. #zqx: Django模型定义的标准注释,标记模型定义区域开始 -# Create your models here. - +# 定义OwnTrackLog数据模型,继承自Django的Model基类 #zqx: 定义OwnTrackLog类,继承自models.Model,创建一个数据库模型 class OwnTrackLog(models.Model): + # 用户标识字段,字符类型,最大长度100,不允许为空 #zqx: 定义tid字段,类型为CharField,最大长度100,null=False表示不允许为空,verbose_name设置字段显示名称 tid = models.CharField(max_length=100, null=False, verbose_name='用户') + # 纬度字段,浮点数类型 #zqx: 定义lat字段,类型为FloatField,verbose_name设置字段显示名称 lat = models.FloatField(verbose_name='纬度') + # 经度字段,浮点数类型 #zqx: 定义lon字段,类型为FloatField,verbose_name设置字段显示名称 lon = models.FloatField(verbose_name='经度') + # 创建时间字段,日期时间类型,默认值为当前时间 #zqx: 定义creation_time字段,类型为DateTimeField,第一个参数是字段名,default设置默认值为now函数 creation_time = models.DateTimeField('创建时间', default=now) + # 定义对象的字符串表示方法,返回用户的tid #zqx: 定义__str__方法,返回对象的tid属性,用于在管理后台等地方显示对象信息 def __str__(self): return self.tid + # 定义模型的元数据选项 #zqx: 定义Meta内部类,用于配置模型的元数据选项 class Meta: + # 设置查询结果的默认排序方式,按创建时间升序排列 #zqx: 设置ordering属性,指定查询结果按creation_time字段升序排列 ordering = ['creation_time'] + # 设置模型在管理后台显示的单数名称 #zqx: 设置verbose_name属性,指定模型在管理后台的单数显示名称 verbose_name = "OwnTrackLogs" + # 设置模型在管理后台显示的复数名称,这里与单数名称相同 #zqx: 设置verbose_name_plural属性,指定模型在管理后台的复数显示名称,这里与单数名称相同 verbose_name_plural = verbose_name + # 设置获取最新记录时依据的字段 #zqx: 设置get_latest_by属性,指定获取最新记录时使用的字段为creation_time get_latest_by = 'creation_time' diff --git a/src/DjangoBlog/owntracks/owntracks b/src/DjangoBlog/owntracks/owntracks new file mode 100644 index 0000000..e69de29 diff --git a/src/DjangoBlog/owntracks/tests.py b/src/DjangoBlog/owntracks/tests.py index 3b4b9d8..b3d4a3a 100644 --- a/src/DjangoBlog/owntracks/tests.py +++ b/src/DjangoBlog/owntracks/tests.py @@ -1,64 +1,83 @@ +# 导入json模块用于处理JSON数据 #zqx: 引入json模块,用于处理JSON格式数据的编码和解码 import json +# 从Django测试模块导入测试客户端、请求工厂和测试用例基类 #zqx: 从django.test导入Client(测试客户端)、RequestFactory(请求工厂)和TestCase(测试用例基类) from django.test import Client, RequestFactory, TestCase +# 从accounts应用导入BlogUser模型 #zqx: 从accounts应用的models模块导入BlogUser用户模型 from accounts.models import BlogUser +# 从当前应用导入OwnTrackLog模型 #zqx: 从当前应用(.)的models模块导入OwnTrackLog模型 from .models import OwnTrackLog +# Create your tests here. #zqx: Django测试文件的标准注释,标记测试代码区域开始 -# Create your tests here. - +# 定义OwnTrackLogTest测试类,继承自Django的TestCase #zqx: 定义OwnTrackLogTest测试类,继承Django的TestCase类,用于测试OwnTrackLog相关功能 class OwnTrackLogTest(TestCase): + # 测试初始化方法,在每个测试方法执行前运行 #zqx: setUp方法,在每个测试方法执行前自动调用,用于初始化测试环境 def setUp(self): + # 创建测试客户端实例 #zqx: 创建Client实例,用于模拟HTTP请求 self.client = Client() + # 创建请求工厂实例 #zqx: 创建RequestFactory实例,用于创建测试请求对象 self.factory = RequestFactory() + # 测试owntracks功能的主要测试方法 #zqx: 定义test_own_track_log测试方法,用于测试owntracks功能 def test_own_track_log(self): + # 创建包含完整位置信息的测试数据 #zqx: 创建包含tid、lat、lon字段的字典对象,作为完整位置信息测试数据 o = { - 'tid': 12, - 'lat': 123.123, - 'lon': 134.341 + 'tid': 12, #zqx: 用户ID字段,值为12 + 'lat': 123.123, #zqx: 纬度字段,值为123.123 + 'lon': 134.341 #zqx: 经度字段,值为134.341 } + # 使用客户端发送POST请求,将位置数据以JSON格式发送到/logtracks端点 #zqx: 使用client.post方法向/owntracks/logtracks路径发送POST请求,数据为JSON格式 self.client.post( - '/owntracks/logtracks', - json.dumps(o), - content_type='application/json') - length = len(OwnTrackLog.objects.all()) - self.assertEqual(length, 1) + '/owntracks/logtracks', #zqx: 请求的目标URL路径 + json.dumps(o), #zqx: 将字典o转换为JSON字符串 + content_type='application/json') #zqx: 设置请求的内容类型为application/json + # 检查数据库中OwnTrackLog记录数量是否为1 #zqx: 查询OwnTrackLog模型的所有记录,检查记录数量是否为1 + length = len(OwnTrackLog.objects.all()) #zqx: 获取OwnTrackLog所有对象的数量 + self.assertEqual(length, 1) #zqx: 断言记录数量等于1 + # 创建不完整的位置数据(缺少经度) #zqx: 创建缺少lon字段的字典对象,作为不完整位置信息测试数据 o = { - 'tid': 12, - 'lat': 123.123 + 'tid': 12, #zqx: 用户ID字段,值为12 + 'lat': 123.123 #zqx: 纬度字段,值为123.123 } + # 再次发送POST请求 #zqx: 使用client.post方法再次发送POST请求,数据为不完整的JSON格式 self.client.post( - '/owntracks/logtracks', - json.dumps(o), - content_type='application/json') - length = len(OwnTrackLog.objects.all()) - self.assertEqual(length, 1) + '/owntracks/logtracks', #zqx: 请求的目标URL路径 + json.dumps(o), #zqx: 将不完整的字典o转换为JSON字符串 + content_type='application/json') #zqx: 设置请求的内容类型为application/json + # 检查数据库记录数量是否仍为1(不完整数据应该不被保存) #zqx: 查询OwnTrackLog模型的所有记录,检查记录数量是否仍为1 + length = len(OwnTrackLog.objects.all()) #zqx: 获取OwnTrackLog所有对象的数量 + self.assertEqual(length, 1) #zqx: 断言记录数量仍等于1,验证不完整数据未被保存 - rsp = self.client.get('/owntracks/show_maps') - self.assertEqual(rsp.status_code, 302) + # 测试未登录用户访问/show_maps端点,应该返回302重定向 #zqx: 测试未登录用户访问/show_maps端点的行为 + rsp = self.client.get('/owntracks/show_maps') #zqx: 使用client.get方法向/owntracks/show_maps路径发送GET请求 + self.assertEqual(rsp.status_code, 302) #zqx: 断言响应状态码为302,表示重定向 - user = BlogUser.objects.create_superuser( - email="liangliangyy1@gmail.com", - username="liangliangyy1", - password="liangliangyy1") + # 创建超级用户用于测试 #zqx: 使用create_superuser方法创建超级用户用于后续测试 + user = BlogUser.objects.create_superuser( #zqx: 调用BlogUser模型的create_superuser方法 + email="liangliangyy1@gmail.com", #zqx: 设置用户邮箱 + username="liangliangyy1", #zqx: 设置用户名 + password="liangliangyy1") #zqx: 设置用户密码 - self.client.login(username='liangliangyy1', password='liangliangyy1') - s = OwnTrackLog() - s.tid = 12 - s.lon = 123.234 - s.lat = 34.234 - s.save() + # 使用创建的用户登录 #zqx: 使用client.login方法以创建的用户身份登录 + self.client.login(username='liangliangyy1', password='liangliangyy1') #zqx: 使用用户名和密码登录 + # 手动创建并保存一个OwnTrackLog实例 #zqx: 手动创建OwnTrackLog对象并保存到数据库 + s = OwnTrackLog() #zqx: 创建OwnTrackLog实例 + s.tid = 12 #zqx: 设置tid属性为12 + s.lon = 123.234 #zqx: 设置lon属性为123.234 + s.lat = 34.234 #zqx: 设置lat属性为34.234 + s.save() #zqx: 保存对象到数据库 - rsp = self.client.get('/owntracks/show_dates') - self.assertEqual(rsp.status_code, 200) - rsp = self.client.get('/owntracks/show_maps') - self.assertEqual(rsp.status_code, 200) - rsp = self.client.get('/owntracks/get_datas') - self.assertEqual(rsp.status_code, 200) - rsp = self.client.get('/owntracks/get_datas?date=2018-02-26') - self.assertEqual(rsp.status_code, 200) + # 测试已登录用户访问各个端点,都应该返回200成功状态码 #zqx: 测试已登录用户访问不同端点的响应状态 + rsp = self.client.get('/owntracks/show_dates') #zqx: 向/owntracks/show_dates路径发送GET请求 + self.assertEqual(rsp.status_code, 200) #zqx: 断言响应状态码为200,表示请求成功 + rsp = self.client.get('/owntracks/show_maps') #zqx: 向/owntracks/show_maps路径发送GET请求 + self.assertEqual(rsp.status_code, 200) #zqx: 断言响应状态码为200,表示请求成功 + rsp = self.client.get('/owntracks/get_datas') #zqx: 向/owntracks/get_datas路径发送GET请求 + self.assertEqual(rsp.status_code, 200) #zqx: 断言响应状态码为200,表示请求成功 + rsp = self.client.get('/owntracks/get_datas?date=2018-02-26') #zqx: 向带日期参数的/owntracks/get_datas路径发送GET请求 + self.assertEqual(rsp.status_code, 200) #zqx: 断言响应状态码为200,表示请求成功 diff --git a/src/DjangoBlog/owntracks/urls.py b/src/DjangoBlog/owntracks/urls.py index c19ada8..b36a3a1 100644 --- a/src/DjangoBlog/owntracks/urls.py +++ b/src/DjangoBlog/owntracks/urls.py @@ -1,12 +1,22 @@ +# 从Django URL模块导入path函数用于定义URL模式 #zqx: 从django.urls模块导入path函数,用于定义URL路由模式 from django.urls import path +# 从当前应用导入视图模块 #zqx: 从当前目录(.)导入views模块,包含处理请求的视图函数 from . import views +# 定义应用命名空间为"owntracks" #zqx: 设置app_name变量为"owntracks",定义该应用的命名空间 app_name = "owntracks" +# 定义URL模式列表 #zqx: 定义urlpatterns列表,包含该应用的所有URL路由模式 urlpatterns = [ + # 定义日志跟踪接口URL,将请求路由到manage_owntrack_log视图函数 #zqx: 使用path函数定义URL模式,将'owntracks/logtracks'路径映射到views.manage_owntrack_log函数,命名为'logtracks' path('owntracks/logtracks', views.manage_owntrack_log, name='logtracks'), + # 定义地图展示页面URL,将请求路由到show_maps视图函数 #zqx: 使用path函数定义URL模式,将'owntracks/show_maps'路径映射到views.show_maps函数,命名为'show_maps' path('owntracks/show_maps', views.show_maps, name='show_maps'), + # 定义数据获取接口URL,将请求路由到get_datas视图函数 #zqx: 使用path函数定义URL模式,将'owntracks/get_datas'路径映射到views.get_datas函数,命名为'get_datas' path('owntracks/get_datas', views.get_datas, name='get_datas'), + # 定义日期展示页面URL,将请求路由到show_log_dates视图函数 #zqx: 使用path函数定义URL模式,将'owntracks/show_dates'路径映射到views.show_log_dates函数,命名为'show_dates' path('owntracks/show_dates', views.show_log_dates, name='show_dates') ] + + diff --git a/src/DjangoBlog/owntracks/views.py b/src/DjangoBlog/owntracks/views.py index 4c72bdd..ade1103 100644 --- a/src/DjangoBlog/owntracks/views.py +++ b/src/DjangoBlog/owntracks/views.py @@ -1,127 +1,161 @@ -# Create your views here. -import datetime -import itertools -import json -import logging -from datetime import timezone -from itertools import groupby - -import django -import requests -from django.contrib.auth.decorators import login_required -from django.http import HttpResponse -from django.http import JsonResponse -from django.shortcuts import render -from django.views.decorators.csrf import csrf_exempt - +# Create your views here. #zqx: Django视图文件标准注释,标记视图代码开始 +# 导入所需的Python标准库和第三方库 #zqx: 导入项目需要的各种标准库和第三方库 +import datetime #zqx: 导入datetime模块,用于处理日期时间相关操作 +import itertools #zqx: 导入itertools模块,用于高效的循环迭代操作 +import json #zqx: 导入json模块,用于处理JSON数据格式 +import logging #zqx: 导入logging模块,用于记录日志信息 +from datetime import timezone #zqx: 从datetime模块导入timezone,用于处理时区相关操作 +from itertools import groupby #zqx: 从itertools模块导入groupby,用于对数据进行分组操作 + +import django #zqx: 导入django模块 +import requests #zqx: 导入requests库,用于发送HTTP请求 +# 导入Django的装饰器、HTTP响应类和视图相关模块 #zqx: 导入Django框架的各种视图相关组件 +from django.contrib.auth.decorators import login_required #zqx: 从django.contrib.auth.decorators导入login_required装饰器,用于限制视图只能由登录用户访问 +from django.http import HttpResponse #zqx: 从django.http导入HttpResponse,用于返回HTTP响应 +from django.http import JsonResponse #zqx: 从django.http导入JsonResponse,用于返回JSON格式的HTTP响应 +from django.shortcuts import render #zqx: 从django.shortcuts导入render函数,用于渲染模板 +from django.views.decorators.csrf import csrf_exempt #zqx: 从django.views.decorators导入csrf_exempt装饰器,用于免除CSRF验证 + +# 导入当前应用的OwnTrackLog模型 #zqx: 从当前应用的models模块导入OwnTrackLog数据模型 from .models import OwnTrackLog +# 获取日志记录器实例 #zqx: 获取名为__name__的日志记录器实例 logger = logging.getLogger(__name__) - +# 装饰器,免除CSRF验证,用于接收外部系统POST请求 #zqx: 使用@csrf_exempt装饰器,免除该视图函数的CSRF验证,允许外部系统POST请求 @csrf_exempt -def manage_owntrack_log(request): - try: - s = json.loads(request.read().decode('utf-8')) - tid = s['tid'] - lat = s['lat'] - lon = s['lon'] - - logger.info( - 'tid:{tid}.lat:{lat}.lon:{lon}'.format( - tid=tid, lat=lat, lon=lon)) - if tid and lat and lon: - m = OwnTrackLog() - m.tid = tid - m.lat = lat - m.lon = lon - m.save() - return HttpResponse('ok') - else: - return HttpResponse('data error') - except Exception as e: - logger.error(e) - return HttpResponse('error') - - +def manage_owntrack_log(request): #zqx: 定义manage_owntrack_log视图函数,接收request参数 + try: #zqx: 开始异常处理块 + # 解析请求体中的JSON数据 #zqx: 解析HTTP请求体中的JSON数据 + s = json.loads(request.read().decode('utf-8')) #zqx: 读取请求体内容并解码为utf-8,然后解析为JSON对象 + tid = s['tid'] #zqx: 从JSON对象中获取tid字段值(用户标识) + lat = s['lat'] #zqx: 从JSON对象中获取lat字段值(纬度) + lon = s['lon'] #zqx: 从JSON对象中获取lon字段值(经度) + + # 记录接收到的位置信息日志 #zqx: 记录接收到的位置信息到日志 + logger.info( #zqx: 使用logger记录info级别的日志信息 + 'tid:{tid}.lat:{lat}.lon:{lon}'.format( #zqx: 格式化日志信息字符串 + tid=tid, lat=lat, lon=lon)) #zqx: 填充格式化参数 + # 验证必要字段是否存在 #zqx: 验证必需的字段是否存在且不为空 + if tid and lat and lon: #zqx: 判断tid、lat、lon三个字段是否都存在且不为空 + # 创建并保存位置记录 #zqx: 创建OwnTrackLog实例并保存位置记录 + m = OwnTrackLog() #zqx: 创建OwnTrackLog模型实例 + m.tid = tid #zqx: 设置实例的tid属性 + m.lat = lat #zqx: 设置实例的lat属性 + m.lon = lon #zqx: 设置实例的lon属性 + m.save() #zqx: 保存实例到数据库 + return HttpResponse('ok') #zqx: 返回'ok'字符串响应 + else: #zqx: 如果必要字段不完整 + return HttpResponse('data error') #zqx: 返回'data error'字符串响应 + except Exception as e: #zqx: 捕获所有异常 + # 记录错误日志并返回错误响应 #zqx: 记录错误日志并返回错误响应 + logger.error(e) #zqx: 使用logger记录error级别的异常信息 + return HttpResponse('error') #zqx: 返回'error'字符串响应 + +# 装饰器,要求用户登录才能访问 #zqx: 使用@login_required装饰器,限制该视图只能由登录用户访问 @login_required -def show_maps(request): - if request.user.is_superuser: - defaultdate = str(datetime.datetime.now(timezone.utc).date()) - date = request.GET.get('date', defaultdate) - context = { - 'date': date +def show_maps(request): #zqx: 定义show_maps视图函数,接收request参数 + # 检查用户是否为超级用户 #zqx: 检查当前登录用户是否为超级用户 + if request.user.is_superuser: #zqx: 判断请求用户是否为超级用户 + # 设置默认日期为当前UTC日期 #zqx: 设置默认日期为当前UTC日期 + defaultdate = str(datetime.datetime.now(timezone.utc).date()) #zqx: 获取当前UTC时间的日期部分并转换为字符串 + # 从GET参数获取日期,如果没有则使用默认日期 #zqx: 从请求GET参数中获取date参数,如果没有则使用默认日期 + date = request.GET.get('date', defaultdate) #zqx: 获取GET参数中的date值,不存在时使用defaultdate + # 构造上下文数据 #zqx: 构造传递给模板的上下文数据 + context = { #zqx: 定义context字典 + 'date': date #zqx: 将date变量添加到context字典中 } - return render(request, 'owntracks/show_maps.html', context) - else: - from django.http import HttpResponseForbidden - return HttpResponseForbidden() - - + # 渲染地图展示页面 #zqx: 渲染show_maps.html模板并返回响应 + return render(request, 'owntracks/show_maps.html', context) #zqx: 使用render函数渲染模板并返回响应 + else: #zqx: 如果用户不是超级用户 + # 非超级用户返回403禁止访问 #zqx: 为非超级用户返回403禁止访问响应 + from django.http import HttpResponseForbidden #zqx: 从django.http导入HttpResponseForbidden + return HttpResponseForbidden() #zqx: 返回403禁止访问响应 + +# 装饰器,要求用户登录才能访问 #zqx: 使用@login_required装饰器,限制该视图只能由登录用户访问 @login_required -def show_log_dates(request): - dates = OwnTrackLog.objects.values_list('creation_time', flat=True) - results = list(sorted(set(map(lambda x: x.strftime('%Y-%m-%d'), dates)))) - - context = { - 'results': results +def show_log_dates(request): #zqx: 定义show_log_dates视图函数,接收request参数 + # 从数据库获取所有记录的创建时间 #zqx: 从数据库中查询OwnTrackLog模型的所有creation_time字段值 + dates = OwnTrackLog.objects.values_list('creation_time', flat=True) #zqx: 使用values_list获取creation_time字段值,flat=True返回扁平化结果 + # 提取日期部分并去重排序 #zqx: 提取日期部分,去重并排序 + results = list(sorted(set(map(lambda x: x.strftime('%Y-%m-%d'), dates)))) #zqx: 使用map提取日期格式化字符串,set去重,sorted排序,list转换为列表 + + # 构造上下文数据 #zqx: 构造传递给模板的上下文数据 + context = { #zqx: 定义context字典 + 'results': results #zqx: 将results变量添加到context字典中 } - return render(request, 'owntracks/show_log_dates.html', context) - - -def convert_to_amap(locations): - convert_result = [] - it = iter(locations) - - item = list(itertools.islice(it, 30)) - while item: - datas = ';'.join( - set(map(lambda x: str(x.lon) + ',' + str(x.lat), item))) - - key = '8440a376dfc9743d8924bf0ad141f28e' - api = 'http://restapi.amap.com/v3/assistant/coordinate/convert' - query = { - 'key': key, - 'locations': datas, - 'coordsys': 'gps' + # 渲染日期展示页面 #zqx: 渲染show_log_dates.html模板并返回响应 + return render(request, 'owntracks/show_log_dates.html', context) #zqx: 使用render函数渲染模板并返回响应 + +# 将GPS坐标转换为高德地图坐标(批量处理,每次30个) #zqx: 定义convert_to_amap函数,用于将GPS坐标批量转换为高德地图坐标,每次处理30个点 +def convert_to_amap(locations): #zqx: 定义convert_to_amap函数,接收locations参数(位置列表) + convert_result = [] #zqx: 初始化转换结果列表 + # 创建迭代器 #zqx: 创建locations列表的迭代器 + it = iter(locations) #zqx: 使用iter函数创建locations的迭代器 + + # 每次取30个位置点进行处理 #zqx: 每次从迭代器中取出30个位置点进行处理 + item = list(itertools.islice(it, 30)) #zqx: 使用itertools.islice从迭代器中取出前30个元素 + while item: #zqx: 当item列表不为空时循环处理 + # 将经纬度格式化为高德API需要的格式 #zqx: 将经纬度数据格式化为高德API所需的格式 + datas = ';'.join( #zqx: 使用';'连接符连接所有坐标字符串 + set(map(lambda x: str(x.lon) + ',' + str(x.lat), item))) #zqx: 使用map提取每个位置的经度和纬度并格式化,set去重,join连接 + + # 高德地图API配置 #zqx: 配置高德地图坐标转换API的参数 + key = '8440a376dfc9743d8924bf0ad141f28e' #zqx: 设置高德地图API的key + api = 'http://restapi.amap.com/v3/assistant/coordinate/convert' #zqx: 设置高德地图API的URL + query = { #zqx: 定义API请求参数字典 + 'key': key, #zqx: API密钥参数 + 'locations': datas, #zqx: 需要转换的坐标数据 + 'coordsys': 'gps' #zqx: 源坐标系为GPS } - rsp = requests.get(url=api, params=query) - result = json.loads(rsp.text) - if "locations" in result: - convert_result.append(result['locations']) - item = list(itertools.islice(it, 30)) - - return ";".join(convert_result) - - + # 发送请求到高德API #zqx: 向高德地图API发送GET请求 + rsp = requests.get(url=api, params=query) #zqx: 使用requests.get发送带参数的GET请求 + result = json.loads(rsp.text) #zqx: 解析API响应的JSON数据 + # 处理API响应结果 #zqx: 处理API返回的结果 + if "locations" in result: #zqx: 判断响应结果中是否包含locations字段 + convert_result.append(result['locations']) #zqx: 如果包含则将locations值添加到转换结果列表中 + # 继续处理下一批数据 #zqx: 继续处理下一批30个数据 + item = list(itertools.islice(it, 30)) #zqx: 从迭代器中继续取出下一批30个元素 + + # 返回转换后的坐标字符串 #zqx: 返回所有转换后的坐标字符串,用分号连接 + return ";".join(convert_result) #zqx: 使用";"连接符连接所有转换结果并返回 + +# 装饰器,要求用户登录才能访问 #zqx: 使用@login_required装饰器,限制该视图只能由登录用户访问 @login_required -def get_datas(request): - now = django.utils.timezone.now().replace(tzinfo=timezone.utc) - querydate = django.utils.timezone.datetime( - now.year, now.month, now.day, 0, 0, 0) - if request.GET.get('date', None): - date = list(map(lambda x: int(x), request.GET.get('date').split('-'))) - querydate = django.utils.timezone.datetime( - date[0], date[1], date[2], 0, 0, 0) - nextdate = querydate + datetime.timedelta(days=1) - models = OwnTrackLog.objects.filter( - creation_time__range=(querydate, nextdate)) - result = list() - if models and len(models): - for tid, item in groupby( - sorted(models, key=lambda k: k.tid), key=lambda k: k.tid): +def get_datas(request): #zqx: 定义get_datas视图函数,接收request参数 + # 获取当前UTC时间并设置为当天0点 #zqx: 获取当前UTC时间并设置为当天的0点0分0秒 + now = django.utils.timezone.now().replace(tzinfo=timezone.utc) #zqx: 获取当前时间并设置时区为UTC + querydate = django.utils.timezone.datetime( #zqx: 创建查询开始日期时间对象 + now.year, now.month, now.day, 0, 0, 0) #zqx: 设置为当年当月当日的0时0分0秒 + # 如果GET参数中有指定日期,则使用指定日期 #zqx: 如果请求GET参数中包含date,则使用指定日期 + if request.GET.get('date', None): #zqx: 判断GET参数中是否存在date参数 + date = list(map(lambda x: int(x), request.GET.get('date').split('-'))) #zqx: 将date参数按'-'分割并转换为整数列表 + querydate = django.utils.timezone.datetime( #zqx: 根据指定日期创建查询开始日期时间对象 + date[0], date[1], date[2], 0, 0, 0) #zqx: 使用指定年月日创建datetime对象 + # 计算查询结束时间(第二天0点) #zqx: 计算查询结束时间,为查询开始时间的下一天0点 + nextdate = querydate + datetime.timedelta(days=1) #zqx: 查询结束时间为开始时间加上1天 + # 查询指定日期范围内的位置记录 #zqx: 查询creation_time在指定日期范围内的OwnTrackLog记录 + models = OwnTrackLog.objects.filter( #zqx: 使用filter方法筛选记录 + creation_time__range=(querydate, nextdate)) #zqx: 筛选creation_time在querydate到nextdate范围内的记录 + result = list() #zqx: 初始化结果列表 + # 如果查询到数据,则按用户分组处理 #zqx: 如果查询到数据则按用户进行分组处理 + if models and len(models): #zqx: 判断models是否存在且不为空 + for tid, item in groupby( #zqx: 使用groupby按tid分组遍历models + sorted(models, key=lambda k: k.tid), key=lambda k: k.tid): #zqx: 先按tid排序,然后按tid分组 + + d = dict() #zqx: 创建字典对象存储用户轨迹数据 + d["name"] = tid #zqx: 设置字典的name字段为用户标识tid + paths = list() #zqx: 初始化路径坐标列表 + # 目前使用原始GPS坐标,注释掉的代码是使用高德转换坐标的部分 #zqx: 当前使用原始GPS坐标,注释掉的是高德坐标转换的代码 + # locations = convert_to_amap( #zqx: 调用convert_to_amap函数转换坐标(已注释) + # sorted(item, key=lambda x: x.creation_time)) #zqx: 按创建时间排序后转换(已注释) + # for i in locations.split(';'): #zqx: 遍历转换后的坐标字符串(已注释) + # paths.append(i.split(',')) #zqx: 将坐标分割后添加到路径列表(已注释) + # 使用GPS原始经纬度按时间排序 #zqx: 使用原始GPS坐标按时间排序 + for location in sorted(item, key=lambda x: x.creation_time): #zqx: 遍历分组后的记录并按创建时间排序 + paths.append([str(location.lon), str(location.lat)]) #zqx: 将经度和纬度转换为字符串并添加到路径列表 + d["path"] = paths #zqx: 设置字典的path字段为路径坐标列表 + result.append(d) #zqx: 将用户轨迹数据字典添加到结果列表 + # 返回JSON格式的轨迹数据 #zqx: 返回JSON格式的轨迹数据响应 + return JsonResponse(result, safe=False) #zqx: 使用JsonResponse返回结果,safe=False允许非字典对象 - d = dict() - d["name"] = tid - paths = list() - # 使用高德转换后的经纬度 - # locations = convert_to_amap( - # sorted(item, key=lambda x: x.creation_time)) - # for i in locations.split(';'): - # paths.append(i.split(',')) - # 使用GPS原始经纬度 - for location in sorted(item, key=lambda x: x.creation_time): - paths.append([str(location.lon), str(location.lat)]) - d["path"] = paths - result.append(d) - return JsonResponse(result, safe=False)