代码注释

zjj_branch
周俊杰 7 months ago
parent 3bd2391f96
commit 4ae5ff5fcb

@ -2,6 +2,7 @@ from django.contrib import admin
# Register your models here.
class OwnTrackLogsAdmin(admin.ModelAdmin):
pass
# 目前该管理类为空,可以根据需要添加自定义的管理界面配置,
# 例如列表显示字段、搜索字段、过滤器等。
pass

@ -1,5 +1,5 @@
from django.apps import AppConfig
class OwntracksConfig(AppConfig):
name = 'owntracks'
# 定义应用的名称为 'owntracks'Django 使用此名称来识别和加载应用。
name = 'owntracks'

@ -1,31 +1,44 @@
# Generated by Django 4.1.7 on 2023-03-02 07:14
from django.db import migrations, models
import django.utils.timezone
# 该迁移文件由 Django 4.1.7 在 2023年3月2日 07:14 自动生成
from django.db import migrations, models # 导入迁移和模型相关的模块
import django.utils.timezone # 导入 Django 提供的时区工具,用于处理时间字段的默认值
class Migration(migrations.Migration):
# 表示这是一个初始迁移,即数据库中还没有任何由该 app 创建的表
initial = True
# 当前迁移不依赖其他迁移文件
dependencies = [
]
# 定义该迁移要执行的一系列操作(在这里是创建一个数据模型)
operations = [
# 创建一个名为 'OwnTrackLog' 的数据模型(对应数据库中的一张表)
migrations.CreateModel(
name='OwnTrackLog',
fields=[
# 主键字段,自增 Big Integer 类型Django 自动创建,作为记录的唯一标识
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
# 用户标识字段,类型为字符串,最大长度为 100用于表示哪个用户的定位信息
# verbose_name 是在 Django Admin 或表单中显示的中文名称
('tid', models.CharField(max_length=100, verbose_name='用户')),
# 纬度字段,浮点数类型,用于存储地理坐标中的纬度信息
('lat', models.FloatField(verbose_name='纬度')),
# 经度字段,浮点数类型,用于存储地理坐标中的经度信息
('lon', models.FloatField(verbose_name='经度')),
# 创建时间字段DateTime 类型,默认值为当前时间(使用 Django 的时区感知时间)
# 用于记录这条定位日志是什么时候被创建/记录的
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
],
# 模型的元数据选项Meta 的内容在这里以字典形式定义)
options={
'verbose_name': 'OwnTrackLogs',
'verbose_name_plural': 'OwnTrackLogs',
'ordering': ['created_time'],
'get_latest_by': 'created_time',
'verbose_name': 'OwnTrackLogs', # 单数形式的中文/英文显示名称(后台管理界面等)
'verbose_name_plural': 'OwnTrackLogs', # 复数形式的显示名称,这里和单数一样
'ordering': ['created_time'], # 默认按创建时间正序排序
'get_latest_by': 'created_time', # 获取最新记录时,依据 created_time 字段
},
),
]
]

@ -1,22 +1,29 @@
# Generated by Django 4.2.5 on 2023-09-06 13:19
from django.db import migrations
# 该迁移文件由 Django 4.2.5 在 2023年9月6日 13:19 自动生成
from django.db import migrations # 只需要导入迁移模块,因为这次只做模型选项和字段的重命名,不涉及新字段或模型
class Migration(migrations.Migration):
# 该迁移依赖上一个迁移,即 0001_initial
dependencies = [
('owntracks', '0001_initial'),
('owntracks', '0001_initial'), # 表示此迁移是在 owntracks app 的 0001_initial 迁移之后执行的
]
# 定义该迁移要执行的具体操作
operations = [
# 修改模型的元数据选项Meta 选项)
migrations.AlterModelOptions(
name='owntracklog',
options={'get_latest_by': 'creation_time', 'ordering': ['creation_time'], 'verbose_name': 'OwnTrackLogs', 'verbose_name_plural': 'OwnTrackLogs'},
name='owntracklog', # 针对 OwnTrackLog 这个模型
options={
'get_latest_by': 'creation_time', # 获取该模型最新记录时,将依据 creation_time 字段(原为 created_time
'ordering': ['creation_time'], # 默认排序字段也改为 creation_time
'verbose_name': 'OwnTrackLogs', # 单数名(后台显示等),保持不变
'verbose_name_plural': 'OwnTrackLogs', # 复数名,保持不变
},
),
# 将数据模型中的字段名从 'created_time' 重命名为 'creation_time'
migrations.RenameField(
model_name='owntracklog',
old_name='created_time',
new_name='creation_time',
model_name='owntracklog', # 要修改的模型名
old_name='created_time', # 原来的字段名
new_name='creation_time', # 新的字段名
),
]
]

