添加owntracks-tyx文件夹:包含注释代码

tyx_branch
汤应雪 3 months ago
parent b75840aa3d
commit da2e3c1324

@ -0,0 +1,7 @@
"""
owntracks 应用的数据库迁移包
此文件标识 migrations 目录为 Python 便于 Django 迁移系统识别和管理
注意除非有特殊需求否则不要修改此文件内容
Django 会自动管理迁移文件的生成和执行
"""

@ -0,0 +1,26 @@
#tyx
"""
owntracks 应用的管理后台配置
注册模型到Django管理界面
"""
from django.contrib import admin
# 导入模型(当前未实际导入,需要添加)
# from .models import OwnTrackLog
class OwnTrackLogsAdmin(admin.ModelAdmin):
"""
OwnTrackLog 模型的管理界面配置类
可以在此自定义管理界面的显示和行为
当前使用默认配置可以添加以下自定义
- list_display: 列表页显示的字段
- list_filter: 筛选器字段
- search_fields: 搜索字段
- ordering: 排序字段
"""
pass
# 注册模型到管理后台(当前未注册,需要取消注释)
# admin.site.register(OwnTrackLog, OwnTrackLogsAdmin)

@ -0,0 +1,22 @@
#tyx
"""
owntracks 应用的配置类
用于配置应用的特定设置
"""
from django.apps import AppConfig
class OwntracksConfig(AppConfig):
"""
owntracks 应用的配置类
继承自Django的AppConfig用于应用级别的配置
"""
# 应用的名称Django使用这个名称来识别应用
name = 'owntracks'
# 可以在此添加其他应用配置,例如:
# verbose_name = "位置追踪" # 在admin中显示的中文名称
# default_auto_field = 'django.db.models.BigAutoField' # 默认主键类型

@ -0,0 +1,38 @@
#tyx
# Generated by Django 4.1.7 on 2023-03-02 07:14
"""
数据库迁移文件
由Django自动生成用于创建和修改数据库表结构
注意迁移文件通常不需要手动修改或添加注释
因为它们是由Django根据models.py的变化自动生成的
"""
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='OwnTrackLog',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('tid', models.CharField(max_length=100, verbose_name='用户')),
('lat', models.FloatField(verbose_name='纬度')),
('lon', models.FloatField(verbose_name='经度')),
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
],
options={
'verbose_name': 'OwnTrackLogs',
'verbose_name_plural': 'OwnTrackLogs',
'ordering': ['created_time'],
'get_latest_by': 'created_time',
},
),
]

@ -0,0 +1,29 @@
#tyx
# Generated by Django 4.2.5 on 2023-09-06 13:19
"""
数据库迁移文件
由Django自动生成用于创建和修改数据库表结构
注意迁移文件通常不需要手动修改或添加注释
因为它们是由Django根据models.py的变化自动生成的
"""
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('owntracks', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='owntracklog',
options={'get_latest_by': 'creation_time', 'ordering': ['creation_time'], 'verbose_name': 'OwnTrackLogs', 'verbose_name_plural': 'OwnTrackLogs'},
),
migrations.RenameField(
model_name='owntracklog',
old_name='created_time',
new_name='creation_time',
),
]

@ -0,0 +1,7 @@
"""
owntracks 应用的数据库迁移包
此文件标识 migrations 目录为 Python 便于 Django 迁移系统识别和管理
注意除非有特殊需求否则不要修改此文件内容
Django 会自动管理迁移文件的生成和执行
"""

@ -0,0 +1,43 @@
#tyx
"""
owntracks 应用的数据模型定义
用于存储和管理 OwnTracks 位置追踪数据
"""
from django.db import models
from django.utils.timezone import now
class OwnTrackLog(models.Model):
"""
OwnTracks 位置日志数据模型
存储用户的位置追踪信息包括经纬度和时间戳
"""
# 用户标识符,对应 OwnTracks 中的 tid
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):
"""
字符串表示方法
返回用户ID用于在admin等界面显示
"""
return self.tid
class Meta:
"""
模型元数据配置
"""
ordering = ['creation_time'] # 默认按创建时间升序排序
verbose_name = "OwnTrackLogs" # 在admin中显示的单数名称
verbose_name_plural = verbose_name # 在admin中显示的复数名称
get_latest_by = 'creation_time' # 获取最新记录时使用的字段

