[loggi-nh0] Add new chart types: cumulative, gap analysis, distributions #113

Closed
opened 2026-02-21 17:59:07 +01:00 by matthias · 0 comments
Owner

Bead ID: loggi-nh0
Type: task
Priority: P2
Status: closed
Close reason: Added 4 new chart types: Cumulative Progress (line chart, all types), Record Gaps (histogram, all types), Duration Distribution (histogram, timerange), Word Length Distribution (histogram, diary). Added backend /stats/distribution endpoint for diary word counts. All charts hidden by default, discoverable via Customize modal. Shared autoBucketHistogram helper for distribution charts. 6 new tests for the distribution endpoint, all 171 tests pass.


Why

The current stats only show activity over time and time-of-day/weekday breakdowns. Missing: cumulative progress (motivational running total), gap analysis (consistency insight), and distribution histograms (shape of individual records).

What

New charts (all defaultVisible: false)

Cumulative Progress (all types)

  • Line chart showing running total over time (events for timepoint, total duration for timerange, total words for diary)
  • Computed client-side: iterate daily data, accumulate running sum
  • Register as { id: 'cumulative', label: 'Cumulative Progress', defaultVisible: false }
  • Full-width card with groupable D/W/M controls

Gap Analysis (all types)

  • Histogram showing distribution of gap lengths (days between consecutive records)
  • Computed client-side from daily data: for each consecutive pair of active days, compute gap in calendar days
  • X-axis: gap length in days (bucketed: 0, 1, 2, 3, 4, 5, 6, 7, 8-14, 15-30, 30+)
  • Y-axis: number of occurrences
  • Register as { id: 'gaps', label: 'Record Gaps', defaultVisible: false }
  • Half-width card

Duration Distribution (timerange only)

  • Histogram of individual session durations
  • Data source: existing /stats/sessions endpoint (already returns per-session durations)
  • Bucket into ranges (e.g. 0-5m, 5-15m, 15-30m, 30-60m, 1-2h, 2h+) — auto-scale buckets based on data range
  • Register as { id: 'dur_dist', label: 'Duration Distribution', defaultVisible: false, kinds: ['timerange'] }
  • Half-width card

Word Count Distribution (diary only)

  • Histogram of per-entry word counts
  • Needs new backend endpoint: GET /api/entries/<id>/stats/distribution returning { records: [{ word_count: N }] } — just the word counts, no text content
  • Bucket into ranges (auto-scale based on data range)
  • Register as { id: 'word_dist', label: 'Word Length Distribution', defaultVisible: false, kinds: ['diary'] }
  • Half-width card

Backend (diary distribution endpoint)

Add to app/stats.py:

@stats_bp.route('/api/entries/<int:entry_id>/stats/distribution', methods=['GET'])

Returns per-record word counts for diary entries. Apply same date filtering as other endpoints.

Frontend

  • Add 4 new entries to PLOT_REGISTRY in stats.js
  • Add cardBuilders entries in renderStatsGrid()
  • Add render functions in stats-charts.js
  • Fetch distribution data alongside sessions in loadStats() (only for diary entries)

Where

  • app/stats.py — new /stats/distribution endpoint
  • app/static/js/stats.js — registry, builders, data loading
  • app/static/js/stats-charts.js — 4 new render functions

Constraints

  • stats-charts.js is already 723 lines — keep new renderers concise
  • Histogram bucket logic should be reusable (shared helper for both distribution charts)
  • Must handle edge cases: 0 or 1 records (no gaps), no completed sessions, etc.

Acceptance criteria

  • All 4 new charts render correctly for their applicable entry types
  • Charts show meaningful data (not empty) when records exist
  • Histograms auto-scale bucket ranges based on data
  • All hidden by default, discoverable via Customize modal
  • New charts respect date range filter
  • No console errors with 0 records or edge cases

Dependencies:

  • blocks: loggi-d7a
**Bead ID:** `loggi-nh0` **Type:** task **Priority:** P2 **Status:** closed **Close reason:** Added 4 new chart types: Cumulative Progress (line chart, all types), Record Gaps (histogram, all types), Duration Distribution (histogram, timerange), Word Length Distribution (histogram, diary). Added backend /stats/distribution endpoint for diary word counts. All charts hidden by default, discoverable via Customize modal. Shared autoBucketHistogram helper for distribution charts. 6 new tests for the distribution endpoint, all 171 tests pass. --- ## Why The current stats only show activity over time and time-of-day/weekday breakdowns. Missing: cumulative progress (motivational running total), gap analysis (consistency insight), and distribution histograms (shape of individual records). ## What ### New charts (all `defaultVisible: false`) **Cumulative Progress** (all types) - Line chart showing running total over time (events for timepoint, total duration for timerange, total words for diary) - Computed client-side: iterate daily data, accumulate running sum - Register as `{ id: 'cumulative', label: 'Cumulative Progress', defaultVisible: false }` - Full-width card with groupable D/W/M controls **Gap Analysis** (all types) - Histogram showing distribution of gap lengths (days between consecutive records) - Computed client-side from daily data: for each consecutive pair of active days, compute gap in calendar days - X-axis: gap length in days (bucketed: 0, 1, 2, 3, 4, 5, 6, 7, 8-14, 15-30, 30+) - Y-axis: number of occurrences - Register as `{ id: 'gaps', label: 'Record Gaps', defaultVisible: false }` - Half-width card **Duration Distribution** (timerange only) - Histogram of individual session durations - Data source: existing `/stats/sessions` endpoint (already returns per-session durations) - Bucket into ranges (e.g. 0-5m, 5-15m, 15-30m, 30-60m, 1-2h, 2h+) — auto-scale buckets based on data range - Register as `{ id: 'dur_dist', label: 'Duration Distribution', defaultVisible: false, kinds: ['timerange'] }` - Half-width card **Word Count Distribution** (diary only) - Histogram of per-entry word counts - Needs new backend endpoint: `GET /api/entries/<id>/stats/distribution` returning `{ records: [{ word_count: N }] }` — just the word counts, no text content - Bucket into ranges (auto-scale based on data range) - Register as `{ id: 'word_dist', label: 'Word Length Distribution', defaultVisible: false, kinds: ['diary'] }` - Half-width card ### Backend (diary distribution endpoint) Add to `app/stats.py`: ```python @stats_bp.route('/api/entries/<int:entry_id>/stats/distribution', methods=['GET']) ``` Returns per-record word counts for diary entries. Apply same date filtering as other endpoints. ### Frontend - Add 4 new entries to `PLOT_REGISTRY` in `stats.js` - Add `cardBuilders` entries in `renderStatsGrid()` - Add render functions in `stats-charts.js` - Fetch distribution data alongside sessions in `loadStats()` (only for diary entries) ## Where - `app/stats.py` — new `/stats/distribution` endpoint - `app/static/js/stats.js` — registry, builders, data loading - `app/static/js/stats-charts.js` — 4 new render functions ## Constraints - `stats-charts.js` is already 723 lines — keep new renderers concise - Histogram bucket logic should be reusable (shared helper for both distribution charts) - Must handle edge cases: 0 or 1 records (no gaps), no completed sessions, etc. ## Acceptance criteria - All 4 new charts render correctly for their applicable entry types - Charts show meaningful data (not empty) when records exist - Histograms auto-scale bucket ranges based on data - All hidden by default, discoverable via Customize modal - New charts respect date range filter - No console errors with 0 records or edge cases **Dependencies:** - blocks: `loggi-d7a`
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
matthias/loggi#113
No description provided.