from datetime import datetime, time, timedelta from django.utils import timezone from tracker.models import DailyStat, Event, VisitSession, Visitor def daily_counts(site, date) -> dict: """四指标共享口径(site=None 聚合全部站点)。所有指标排除爬虫。 日界取 TIME_ZONE(Asia/Shanghai)自然日:make_aware(datetime.combine(date, time.min))。 口径:pv/uv 均从事件表统计(排除 is_bot):pv 只计 event_name=page_view (SDK 0.2.0 每 15s 一条的 page_stay 增量与 sdk_loaded 不并计,防 PV 随停留时长灌倍), uv=distinct visitor(任意事件即计); sessions 按会话 started_at 落入当日且访客非爬虫;new_visitors 按 Visitor.first_seen_at 落入当日。 refresh_daily 落库与 dashboard 今日实时位共用此函数,防两处口径漂移(Task8 审查结论)。 """ day_start = timezone.make_aware(datetime.combine(date, time.min)) day_end = day_start + timedelta(days=1) ev = Event.objects.filter(is_bot=False, occurred_at__gte=day_start, occurred_at__lt=day_end) vs = VisitSession.objects.filter(visitor__is_bot=False, started_at__gte=day_start, started_at__lt=day_end) vis = Visitor.objects.filter(is_bot=False, first_seen_at__gte=day_start, first_seen_at__lt=day_end) if site is not None: ev = ev.filter(site=site) vs = vs.filter(site=site) vis = vis.filter(site=site) return {"pv": ev.filter(event_name="page_view").count(), "uv": ev.values("visitor").distinct().count(), "sessions": vs.count(), "new_visitors": vis.count()} def refresh_daily(site, date) -> DailyStat: """重算某站点某自然日的聚合(幂等,update_or_create)。指标口径见 daily_counts。""" obj, _ = DailyStat.objects.update_or_create(site=site, date=date, defaults=daily_counts(site, date)) return obj