|
|
|
@ -96,6 +96,7 @@ There is no event log. `GameAnalytics` derives everything from columns on `playe |
|
|
|
forward, so the browser back button can't rewind the funnel. |
|
|
|
- `device` — what they played *on*: `mobile`, `tablet` or `desktop`. |
|
|
|
- `locale` and `country` — who they are, feeding the two breakdown panels beside devices. |
|
|
|
- `utm_source` / `utm_medium` / `utm_campaign` / `utm_content` — how they got here. See below. |
|
|
|
|
|
|
|
Step names are `facts`, `intro`, `stage_<n>`, `stage_<n>_result`, `last_save`, `done`, |
|
|
|
`results`, ordered by `Player.step_rank`. |
|
|
|
@ -125,6 +126,31 @@ counted as desktop. Nothing short of client-side probing fixes it; the tablet sh |
|
|
|
floor, not an exact figure. Players created before this column existed are `nil` forever — |
|
|
|
the UA was never stored, so there is nothing to backfill from. |
|
|
|
|
|
|
|
### Traffic source (QR codes) |
|
|
|
|
|
|
|
The printed material — table talker, coffee machine banner, canteen poster, toilet talker, |
|
|
|
booth poster — each carries a QR code to the same game, differing only in `utm_content`: |
|
|
|
|
|
|
|
https://savethetomato.ikeafoundation.org/?utm_source=physical&utm_medium=qr |
|
|
|
&utm_campaign=ikea_foundation_week_2026&utm_content=table_talker |
|
|
|
|
|
|
|
`UtmTracking` (`app/controllers/concerns/utm_tracking.rb`) is included in `LanguagesController` |
|
|
|
and `GameController`. It parks the four tags in the session, and `GameController#start` writes |
|
|
|
them onto the player row. The session hop is not optional: the QR codes point at the bare |
|
|
|
domain, `LanguagesController#index` redirects to `/:locale` and drops the query string, and the |
|
|
|
player does not exist until the language page posts to `game#start`. |
|
|
|
|
|
|
|
Last touch wins — a second scan is a second visit, and the player created right after it |
|
|
|
belongs to the code that was actually scanned. Values are trimmed to 100 characters, and only |
|
|
|
the four known keys are ever read, so the query string cannot write anything else. |
|
|
|
|
|
|
|
`GameAnalytics#by_source` groups on `COALESCE(utm_content, utm_source, 'direct')` — `utm_content` |
|
|
|
is the only tag that differs between the codes, and untagged players all land in one bucket that |
|
|
|
is pinned to the bottom of the panel so the QR rows stay together at the top. |
|
|
|
|
|
|
|
**Not counted here:** the board game QR points at `dice.ikeafoundation.org`, a different app. |
|
|
|
Its `utm_content=board_game` scans are recorded there, not in this dashboard. |
|
|
|
|
|
|
|
### Thumbs up / down |
|
|
|
|
|
|
|
`Player#rate!` stores `rating` as `1` / `-1` (`Player::RATINGS`), one per player — voting |
|
|
|
|