Dashboard: Nutzer (installierte PWA) von reinen Aufrufern trennen

Die Geräte-Zahlen (heute/7 Tage/30 Tage/insgesamt) zählten bisher jedes
Gerät, das die Seite auch nur im Browser-Tab geöffnet hat. Jetzt zählen
sie nur Geräte, die PioMint mindestens einmal als installierte PWA
(Standalone) geöffnet haben - echte Nutzer statt bloße Aufrufer. Deren
Anzahl wird separat ausgewiesen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8BG6rcfV94au1CrMNQq9E
main
Samuel Zielke 3 weeks ago
parent 6a6a1f5962
commit e14f58769c

@ -50,8 +50,8 @@
</div>
<div class="col-6 col-md-6">
<div class="stat-card text-center">
<div class="stat-value">{{ unique_devices_total }}</div>
<div class="stat-label">Geräte insgesamt <small class="text-muted">(eindeutig)</small></div>
<div class="stat-value">{{ users_total }}</div>
<div class="stat-label">Nutzer insgesamt <small class="text-muted">(als App installiert)</small></div>
</div>
</div>
</div>
@ -59,20 +59,20 @@
<div class="row g-3 mb-3">
<div class="col-6 col-md-4">
<div class="stat-card text-center">
<div class="stat-value">{{ devices_today }}</div>
<div class="stat-label">Geräte heute</div>
<div class="stat-value">{{ users_today }}</div>
<div class="stat-label">Nutzer heute</div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="stat-card text-center">
<div class="stat-value">{{ devices_7d }}</div>
<div class="stat-label">Geräte (7 Tage)</div>
<div class="stat-value">{{ users_7d }}</div>
<div class="stat-label">Nutzer (7 Tage)</div>
</div>
</div>
<div class="col-6 col-md-4">
<div class="stat-card text-center">
<div class="stat-value">{{ devices_30d }}</div>
<div class="stat-label">Geräte (30 Tage)</div>
<div class="stat-value">{{ users_30d }}</div>
<div class="stat-label">Nutzer (30 Tage)</div>
</div>
</div>
</div>
@ -80,7 +80,7 @@
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="stat-card">
<h5>Plattform <small class="text-muted">(eindeutige Geräte)</small></h5>
<h5>Plattform <small class="text-muted">(eindeutige Nutzer)</small></h5>
<ul class="list-unstyled mb-0">
{% for row in by_platform %}
<li class="d-flex justify-content-between border-bottom py-1">
@ -94,7 +94,7 @@
</div>
<div class="col-md-6">
<div class="stat-card">
<h5>Browser <small class="text-muted">(eindeutige Geräte)</small></h5>
<h5>Browser <small class="text-muted">(eindeutige Nutzer)</small></h5>
<ul class="list-unstyled mb-0">
{% for row in by_browser %}
<li class="d-flex justify-content-between border-bottom py-1">
@ -109,10 +109,12 @@
</div>
<div class="stat-card mb-3">
<h5 class="mb-1">Als App installiert</h5>
<h5 class="mb-1">Nutzer vs. Aufrufer</h5>
<p class="mb-0 text-muted">
{{ standalone_count }} von {{ unique_devices_total }} Geräten insgesamt haben
PioMint (mindestens einmal als) installierte PWA geöffnet statt im Browser-Tab.
<b>{{ users_total }} Nutzer</b> haben PioMint mindestens einmal als installierte PWA
(Standalone) geöffnet – das ist echte Nutzung. Weitere
<b>{{ visitors_total }} Aufrufer</b> haben die Seite nur im Browser-Tab geöffnet,
aber nie installiert; sie fließen oben nicht in die Nutzer-Zahlen ein.
</p>
</div>
@ -126,7 +128,7 @@
<th>Pings</th>
<th>Erster Kontakt</th>
<th>Letzter Kontakt</th>
<th>Standalone</th>
<th>Nutzer?</th>
</tr>
</thead>
<tbody>

@ -104,15 +104,22 @@ def usage_dashboard(request):
events = UsageEvent.objects.all()
# "Nutzer" = Gerät hat PioMint mindestens einmal als installierte PWA
# (Standalone) geöffnet - nur das ist echte Nutzung. Ein Gerät, das die
# Seite nur im Browser-Tab geöffnet hat, ist ein "Aufrufer", kein Nutzer.
user_device_ids = events.filter(is_standalone=True).values_list("device_id", flat=True).distinct()
users = events.filter(device_id__in=user_device_ids)
visitors_total = events.exclude(device_id__in=user_device_ids).values("device_id").distinct().count()
context = {
"total_connections": events.count(),
"unique_devices_total": events.values("device_id").distinct().count(),
"devices_today": events.filter(created_at__gte=today_start).values("device_id").distinct().count(),
"devices_7d": events.filter(created_at__gte=now - timedelta(days=7)).values("device_id").distinct().count(),
"devices_30d": events.filter(created_at__gte=now - timedelta(days=30)).values("device_id").distinct().count(),
"standalone_count": events.filter(is_standalone=True).values("device_id").distinct().count(),
"by_platform": events.values("platform").annotate(count=Count("device_id", distinct=True)).order_by("-count"),
"by_browser": events.values("browser").annotate(count=Count("device_id", distinct=True)).order_by("-count"),
"visitors_total": visitors_total,
"users_total": user_device_ids.count(),
"users_today": users.filter(created_at__gte=today_start).values("device_id").distinct().count(),
"users_7d": users.filter(created_at__gte=now - timedelta(days=7)).values("device_id").distinct().count(),
"users_30d": users.filter(created_at__gte=now - timedelta(days=30)).values("device_id").distinct().count(),
"by_platform": users.values("platform").annotate(count=Count("device_id", distinct=True)).order_by("-count"),
"by_browser": users.values("browser").annotate(count=Count("device_id", distinct=True)).order_by("-count"),
"device_summary": (
events.values("device_id")
.annotate(

Loading…
Cancel
Save

Powered by TurnKey Linux.