Telemetry / Anonymous Usage Analytics¶
Statuss: Q14 Polish & Telemetry sprintā ieviests (2026-05-07) Privacy: Opt-in, GDPR atbilstošs, NEsatur PII
Kāpēc?¶
Bez telemetrijas mēs nezinām: - Kādus ekrānus lietotāji visbiežāk apmeklē - Kur viņi "stuck" (ilgs dwell, tad atstāj) - Kuru feature izmanto reti / nemaz - Vai jaunās UX izmaiņas patiesi uzlabo plūsmu
Q13 lauka beta atskaišu apkopošana ir anekdotiska. Telemetry dod kvantitatīvu datu plūsmu.
Privacy first¶
Kas TIEK sūtīts¶
event_name— fiksēts whitelist (sk._ALLOWED_EVENT_NAMES)screen— ekrāna nosaukums (MapScreen,InputScreenu.c.)props— JSON ar event-specific datiem (no PII!)app_version/app_build— versija un build SHAplatform—android/ios/unknowndevice_token— anonīms UUID (ca-nav-XXXXXXXX), kas ģenerēts pirmajā startā
Kas NETIEK sūtīts¶
- ❌ GPS koordinātes
- ❌ CA numuri (kuras īpašums)
- ❌ Kadastra numuri
- ❌ Lietotāja vārds, e-pasts, telefona numurs
- ❌ Ierīces ID (Android ID, IDFA)
- ❌ IP adrese (server log'os var parādīties, bet nav saglabāts
event_logtabulā) - ❌ Faili / kontaktpersonas / kalendārs
Opt-in plūsma¶
- Lietotājs default: opt-in OFF, telemetrija nesūta neko
- Settings → Privātums → "Anonīma lietošanas analītika" toggle
- Pirmais opt-in: tooltip ar īsu skaidrojumu
- Lietotājs var jebkurā brīdī izslēgt — buffer iztīrīts uzreiz
- Lietotājs var pieprasīt visas vēstures dzēšanu — Settings → "Dzēst manu vēsturi" →
DELETE /api/event/all
Server retention¶
90 dienas. Pēc tam events automātiski tiek dzēsti ar dienas cron (sk. backend tools/event_log_cleanup.py — ja vēl nav izveidots, izveido):
DELETE FROM event_log WHERE created_at < NOW() - INTERVAL '90 days';
Event schema¶
Whitelist (whitelist'ā server side)¶
| event_name | Kad | Tipiski props |
|---|---|---|
app_open |
Lietotne sākas | startup_ms |
app_background |
Lietotne fonā | — |
screen_view |
Navigation uz jaunu ekrānu | from_screen, dwell_ms_prev |
ca_loaded |
CA veiksmīgi ielādēts | features_count, area_ha_total, source: vmd|kml|package |
ca_unloaded |
CA aizvērts | dwell_ms |
polygon_drawn |
Poligons saglabāts | vertices_count, area_ha, source: tap|track |
polygon_undone |
Lietotājs nospiest "Atcelt" pēc save | — |
monitoring_added |
Pievienots Mani īpašumi | — |
monitoring_removed |
Noņemts no Mani īpašumi | — |
geofence_alert |
Robežas pārkāpums | direction: out|in, distance_m |
notification_received |
ntfy push | topic, priority |
feature_used |
Generic feature usage | feature: kml_export|measurement|... |
error |
Handled error | error_type, hint (NEsūtīt error message text!) |
settings_changed |
Settings toggle | setting: keep_screen_on|..., value: true|false |
theme_toggled |
Tēmas pārslēdzējs | mode: dark|light|system |
language_changed |
Valodas maiņa | from, to |
Constraints¶
_MAX_EVENTS_PER_BATCH = 100events vienā POST_MAX_PROPS_BYTES = 4096(JSON serialized)- Rate limit: 60 batchi/min/device
Architecture¶
Mobile (CA Navigator) Server Grafana
┌────────────────────────┐
│ Settings toggle │ user opt-in
│ (default OFF) │ ──────────────► TelemetryService.setOptIn(true)
└────────────────────────┘ │
▼
┌─────────────────────┐
│ TelemetryService │
│ - in-memory buffer │
│ - flush 30s/50 evts │
│ - persist on offline│
└──────────┬──────────┘
│ POST /api/event/batch
▼
┌─────────────────────┐
│ vzd_local_service.py│
│ /api/event/batch │
│ validate whitelist │
│ insert event_log │
└──────────┬──────────┘
│
▼
┌─────────────┐
│ event_log │
│ PostgreSQL │
│ retention │
│ 90d cron │
└──────┬──────┘
│ pg_database_size_bytes
│ + custom queries
▼
┌──────────────────┐
│ Grafana │
│ "Mobile Usage" │
│ dashboard │
└──────────────────┘
Mobile API¶
TelemetryService.instance.track(eventName, {screen, props})¶
// Manuālais call no UI
TelemetryService.instance.track('feature_used', props: {'feature': 'kml_export'});
// Auto-track screen views — NavigatorObserver dara to bez paši UI koda
// MaterialApp.navigatorObservers: [_TelemetryNavigatorObserver()]
// Iespraust app_open
TelemetryService.instance.track('app_open', props: {'startup_ms': 1200});
Pārvaldība¶
// Opt-in
await TelemetryService.instance.setOptIn(true);
// Manuāls flush (pirms aizvēršanas)
await TelemetryService.instance.flush();
// GDPR — dzēst visu serverī
final ok = await TelemetryService.instance.deleteAllRemote();
Server API¶
POST /api/event/batch¶
Body:
{
"device_token": "ca-nav-7f3a8b2c",
"events": [
{
"event_name": "screen_view",
"screen": "MapScreen",
"props": {"from_screen": "InputScreen", "dwell_ms_prev": 4500},
"app_version": "1.11.5+22",
"app_build": "ca8f89a3",
"platform": "android",
"client_ts": "2026-05-07T10:15:30.123Z"
}
]
}
Response (201):
{"accepted": 50, "rejected": 0}
DELETE /api/event/all?device_token=X (GDPR 17)¶
Response: {"deleted": 142} — kopējais events dzēsts.
Rate limit 3/h — pasargā pret abuse.
Grafana dashboard¶
Sk. server/observability/grafana/dashboards/klm-mobile-usage.json (provisioned, automātiski parādās folder "KLM").
Paneļi:
1. Daily Active Users (DAU) — count(distinct device_token) WHERE created_at > now() - 24h
2. Top 10 ekrāni (24h) — pa ekrāna apmeklējumu skaitu
3. Top events (24h) — pa event_name skaitu
4. Drop-off funnel — InputScreen → CA loaded → MapScreen → polygon drawn
5. Errors per minute — by error_type
6. App version distribution — kuras versijas vēl produkcijā
7. Platform split — Android vs iOS vs unknown
Operational¶
Monitorings¶
/health row_counts un Prometheus klm_db_row_count{table="event_log"} rāda kopējo events skaitu.
Saprātīgs aug: - 50 lietotāji × 200 events/dienā = 10k events/diena - 90 dienu retention = ~900k events steady-state - ~50 MB disk space (JSONB props ~50 baitus average)
Disk space¶
Ja event_log audz strauji (>500 MB), pārbaudīt:
- SELECT event_name, COUNT(*) FROM event_log GROUP BY 1 ORDER BY 2 DESC — kāds events spam
- Iespējams: screen_view props per liels (drop_ms_prev liels JSON?) — samazināt
Cron retention¶
Pievienot crontab -l:
0 3 * * 3 docker exec klm-vzd-service psql $DATABASE_URL -c "DELETE FROM event_log WHERE created_at < NOW() - INTERVAL '90 days'" >> /var/log/klm-event-cleanup.log 2>&1 # klm-event-cleanup
(trešdienās 03:00, atstarpēts no LAD imports otrdienās).
Privacy review¶
Reizi 6 mēnešos:
- Pārbaudīt _ALLOWED_EVENT_NAMES — vai nav jauni events ar PII risku
- Pārbaudīt event log SQL kvēriju logus — nav device_token joining ar īsto user_subscription tabulu (citādi anonimitāte salauzta)
- Atjaunot Privacy Policy, ja schema mainās