events: traffic-weighted appears_at lead-in replaces the pre-buffer
ready-for-agent
## Parent
#3281
## What to build
Replace the `2h × days_long` pre-buffer in the `events_active` view with a precomputed `appears_at` timestamp that scales an event's map lead-in by its significance and by how many people are looking at the map (night and weekend stretch it). This is the data-plane slice; both maps consume it immediately (the detail map, which applies no client time filter, will visibly show the new smart lead-in).
`appears_at` is a pure function of `(starts_at, ends_at)`, computed in the **Python data pipeline** (not in SQL), Europe/Berlin-local and DST-aware, stored as a new `events.appears_at` column carried through `events.parquet` and the server loader:
1. `budget_hours = max(duration_hours, 8)` — floor on the **budget**, so the weighting reaches short events too.
2. Walk backward from `starts_at`, spending the budget at a **traffic rate** per real hour, until exhausted:
- weekday daytime (10:00–18:00) = `1.0` (baseline, fastest drain)
- night (18:00–10:00) = `0.5`
- weekend (Sat/Sun) = `0.5`
- rates **stack** multiplicatively → weekend-night = `0.25` (slowest, longest window)
3. `appears_at = max(walk_back_result, starts_at − 48h)` — hard 48 h cap on the window.
Then:
- The migration adds `events.appears_at`, redefines `events_active` to gate `now() BETWEEN appears_at AND ends_at` (dropping the old pre-buffer arithmetic), and adds an `events_upcoming` view = `now() ≤ ends_at AND starts_at ≤ now() + INTERVAL '14 days'`.
- Both views select only `starts_at`/`ends_at` (+ existing `*_epoch` columns) — **`appears_at` must never appear in either view's projection / the tile payload**; it is a server-side gate only.
- The Martin config exposes `events_upcoming` as a second tile source alongside `events_active`.
- The server events loader parses and inserts `appears_at`.
No client changes in this slice.
## Acceptance criteria
- [ ] The pipeline computes `appears_at` with the budget floor (8 h), traffic-rate walk-back (night/weekend 0.5, stacking; weekday-daytime 1.0), and 48 h window cap, in Europe/Berlin local time.
- [ ] Unit tests cover the awkward cases: a DST-transition night, an event straddling Friday→Saturday, the 48 h cap biting on a multi-day event, and the 8 h floor on a 40 min weekday-daytime talk vs. the same talk on a Saturday (longer window).
- [ ] `events_active` returns rows where `now()` is within `[appears_at, ends_at]`; the old `2h × days_long` expression is gone.
- [ ] `events_upcoming` returns rows ending in the future and starting within 14 days, and is a superset of `events_active`.
- [ ] Neither view exposes `appears_at`; tiles carry only `starts_at`/`ends_at` (+ epochs).
- [ ] `events_upcoming` is reachable as a Martin tile source.
- [ ] The server loader populates `events.appears_at`; existing event-loading tests still pass.
## Blocked by
None - can start immediately.
关闭于 2026-06-13 0 条评论