@ -2,19 +2,28 @@ from django.db import models
from django.utils.timezone import now
# Create your models here.
# 定义数据模型类 OwnTrackLog用于存储用户的轨迹日志信息。
class OwnTrackLog(models.Model):
# 用户标识字段字符型最大长度为100不允许为空。
tid = models.CharField(max_length=100, null=False, verbose_name='用户')
# 纬度字段,浮点型,用于存储地理位置的纬度信息。
lat = models.FloatField(verbose_name='纬度')
# 经度字段,浮点型,用于存储地理位置的经度信息。
lon = models.FloatField(verbose_name='经度')
# 创建时间字段,日期时间型,默认值为当前时间。
creation_time = models.DateTimeField('创建时间', default=now)
def __str__(self):
# 定义对象的字符串表示形式,返回用户的标识 tid。
return self.tid
class Meta:
ordering = ['creation_time']
verbose_name = "OwnTrackLogs"
verbose_name_plural = verbose_name
get_latest_by = 'creation_time'
# 定义模型的元数据选项。
ordering = ['creation_time'] # 默认按创建时间升序排序。
verbose_name = "OwnTrackLogs" # 模型在管理界面中的单数显示名称。
verbose_name_plural = verbose_name # 模型在管理界面中的复数显示名称,与单数相同。
get_latest_by = 'creation_time' # 指定获取最新对象时依据的字段为 creation_time。

@ -5,60 +5,78 @@ from django.test import Client, RequestFactory, TestCase
from accounts.models import BlogUser
from .models import OwnTrackLog
# Create your tests here.
class OwnTrackLogTest(TestCase):
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
# 在每个测试方法执行前运行,用于初始化测试环境。
self.client = Client() # 创建一个 Django 测试客户端,用于模拟 HTTP 请求。
self.factory = RequestFactory() # 创建一个请求工厂,用于创建请求对象。
def test_own_track_log(self):
# 测试用例:测试 OwnTrackLog 模型的数据记录和相关的视图功能。
# 测试数据1包含 tid, lat, lon 的完整数据。
o = {
'tid': 12,
'lat': 123.123,
'lon': 134.341
}
# 向 '/owntracks/logtracks' 发送 POST 请求,传递 JSON 格式的数据。
self.client.post(
'/owntracks/logtracks',
json.dumps(o),
content_type='application/json')
json.dumps(o), # 将字典转换为 JSON 字符串。
content_type='application/json') # 指定内容类型为 JSON。
# 检查数据库中 OwnTrackLog 对象的数量是否为 1。
length = len(OwnTrackLog.objects.all())
self.assertEqual(length, 1)
# 测试数据2缺少 lon 字段的不完整数据。
o = {
'tid': 12,
'lat': 123.123
}
# 向 '/owntracks/logtracks' 发送 POST 请求,传递不完整的数据。
self.client.post(
'/owntracks/logtracks',
json.dumps(o),
content_type='application/json')
# 检查数据库中 OwnTrackLog 对象的数量是否仍为 1未新增
length = len(OwnTrackLog.objects.all())
self.assertEqual(length, 1)
# 测试未登录用户访问 '/owntracks/show_maps' 视图,预期返回 302 重定向状态码。
rsp = self.client.get('/owntracks/show_maps')
self.assertEqual(rsp.status_code, 302)
# 创建一个超级用户,用于后续的登录和权限测试。
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="liangliangyy1")
# 使用超级用户凭据登录。
self.client.login(username='liangliangyy1', password='liangliangyy1')
# 手动创建一个 OwnTrackLog 对象并保存到数据库。
s = OwnTrackLog()
s.tid = 12
s.lon = 123.234
s.lat = 34.234
s.save()
# 测试登录用户访问 '/owntracks/show_dates' 视图,预期返回 200 状态码。
rsp = self.client.get('/owntracks/show_dates')
self.assertEqual(rsp.status_code, 200)
# 测试登录用户访问 '/owntracks/show_maps' 视图,预期返回 200 状态码。
rsp = self.client.get('/owntracks/show_maps')
self.assertEqual(rsp.status_code, 200)
# 测试登录用户访问 '/owntracks/get_datas' 视图,预期返回 200 状态码。
rsp = self.client.get('/owntracks/get_datas')
self.assertEqual(rsp.status_code, 200)
# 测试登录用户访问带有日期参数的 '/owntracks/get_datas' 视图,预期返回 200 状态码。
rsp = self.client.get('/owntracks/get_datas?date=2018-02-26')
self.assertEqual(rsp.status_code, 200)
self.assertEqual(rsp.status_code, 200)

