video_monitor/app/views/AlarmView.py

119 lines
4.0 KiB
Python
Raw Normal View History

2026-08-30 22:22:11 +08:00
from datetime import datetime, timedelta
from django.shortcuts import render
from django.db.models import Count
from django.db.models.functions import TruncDate
from app.views.ViewsBase import *
from app.models import AlarmModel, StreamModel
def index(request):
"""报警管理页面:集中展示与处理进入区域/滞留/运动等报警事件及快照"""
return render(request, 'app/alarm/index.html', {})
def dashboard(request):
"""报警统计看板页面"""
return render(request, 'app/alarm/dashboard.html', {})
# 报警事件类型中文名(与 services/alarm_service.ALARM_EVENT_TYPES 对齐)
_ALARM_TYPE_NAMES = {
'entered_zone': '区域入侵',
'loiter': '滞留',
'dwell': '滞留报警',
'line_cross': '越线检测',
'line_count': '越线计数',
'direction': '方向入侵',
'density': '密度报警',
'alarm': '报警',
}
def _stream_display_name(s):
return (getattr(s, 'nickname', '') or getattr(s, 'name', '') or ('#%s' % s.id))
def _build_alarm_stats(request):
"""聚合报警统计数据。days 仅支持 7 / 30。"""
try:
days = int(request.GET.get('days', 7))
except Exception:
days = 7
days = 30 if days >= 30 else 7
now = datetime.now()
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
week_start = today_start - timedelta(days=6) # 近7天含今天
total = AlarmModel.objects.count()
today = AlarmModel.objects.filter(timestamp__gte=today_start).count()
week = AlarmModel.objects.filter(timestamp__gte=week_start).count()
stream_count = (AlarmModel.objects.exclude(stream_id__isnull=True)
.values('stream_id').distinct().count())
# 近 days 天每日趋势(无报警的日期补 0
trend_start = today_start - timedelta(days=days - 1)
tq = (AlarmModel.objects.filter(timestamp__gte=trend_start)
.annotate(d=TruncDate('timestamp'))
.values('d').annotate(c=Count('id')))
tmap = {}
for row in tq:
d = row['d']
key = d.strftime('%Y-%m-%d') if hasattr(d, 'strftime') else str(d)[:10]
tmap[key] = row['c']
trend = []
for i in range(days):
d = trend_start + timedelta(days=i)
key = d.strftime('%Y-%m-%d')
trend.append({"date": d.strftime('%m-%d'), "full": key, "count": tmap.get(key, 0)})
# 报警类型分布
ty = AlarmModel.objects.values('event_type').annotate(c=Count('id')).order_by('-c')
by_type = []
for r in ty:
et = r['event_type'] or 'unknown'
by_type.append({"type": et, "name": _ALARM_TYPE_NAMES.get(et, et), "count": r['c']})
# TOP 报警摄像头
top = (AlarmModel.objects.exclude(stream_id__isnull=True)
.values('stream_id').annotate(c=Count('id')).order_by('-c')[:8])
sids = [r['stream_id'] for r in top]
smap = {s.id: _stream_display_name(s) for s in StreamModel.objects.filter(id__in=sids)}
top_streams = [{"stream_id": r['stream_id'],
"name": smap.get(r['stream_id'], '#%s' % r['stream_id']),
"count": r['c']} for r in top]
return {
"today": today,
"week": week,
"total": total,
"stream_count": stream_count,
"days": days,
"trend": trend,
"by_type": by_type,
"top_streams": top_streams,
}
def api_openStats(request):
"""报警统计聚合接口"""
ret = False
msg = LANG_VIEWS_T(request, "msg_unknown_error")
data = {}
if request.method == 'GET':
__check_ret, __check_msg = f_checkRequestSafe(request)
if __check_ret:
try:
data = _build_alarm_stats(request)
ret = True
msg = LANG_VIEWS_T(request, "msg_success")
except Exception as e:
msg = str(e)
else:
msg = __check_msg
else:
msg = LANG_VIEWS_T(request, "msg_method_not_supported")
return f_responseJson({"code": 1000 if ret else 0, "msg": msg, "data": data})