You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
djangoBlogStudy/src/owntracks/views.py

201 lines
8.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#gjl
# 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
# 导入当前应用的模型,用于在视图中操作数据库
from .models import OwnTrackLog
# 获取一个 logger 实例,用于记录日志信息(如请求、错误等)
logger = logging.getLogger(__name__)
# 视图函数:接收并处理 OwnTracks 客户端上传的位置日志数据
# 使用 @csrf_exempt 装饰器免除 CSRF 验证,因为客户端可能无法提供 CSRF token
# 该函数预期接收 JSON 格式的 POST 请求
@csrf_exempt
def manage_owntrack_log(request):
try:
# gjl 读取请求体的原始数据,并解码为 UTF-8 字符串,然后解析为 Python 字典
s = json.loads(request.read().decode('utf-8'))
# gjl 从解析后的数据中提取 tid用户/设备标识、lat纬度、lon经度
tid = s['tid']
lat = s['lat']
lon = s['lon']
# gjl 记录一条 INFO 级别的日志,包含接收到的位置信息
logger.info(
'tid:{tid}.lat:{lat}.lon:{lon}'.format(
tid=tid, lat=lat, lon=lon))
# 验证提取的数据是否都存在(非空)
if tid and lat and lon:
# gjl 创建一个新的 OwnTrackLog 模型实例
m = OwnTrackLog()
# gjl 将接收到的数据赋值给模型字段
m.tid = tid
m.lat = lat
m.lon = lon
# gjl 保存到数据库creation_time 会自动使用默认值 now
m.save()
# gjl 返回 HTTP 响应 'ok',表示数据接收和保存成功
return HttpResponse('ok')
else:
# gjl 如果数据不完整,返回 'data error'
return HttpResponse('data error')
except Exception as e:
# gjl 捕获任何异常,记录错误日志,并返回 'error' 响应
logger.error(e)
return HttpResponse('error')
# gjl 视图函数:渲染显示地图的 HTML 页面
# gjl 使用 @login_required 装饰器,确保只有登录用户才能访问
@login_required
def show_maps(request):
# gjl 进一步检查用户是否为超级用户(管理员)
if request.user.is_superuser:
# gjl 获取当前 UTC 日期,作为默认日期
defaultdate = str(datetime.datetime.now(timezone.utc).date())
# gjl 从 GET 请求参数中获取 'date',如果不存在则使用默认日期
date = request.GET.get('date', defaultdate)
# gjl 构造模板上下文,将日期传递给前端
context = {
'date': date
}
# gjl 使用指定的 HTML 模板渲染响应
return render(request, 'owntracks/show_maps.html', context)
else:
# gjl 如果不是超级用户,返回 403 禁止访问
from django.http import HttpResponseForbidden
return HttpResponseForbidden()
# gjl 视图函数:渲染显示有轨迹记录的日期列表页面
# gjl 使用 @login_required 装饰器,确保只有登录用户才能访问
@login_required
def show_log_dates(request):
# gjl 查询所有 OwnTrackLog 记录的 creation_time 字段值
dates = OwnTrackLog.objects.values_list('creation_time', flat=True)
# gjl 将 datetime 对象转换为 'YYYY-MM-DD' 格式的字符串,并去重、排序
results = list(sorted(set(map(lambda x: x.strftime('%Y-%m-%d'), dates))))
# gjl 构造模板上下文,将日期列表传递给前端
context = {
'results': results
}
# gjl 使用指定的 HTML 模板渲染响应
return render(request, 'owntracks/show_log_dates.html', context)
# gjl 辅助函数:将 GPS 坐标WGS-84批量转换为高德地图坐标系GCJ-02
# gjl 由于高德 API 有数量限制,此处每次处理最多 30 个点
# gjl 注意:此函数在 get_datas 中被注释掉,当前未使用
def convert_to_amap(locations):
convert_result = [] # 存储转换后的坐标结果
it = iter(locations) # 创建一个迭代器
# gjl 每次从迭代器中取出最多 30 个位置点
item = list(itertools.islice(it, 30))
while item:
# gjl 将每个点的经度和纬度拼接成 "lon,lat" 字符串,用分号连接成一个列表
datas = ';'.join(
set(map(lambda x: str(x.lon) + ',' + str(x.lat), item)))
# gjl 高德坐标转换 API 的固定参数
key = '8440a376dfc9743d8924bf0ad141f28e'
api = 'http://restapi.amap.com/v3/assistant/coordinate/convert'
# 构造请求参数
query = {
'key': key,
'locations': datas,
'coordsys': 'gps' # 指定原始坐标系为 GPSWGS-84
}
# 发送 GET 请求到高德 API
rsp = requests.get(url=api, params=query)
# 解析 API 返回的 JSON 数据
result = json.loads(rsp.text)
# 如果返回结果包含转换后的坐标,则添加到结果列表
if "locations" in result:
convert_result.append(result['locations'])
# 继续处理下一批最多 30 个点
item = list(itertools.islice(it, 30))
# 将所有批次的转换结果合并成一个以分号分隔的字符串并返回
return ";".join(convert_result)
# gjl 视图函数:返回指定日期的位置轨迹数据,供前端地图使用
# gjl 返回 JSON 格式的数据,通常由 AJAX 调用
# gjl 使用 @login_required 装饰器,确保只有登录用户才能访问
@login_required
def get_datas(request):
# 获取当前 UTC 时间
now = django.utils.timezone.now().replace(tzinfo=timezone.utc)
# 构造查询日期的起始时间(当天 00:00:00 UTC
querydate = django.utils.timezone.datetime(
now.year, now.month, now.day, 0, 0, 0)
# 如果请求中包含 'date' 参数,则使用该参数指定的日期
if request.GET.get('date', None):
# 解析 'YYYY-MM-DD' 格式的日期字符串
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)
# gjl 计算查询结束时间(起始时间 + 1 天)
nextdate = querydate + datetime.timedelta(days=1)
# gjl查询指定日期范围内当天的所有位置记录
models = OwnTrackLog.objects.filter(
creation_time__range=(querydate, nextdate))
# gjl 初始化结果列表
result = list()
# glj 如果查询到记录,则按 tid用户/设备)分组处理
if models and len(models):
for tid, item in groupby(
# 先按 tid 排序,以便 groupby 正确分组
sorted(models, key=lambda k: k.tid),
# 指定分组依据为 tid
key=lambda k: k.tid):
# gjl 为每个 tid 创建一个数据字典
d = dict()
d["name"] = tid # 记录设备/用户名称
paths = list() # 存储该设备的轨迹点序列
# gjl 方案一(当前启用):直接使用原始 GPS 坐标
# gjl 遍历该设备当天的所有位置点,按时间排序
for location in sorted(item, key=lambda x: x.creation_time):
# gjl 将每个点的经度和纬度作为字符串列表添加到 paths
paths.append([str(location.lon), str(location.lat)])
# # gjl 方案二(注释中):使用高德转换后的坐标
# # gjl 先转换坐标,然后解析
# locations = convert_to_amap(
# sorted(item, key=lambda x: x.creation_time))
# for i in locations.split(';'):
# paths.append(i.split(','))
# 将轨迹点列表赋值给字典的 "path" 键
d["path"] = paths
# 将该设备的轨迹数据添加到总结果列表
result.append(d)
# gjl 将结果列表序列化为 JSON 并返回
# gjl safe=False 允许返回非字典类型的对象(如列表)
return JsonResponse(result, safe=False)