Files
GIA/core/views/availability.py

148 lines
5.0 KiB
Python

from __future__ import annotations
from datetime import datetime, timezone
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.views import View
from core.models import (
ContactAvailabilityEvent,
ContactAvailabilitySettings,
ContactAvailabilitySpan,
Person,
)
def _to_int(value, default=0):
try:
return int(value)
except Exception:
return int(default)
def _to_bool(value, default=False):
if value is None:
return bool(default)
text = str(value).strip().lower()
if text in {"1", "true", "yes", "on", "y"}:
return True
if text in {"0", "false", "no", "off", "n"}:
return 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"
def _settings(self, request):
row, _ = ContactAvailabilitySettings.objects.get_or_create(user=request.user)
return row
def post(self, request):
row = self._settings(request)
row.enabled = _to_bool(request.POST.get("enabled"), row.enabled)
row.show_in_chat = _to_bool(request.POST.get("show_in_chat"), row.show_in_chat)
row.show_in_groups = _to_bool(
request.POST.get("show_in_groups"), row.show_in_groups
)
row.inference_enabled = _to_bool(
request.POST.get("inference_enabled"), row.inference_enabled
)
row.retention_days = max(1, _to_int(request.POST.get("retention_days"), 90))
row.fade_threshold_seconds = max(
30, _to_int(request.POST.get("fade_threshold_seconds"), 900)
)
row.save(
update_fields=[
"enabled",
"show_in_chat",
"show_in_groups",
"inference_enabled",
"retention_days",
"fade_threshold_seconds",
"updated_at",
]
)
return self.get(request)
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]
)
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",
],
}
return render(request, self.template_name, context)