Improve tasks and backdate insights

This commit is contained in:
2026-03-03 17:21:06 +00:00
parent 9c14e51b43
commit 2898d9e832
18 changed files with 1617 additions and 264 deletions

View File

@@ -1,16 +1,13 @@
from __future__ import annotations
from datetime import datetime, timezone
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, Max, Q
from django.shortcuts import render
from django.views import View
from core.models import (
ContactAvailabilityEvent,
ContactAvailabilitySettings,
ContactAvailabilitySpan,
Person,
)
@@ -32,19 +29,6 @@ def _to_bool(value, default=False):
return bool(default)
def _iso_to_ms(value: str) -> int:
raw = str(value or "").strip()
if not raw:
return 0
try:
dt = datetime.fromisoformat(raw)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return int(dt.timestamp() * 1000)
except Exception:
return 0
class AvailabilitySettingsPage(LoginRequiredMixin, View):
template_name = "pages/availability-settings.html"
@@ -81,67 +65,45 @@ class AvailabilitySettingsPage(LoginRequiredMixin, View):
def get(self, request):
settings_row = self._settings(request)
person_id = str(request.GET.get("person") or "").strip()
service = str(request.GET.get("service") or "").strip().lower()
state = str(request.GET.get("state") or "").strip().lower()
source_kind = str(request.GET.get("source_kind") or "").strip().lower()
start_ts = _iso_to_ms(request.GET.get("start"))
end_ts = _iso_to_ms(request.GET.get("end"))
if end_ts <= 0:
end_ts = int(datetime.now(tz=timezone.utc).timestamp() * 1000)
if start_ts <= 0:
start_ts = max(0, end_ts - (14 * 24 * 60 * 60 * 1000))
events_qs = ContactAvailabilityEvent.objects.filter(user=request.user)
spans_qs = ContactAvailabilitySpan.objects.filter(user=request.user)
if person_id:
events_qs = events_qs.filter(person_id=person_id)
spans_qs = spans_qs.filter(person_id=person_id)
if service:
events_qs = events_qs.filter(service=service)
spans_qs = spans_qs.filter(service=service)
if state:
events_qs = events_qs.filter(availability_state=state)
spans_qs = spans_qs.filter(state=state)
if source_kind:
events_qs = events_qs.filter(source_kind=source_kind)
events_qs = events_qs.filter(ts__gte=start_ts, ts__lte=end_ts)
spans_qs = spans_qs.filter(start_ts__lte=end_ts, end_ts__gte=start_ts)
events = list(
events_qs.select_related("person", "person_identifier").order_by("-ts")[:500]
contact_stats = list(
ContactAvailabilityEvent.objects.filter(
user=request.user, person__isnull=False
)
.values("person_id", "person__name", "service")
.annotate(
total_events=Count("id"),
available_events=Count(
"id", filter=Q(availability_state="available")
),
fading_events=Count("id", filter=Q(availability_state="fading")),
unavailable_events=Count(
"id", filter=Q(availability_state="unavailable")
),
unknown_events=Count("id", filter=Q(availability_state="unknown")),
native_presence_events=Count(
"id", filter=Q(source_kind="native_presence")
),
read_receipt_events=Count("id", filter=Q(source_kind="read_receipt")),
typing_events=Count(
"id",
filter=Q(source_kind="typing_start")
| Q(source_kind="typing_stop"),
),
message_activity_events=Count(
"id",
filter=Q(source_kind="message_in")
| Q(source_kind="message_out"),
),
inferred_timeout_events=Count(
"id", filter=Q(source_kind="inferred_timeout")
),
last_event_ts=Max("ts"),
)
.order_by("-total_events", "person__name", "service")
)
spans = list(
spans_qs.select_related("person", "person_identifier").order_by("-end_ts")[:500]
)
people = list(Person.objects.filter(user=request.user).order_by("name"))
context = {
"settings_row": settings_row,
"people": people,
"events": events,
"spans": spans,
"filters": {
"person": person_id,
"service": service,
"state": state,
"source_kind": source_kind,
"start": request.GET.get("start") or "",
"end": request.GET.get("end") or "",
},
"service_choices": ["signal", "whatsapp", "xmpp", "instagram", "web"],
"state_choices": ["available", "fading", "unavailable", "unknown"],
"source_kind_choices": [
"native_presence",
"read_receipt",
"typing_start",
"typing_stop",
"message_in",
"message_out",
"inferred_timeout",
],
"contact_stats": contact_stats,
}
return render(request, self.template_name, context)