Sign in to run and submit your work
Reading is open to everyone. Running code and saving drafts need an account so your work is yours and comes back on your next visit.
or
CODE WORKSPACE
Product analytics wants sessions rather than raw events. A session is a run of one user's events with no more than gap_minutes of inactivity between consecutive events. The feed arrives in the order the producer flushed it, which is neither grouped by user nor sorted by time.
Write sessionize(events, gap_minutes). Return one summary per session.
Function to write
sessionize(events: list[dict], gap_minutes: int) -> list[dict]One dict per session: user_id, session_index, started_at, ended_at, events.
How to approach it
Group by user first, sort inside each group, then walk the gaps.
Sample cases
+ 2 held back until you submit
an unsorted feed
Three users interleaved and out of order, with a 78-minute break inside one of them.
Input
Argument 1
| event_id | user_id | at |
|---|---|---|
| 5 | u2 | 2026-03-02 09:00:00 |
| 1 | u1 | 2026-03-02 08:00:00 |
| 2 | u1 | 2026-03-02 08:12:00 |
| 6 | u2 | 2026-03-02 09:05:00 |
| 3 | u1 | 2026-03-02 09:30:00 |
| 7 | u2 | 2026-03-02 11:00:00 |
| 4 | u1 | 2026-03-02 09:44:00 |
| 8 | u3 | 2026-03-02 07:00:00 |
Argument 2
30Returns
| user_id | session_index | started_at | ended_at | events |
|---|---|---|---|---|
| u1 | 1 | 2026-03-02 08:00:00 | 2026-03-02 08:12:00 | 2 |
| u1 | 2 | 2026-03-02 09:30:00 | 2026-03-02 09:44:00 | 2 |
| u2 | 1 | 2026-03-02 09:00:00 | 2026-03-02 09:05:00 | 2 |
| u2 | 2 | 2026-03-02 11:00:00 | 2026-03-02 11:00:00 | 1 |
| u3 | 1 | 2026-03-02 07:00:00 | 2026-03-02 07:00:00 | 1 |
a single event
One event is still one session, starting and ending at the same moment.
Input
Argument 1
| event_id | user_id | at |
|---|---|---|
| 1 | u1 | 2026-03-02 08:00:00 |
Argument 2
30Returns
| user_id | session_index | started_at | ended_at | events |
|---|---|---|---|---|
| u1 | 1 | 2026-03-02 08:00:00 | 2026-03-02 08:00:00 | 1 |
no events
Nothing in, nothing out — and no crash reading the first element.
Input
Argument 1
[] (empty list)
Argument 2
30Returns
[] (empty list)
Constraints
user_id, session_index, started_at, ended_at and events — the count of events in that session.Worked example
u1 has four events: 08:00, 08:12, 09:30 and 09:44 — but they arrive as event_ids 1, 2, 3, 4 scattered between u2's and u3's. The 78-minute break between 08:12 and 09:30 splits them into two sessions of two events each.
itertools.groupby is the natural reach here and is wrong on this input: it starts a new group every time the key changes, so u2 at the top of the feed and u2 again four rows later become two separate groups. It only groups correctly on input that is already sorted by the grouping key — which this feed is not.
What this tests
That grouping and ordering are separate obligations, and that itertools.groupby silently assumes the first one has already been done. Composing a sort, a group and a gap rule is the whole job.
sessionize(events: list[dict], gap_minutes: int) -> list[dict]Submit for review to find out what your query gets right, what it gets wrong, and how it compares with the best working query for this exercise.
This scenario runs a full workspace — editor, canvas and results side by side. It needs a laptop or desktop to be usable. Open this page on a bigger screen to start building.