@ -0,0 +1,101 @@
#tyx
"""
owntracks 应用的测试用例
验证模型和视图功能的正确性
"""
import json
from django.test import Client, RequestFactory, TestCase
from accounts.models import BlogUser
from .models import OwnTrackLog
class OwnTrackLogTest(TestCase):
"""
OwnTrackLog 模型和视图的测试类
"""
def setUp(self):
"""
测试初始化方法
在每个测试方法执行前运行准备测试环境
"""
self.client = Client() # Django测试客户端
self.factory = RequestFactory() # 请求工厂,用于创建请求对象
def test_own_track_log(self):
"""
测试位置日志功能
包括数据接收存储和权限验证
"""
# 测试用例1正常的位置数据提交
o = {
'tid': 12, # 用户ID
'lat': 123.123, # 纬度
'lon': 134.341 # 经度
}
# 发送POST请求提交位置数据
self.client.post(
'/owntracks/logtracks',
json.dumps(o), # 将字典转换为JSON字符串
content_type='application/json') # 设置内容类型为JSON
# 验证数据是否成功保存
length = len(OwnTrackLog.objects.all())
self.assertEqual(length, 1) # 应该有一条记录
# 测试用例2缺少必要字段的数据提交
o = {
'tid': 12,
'lat': 123.123
# 缺少经度字段
}
# 发送不完整的数据
self.client.post(
'/owntracks/logtracks',
json.dumps(o),
content_type='application/json')
# 验证数据条数没有增加
length = len(OwnTrackLog.objects.all())
self.assertEqual(length, 1) # 应该还是只有一条记录
# 测试用例3未登录用户访问地图页面
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')
# 创建测试位置记录
s = OwnTrackLog()
s.tid = 12
s.lon = 123.234
s.lat = 34.234
s.save()
# 测试用例4登录用户访问日期列表页面
rsp = self.client.get('/owntracks/show_dates')
self.assertEqual(rsp.status_code, 200) # 应该返回200成功
# 测试用例5登录用户访问地图页面
rsp = self.client.get('/owntracks/show_maps')
self.assertEqual(rsp.status_code, 200) # 应该返回200成功
# 测试用例6获取位置数据默认日期
rsp = self.client.get('/owntracks/get_datas')
self.assertEqual(rsp.status_code, 200) # 应该返回200成功
# 测试用例7获取指定日期的位置数据
rsp = self.client.get('/owntracks/get_datas?date=2018-02-26')
self.assertEqual(rsp.status_code, 200) # 应该返回200成功

@ -0,0 +1,31 @@
#tyx
"""
owntracks 应用的URL配置
定义应用的路由映射关系
"""
from django.urls import path
from . import views
# 应用命名空间用于反向解析URL时避免冲突
app_name = "owntracks"
# URL模式列表
urlpatterns = [
# 接收位置数据接口
# path: 定义URL路径和对应的视图函数
# 'owntracks/logtracks': URL路径
# views.manage_owntrack_log: 对应的视图函数
# name='logtracks': URL名称用于反向解析
path('owntracks/logtracks', views.manage_owntrack_log, name='logtracks'),
# 地图展示页面
path('owntracks/show_maps', views.show_maps, name='show_maps'),
# 位置数据API接口
path('owntracks/get_datas', views.get_datas, name='get_datas'),
# 日期列表页面
path('owntracks/show_dates', views.show_log_dates, name='show_dates')
]

@ -0,0 +1,209 @@
#tyx
"""
owntracks 应用的视图函数
处理位置数据的接收展示和查询
"""
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
from .models import OwnTrackLog
# 获取当前模块的日志器
logger = logging.getLogger(__name__)
@csrf_exempt # 免除CSRF保护便于外部应用调用
def manage_owntrack_log(request):
"""
接收和处理 OwnTracks 客户端发送的位置数据
POST请求接收JSON格式的位置数据并存入数据库
"""
try:
# 解析请求体中的JSON数据
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') # 返回错误响应
@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
}
# 渲染地图展示页面
return render(request, 'owntracks/show_maps.html', context)
else:
# 非超级用户返回403禁止访问
from django.http import HttpResponseForbidden
return HttpResponseForbidden()
@login_required
def show_log_dates(request):
"""
显示所有有位置记录的日期列表
用于在地图页面中选择特定日期查看轨迹
"""
# 获取所有记录的创建时间
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
}
# 渲染日期列表页面
return render(request, 'owntracks/show_log_dates.html', context)
def convert_to_amap(locations):
"""
将GPS坐标转换为高德地图坐标系统
由于GPS坐标系与高德地图不同需要进行坐标转换
Args:
locations: OwnTrackLog 对象列表包含经纬度信息
Returns:
str: 转换后的坐标字符串格式为"经度,纬度;经度,纬度;..."
"""
convert_result = []
it = iter(locations) # 创建迭代器
# 每次处理30个坐标高德API限制
item = list(itertools.islice(it, 30))
while item:
# 将坐标格式化为高德API要求的格式
datas = ';'.join(
set(map(lambda x: str(x.lon) + ',' + str(x.lat), item)))
# 高德地图API密钥实际使用中应放在配置文件中
key = '8440a376dfc9743d8924bf0ad141f28e'
api = 'http://restapi.amap.com/v3/assistant/coordinate/convert'
query = {
'key': key,
'locations': datas,
'coordsys': 'gps' # 指定源坐标系为GPS
}
# 调用高德坐标转换API
rsp = requests.get(url=api, params=query)
result = json.loads(rsp.text)
# 检查API响应是否包含转换结果
if "locations" in result:
convert_result.append(result['locations'])
# 获取下一批坐标
item = list(itertools.islice(it, 30))
# 合并所有转换结果
return ";".join(convert_result)
@login_required
def get_datas(request):
"""
提供位置数据的JSON API接口
返回指定日期的用户轨迹数据用于在地图上绘制路径
Returns:
JsonResponse: 包含用户轨迹数据的JSON响应
"""
# 获取当前时间并设置为UTC时区
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):
# 按用户ID分组每个用户的轨迹单独处理
for tid, item in groupby(
sorted(models, key=lambda k: k.tid), key=lambda k: k.tid):
d = dict() # 创建用户数据字典
d["name"] = tid # 设置用户名
paths = list() # 初始化路径点列表
# 选项1使用高德转换后的经纬度当前被注释
# locations = convert_to_amap(
# sorted(item, key=lambda x: x.creation_time))
# for i in locations.split(';'):
# paths.append(i.split(','))
# 选项2使用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) # 添加到结果列表
# 返回JSON格式的响应
return JsonResponse(result, safe=False) # safe=False允许非字典对象被序列化
Loading…
Cancel
Save