@ -2,11 +2,22 @@ from django.urls import path
from . import views
app_name = "owntracks"
app_name = "owntracks" # 定义应用命名空间为 "owntracks",用于在模板和视图中区分不同应用的 URL。
urlpatterns = [
# URL 模式:处理位置跟踪数据的记录。
# 映射到 views.manage_owntrack_log 视图函数,命名为 'logtracks'。
path('owntracks/logtracks', views.manage_owntrack_log, name='logtracks'),
# URL 模式:展示地图视图。
# 映射到 views.show_maps 视图函数,命名为 'show_maps'。
path('owntracks/show_maps', views.show_maps, name='show_maps'),
# URL 模式:获取数据视图。
# 映射到 views.get_datas 视图函数,命名为 'get_datas'。
path('owntracks/get_datas', views.get_datas, name='get_datas'),
# URL 模式:展示日志日期视图。
# 映射到 views.show_log_dates 视图函数,命名为 'show_dates'。
path('owntracks/show_dates', views.show_log_dates, name='show_dates')
]
]

@ -1,4 +1,4 @@
# Create your views here.
# 导入所需的模块和库。
import datetime
import itertools
import json
@ -8,120 +8,155 @@ 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
from django.contrib.auth.decorators import login_required # 用于要求用户登录的装饰器。
from django.http import HttpResponse # 用于返回 HTTP 响应。
from django.http import JsonResponse # 用于返回 JSON 格式的 HTTP 响应。
from django.shortcuts import render # 用于渲染模板并返回 HTTP 响应。
from django.views.decorators.csrf import csrf_exempt # 用于豁免 CSRF 验证的装饰器。
from .models import OwnTrackLog
from .models import OwnTrackLog # 导入自定义的 OwnTrackLog 模型。
logger = logging.getLogger(__name__)
logger = logging.getLogger(__name__) # 获取当前模块的日志记录器。
# 视图函数:处理位置跟踪数据的记录。
# 使用 @csrf_exempt 装饰器豁免 CSRF 验证,适用于 API 接口。
@csrf_exempt
def manage_owntrack_log(request):
try:
# 从请求体中读取并解码 JSON 数据。
s = json.loads(request.read().decode('utf-8'))
tid = s['tid']
lat = s['lat']
lon = s['lon']
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 = OwnTrackLog() # 创建一个新的 OwnTrackLog 实例。
m.tid = tid
m.lat = lat
m.lon = lon
m.save()
return HttpResponse('ok')
m.save() # 保存实例到数据库。
return HttpResponse('ok') # 返回成功响应。
else:
return HttpResponse('data error')
return HttpResponse('data error') # 返回数据错误响应。
except Exception as e:
# 记录任何异常到日志。
logger.error(e)
return HttpResponse('error')
return HttpResponse('error') # 返回一般错误响应。
# 视图函数:展示地图视图。
# 使用 @login_required 装饰器确保用户已登录,同时检查用户是否为超级用户。
@login_required
def show_maps(request):
if request.user.is_superuser:
# 获取当前 UTC 时间的日期,作为默认日期。
defaultdate = str(datetime.datetime.now(timezone.utc).date())
# 从请求参数中获取日期,如果未提供则使用默认日期。
date = request.GET.get('date', defaultdate)
# 构建传递给模板的上下文,包含日期信息。
context = {
'date': date
}
# 渲染 'owntracks/show_maps.html' 模板,并传递上下文。
return render(request, 'owntracks/show_maps.html', context)
else:
# 如果用户不是超级用户,返回 403 Forbidden 响应。
from django.http import HttpResponseForbidden
return HttpResponseForbidden()
# 视图函数:展示日志日期视图。
# 使用 @login_required 装饰器确保用户已登录。
@login_required
def show_log_dates(request):
# 从 OwnTrackLog 对象中获取所有创建时间,并提取日期部分。
dates = OwnTrackLog.objects.values_list('creation_time', flat=True)
# 对日期进行排序并去重,转换为 'YYYY-MM-DD' 格式的字符串列表。
results = list(sorted(set(map(lambda x: x.strftime('%Y-%m-%d'), dates))))
# 构建传递给模板的上下文,包含日期结果列表。
context = {
'results': results
}
# 渲染 'owntracks/show_log_dates.html' 模板,并传递上下文。
return render(request, 'owntracks/show_log_dates.html', context)
# 函数:将 GPS 坐标转换为高德地图坐标。
# 该函数通过调用高德地图的坐标转换 API 实现坐标转换。
def convert_to_amap(locations):
convert_result = []
it = iter(locations)
convert_result = [] # 用于存储转换后的坐标结果。
it = iter(locations) # 创建一个迭代器以便分批处理位置数据。
item = list(itertools.islice(it, 30))
item = list(itertools.islice(it, 30)) # 每次处理最多 30 个位置。
while item:
# 将位置数据拼接为 'lon,lat' 格式的字符串,使用分号分隔。
datas = ';'.join(
set(map(lambda x: str(x.lon) + ',' + str(x.lat), item)))
key = '8440a376dfc9743d8924bf0ad141f28e'
api = 'http://restapi.amap.com/v3/assistant/coordinate/convert'
key = '8440a376dfc9743d8924bf0ad141f28e' # 高德地图 API 的密钥,请根据实际情况替换。
api = 'http://restapi.amap.com/v3/assistant/coordinate/convert' # 高德地图坐标转换 API 的 URL。
query = {
'key': key,
'locations': datas,
'coordsys': 'gps'
'coordsys': 'gps' # 指定坐标系统为 GPS。
}
rsp = requests.get(url=api, params=query)
result = json.loads(rsp.text)
rsp = requests.get(url=api, params=query) # 发送 GET 请求到高德地图 API。
result = json.loads(rsp.text) # 解析 API 响应的 JSON 数据。
if "locations" in result:
# 如果响应中包含转换后的坐标,将其添加到结果列表中。
convert_result.append(result['locations'])
item = list(itertools.islice(it, 30))
item = list(itertools.islice(it, 30)) # 处理下一批 30 个位置。
# 将所有转换后的坐标结果拼接为一个字符串,使用分号分隔。
return ";".join(convert_result)
@login_required
# 视图函数:获取数据视图。
# 该视图根据请求参数获取特定日期范围内的轨迹数据,并返回 JSON 格式的响应。
def get_datas(request):
now = django.utils.timezone.now().replace(tzinfo=timezone.utc)
now = django.utils.timezone.now().replace(tzinfo=timezone.utc) # 获取当前的 UTC 时间。
querydate = django.utils.timezone.datetime(
now.year, now.month, now.day, 0, 0, 0)
now.year, now.month, now.day, 0, 0, 0) # 构建当天的起始时间00:00:00
# 如果请求中包含日期参数,则根据参数构建查询日期。
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)
nextdate = querydate + datetime.timedelta(days=1) # 计算查询日期的下一天,作为结束时间。
# 从 OwnTrackLog 对象中筛选出创建时间在查询日期范围内的数据。
models = OwnTrackLog.objects.filter(
creation_time__range=(querydate, nextdate))
result = list()
result = list() # 用于存储最终的结果数据。
if models and len(models):
# 按用户标识 (tid) 对数据进行分组和排序。
for tid, item in groupby(
sorted(models, key=lambda k: k.tid), key=lambda k: k.tid):
d = dict()
d["name"] = tid
paths = list()
# 使用高德转换后的经纬度
d = dict() # 创建一个字典,用于存储单个用户的数据。
d["name"] = tid # 用户标识。
paths = list() # 用于存储用户的轨迹路径。
# 目前代码中提供了使用高德转换后的经纬度和直接使用 GPS 原始经纬度的两种方式,
# 其中使用高德转换坐标的部分被注释。
# 使用高德转换后的经纬度(被注释)。
# locations = convert_to_amap(
# sorted(item, key=lambda x: x.creation_time))
# for i in locations.split(';'):
# paths.append(i.split(','))
# 使用GPS原始经纬度
# 使用 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)
paths.append([str(location.lon), str(location.lat)]) # 添加经纬度到路径列表。
d["path"] = paths # 将路径列表添加到用户数据字典中。
result.append(d) # 将用户数据字典添加到结果列表中。
# 将结果以 JSON 格式返回,设置 safe=False 以允许非字典类型的对象被序列化。
return JsonResponse(result, safe=False)
Loading…
Cancel
Save