Files
deardiary/todo/stats.md
lotherk 0bdd71a4ed feat: v0.1.0 - geolocation capture, calendar, search, Starlight docs site
- Automatic browser geolocation capture on event creation
- Reverse geocoding via Nominatim API for place names
- Full-text search with SQLite FTS5
- Calendar view for browsing past entries
- DateNavigator component for day navigation
- SearchModal with Ctrl+K shortcut
- QuickAddWidget with Ctrl+J shortcut
- Starlight documentation site with GitHub Pages deployment
- Multiple AI provider support (Groq, OpenAI, Anthropic, Ollama, LM Studio)
- Multi-user registration support

BREAKING: Events now include latitude/longitude/placeName fields
2026-03-27 02:27:55 +00:00

16 KiB
Raw Permalink Blame History

Statistics Feature Specification

Overview

Add a comprehensive statistics dashboard to track journaling habits and provide insights. All analytics are computed locally from user data - no external services required.


1. Streak Tracking

Feature Description

Track consecutive days of journaling activity to motivate users to maintain their habit.

Metrics to Track

  • Current Streak: Consecutive days with at least one event
  • Longest Streak: All-time record of consecutive days
  • Streak Start Date: When current streak began
  • Days Until Milestone: Days until 7/14/30/60/90 day milestones
  • Streak Risk: Days with no events that would break streak

Visualization Approaches

  • Flame icon with day count (🔥 12 days)
  • Progress bar to next milestone
  • Calendar mini-view showing streak days
  • Warning indicator when streak at risk

Dashboard Layout

┌─────────────────────────────────────┐
│  🔥 12 Day Streak    🏆 45 days     │
│  ████████░░░░░░░░  4 days to 16   │
└─────────────────────────────────────┘

Database Queries

// Current streak calculation
const events = await prisma.event.findMany({
  where: { userId },
  select: { date: true },
  orderBy: { date: 'desc' }
});

// Group by date, find consecutive days

Implementation Complexity

  • Backend: Medium - requires date range queries with grouping
  • Frontend: Low - simple counter display
  • Priority: HIGH - strong motivation driver

2. Word Count Statistics

Feature Description

Track total words written in events and journal entries over time.

Metrics to Track

  • Total Words: All-time word count
  • Daily Average: Average words per day (active days)
  • Today/This Week/This Month: Rolling word counts
  • Longest Entry: Day with most words
  • Word Count Distribution: Histogram by day
  • Writing Sessions: Days with 100+/500+/1000+ words

Visualization Approaches

  • Line chart showing word count over time (30/90/365 days)
  • Comparison bar chart: this week vs last week
  • Mini sparkline showing recent trend
  • Percentile badges (e.g., "Top 10% writers")

Dashboard Layout

┌─────────────────────────────────────┐
│  📝 12,450 words total             │
│  Avg: 156/day • This week: 1,234   │
│  ┌────────────────────────────────┐ │
│  │    ▂▃▅▇▅▃▂▄▅▇█▄▃▂▅▇           │ │
│  └────────────────────────────────┘ │
└─────────────────────────────────────┘

Database Queries

// Word count aggregation
await prisma.$queryRaw`
  SELECT date, SUM(LENGTH(content) - LENGTH(REPLACE(content, ' ', '')) + 1) as word_count
  FROM Event
  WHERE userId = ${userId}
  GROUP BY date
`;

Implementation Complexity

  • Backend: Low - simple aggregation queries
  • Frontend: Low - chart library needed
  • Priority: MEDIUM

3. Entry Frequency Heatmap

Feature Description

GitHub-style contribution heatmap showing activity intensity by day.

Metrics to Track

  • Days with events (boolean per day)
  • Event count per day (intensity levels)
  • Active weeks/months count
  • Most productive day of week
  • Most productive month

Visualization Approaches

  • GitHub-style grid (52 weeks × 7 days)
  • Color scale: empty → light → dark (slate-800 to purple-600)
  • Tooltip on hover showing date + count
  • Day-of-week labels on left
  • Month labels on top

Dashboard Layout

┌─────────────────────────────────────┐
│  Activity Heatmap                   │
│        Jan Feb Mar Apr May Jun      │
│  Mon  ░░░████▓▓░░░░░░░              │
│  Tue  ░▓▓████▓▓░░░░░░░              │
│  Wed  ░░░████▓▓░░░░░░░              │
│  Thu  ░░░░░░▓▓░░░░░░░░              │
│  Fri  ░░░░░░░░░░░░░░░░░              │
│                                      │
│  Less ░▒▓█ More                     │
└─────────────────────────────────────┘

Database Queries

// Heatmap data
await prisma.event.groupBy({
  by: ['date'],
  where: { userId },
  _count: { id }
});

Implementation Complexity

  • Backend: Low - groupBy query
  • Frontend: Medium - requires heatmap component (use react-heatmap-grid or custom SVG)
  • Priority: HIGH - visual engagement

4. Event Type Distribution

Feature Description

Breakdown of events by type (text, photo, voice, health, etc.).

Metrics to Track

  • Count per event type
  • Percentage distribution
  • Type trends over time (increasing/decreasing)
  • Media attachments count

Visualization Approaches

  • Donut/pie chart with legend
  • Horizontal bar chart (easier to read)
  • Stacked area chart over time
  • Type badges with counts

Dashboard Layout

┌─────────────────────────────────────┐
│  Event Types                        │
│  ┌──────────┐                      │
│  │  text    │  ████████  45%       │
│  │  photo   │  █████░░░  28%       │
│  │  voice   │  ███░░░░░  15%       │
│  │  health  │  █░░░░░░░   8%       │
│  │  event   │  ██░░░░░░   4%       │
│  └──────────┘                      │
└─────────────────────────────────────┘

Database Queries

// Type distribution
await prisma.event.groupBy({
  by: ['type'],
  where: { userId },
  _count: { id }
});

Implementation Complexity

  • Backend: Low - simple groupBy
  • Frontend: Low - chart library
  • Priority: MEDIUM - nice-to-have insight

5. Time-of-Day Patterns

Feature Description

Analyze when users typically log events (morning person vs night owl).

Metrics to Track

  • Events by hour of day (0-23)
  • Events by time block (Morning 5-11, Afternoon 12-17, Evening 18-22, Night 23-4)
  • Most active hour
  • Average time of first event
  • Average time of last event

Visualization Approaches

  • 24-hour radial/bar chart
  • Time block pie chart
  • Timeline showing first-last event range
  • "Your prime time" indicator

Dashboard Layout

┌─────────────────────────────────────┐
│  Writing Time Patterns              │
│           ▔▔▔▔▔▔▔                   │
│      ▔▔          ▔▔▔                │
│   ▔▔      ●        ▔▔              │
│   ▔  ▔▔              ▔             │
│   Morning   Afternoon   Evening    │
│   (6-12)     (12-6)      (6-12)    │
│                                      │
│  ☀️ Peak: 9:00 AM                   │
└─────────────────────────────────────┘

Database Queries

// Hour extraction from createdAt
await prisma.$queryRaw`
  SELECT strftime('%H', createdAt) as hour, COUNT(*) as count
  FROM Event
  WHERE userId = ${userId}
  GROUP BY hour
`;

Implementation Complexity

  • Backend: Low - SQL date functions
  • Frontend: Medium - radial chart
  • Priority: LOW - interesting but not core

6. Monthly/Yearly Summaries

Feature Description

Aggregate statistics for specific time periods with comparison to previous periods.

Metrics to Track

  • Monthly: events, journals, words, active days
  • Yearly: same metrics
  • Month-over-month change (%)
  • Year-over-year change (%)
  • Best month ever
  • Days with 100% journal generation

Visualization Approaches

  • Monthly bar chart comparison
  • Year-in-review cards
  • Progress ring showing yearly goals
  • "This time last year" comparison

Dashboard Layout

┌─────────────────────────────────────┐
│  2024 Year in Review                │
│  ┌────────┐ ┌────────┐ ┌────────┐  │
│  │  342   │ │   45   │ │ 15,600 │  │
│  │Events  │ │ Journals│ │ Words  │  │
│  └────────┘ └────────┘ └────────┘  │
│                                      │
│  Best month: November (52 events)   │
│  +12% vs 2023                       │
└─────────────────────────────────────┘

Database Queries

// Monthly aggregation
await prisma.event.groupBy({
  by: ['date'],
  where: { 
    userId,
    date: { gte: '2024-01-01', lte: '2024-12-31' }
  },
  _count: { id }
});

Implementation Complexity

  • Backend: Low - grouped queries with date filters
  • Frontend: Low-Medium - cards and charts
  • Priority: MEDIUM

7. Progress & Motivation Features

Feature Description

Gamification elements to encourage consistent journaling.

Metrics & Features

  • Achievement Badges:
    • "First Entry" (1 event)
    • "Week Warrior" (7 day streak)
    • "Month Master" (30 day streak)
    • "Century" (100 events)
    • "Word Smith" (10,000 words)
    • "Photojournalist" (50 photos)
    • "Consistent" (journal every day for a month)
  • Goals: Set daily/weekly targets
  • Streak Protection: 1 "freeze" per month to preserve streak
  • Weekly Report: Auto-generated summary every Sunday
  • Milestone Celebrations: Confetti at 7/14/30/60/90 days

Visualization Approaches

  • Badge grid (earned + locked)
  • Progress rings for goals
  • Streak flame animation
  • Weekly summary card

Dashboard Layout

┌─────────────────────────────────────┐
│  Achievements                       │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐       │
│  │🔥7 │ │🌟30│ │📝1K│ │📸10│       │
│  └────┘ └────┘ └────┘ └────┘       │
│  ┌────┐ ┌────┐ ┌────┐              │
│  │🔒 │ │🔒 │ │🔒 │  (locked)        │
│  └────┘ └────┘ └────┘              │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│  Daily Goal: 5 events              │
│  ████████░░░░░░░░░  3/5             │
└─────────────────────────────────────┘

Database Queries

// Achievement checks - multiple queries
const eventCount = await prisma.event.count({ where: { userId } });
const streak = await calculateStreak(userId);

Implementation Complexity

  • Backend: Medium - multiple queries, achievement logic
  • Frontend: Medium - badge components, progress rings
  • Priority: MEDIUM - strong engagement driver

8. Privacy-Preserving Analytics

Feature Description

Ensure all statistics computation keeps data on-device.

Approach

  • All aggregations in SQLite: Use Prisma raw queries on server
  • No external analytics services: No GA, Mixpanel, etc.
  • Opt-in sharing: Optional anonymous stats (streak length, event count) for community features
  • Local-first: Dashboard computes from local API responses
  • Data export: Users can export all their data

Implementation

// All stats computed server-side via API
// Frontend receives aggregated numbers, not raw data
GET /api/v1/stats/streak
GET /api/v1/stats/words
GET /api/v1/stats/heatmap

Privacy Considerations

  • No tracking cookies
  • No cross-user analytics
  • API key = user identity (no additional tracking)
  • Full data deletion on user request

API Changes

New Endpoints

// Statistics aggregation
GET /api/v1/stats/streak           { currentStreak, longestStreak, streakStart }
GET /api/v1/stats/words            { total, dailyAvg, thisWeek, thisMonth, trend }
GET /api/v1/stats/heatmap         [{ date, count }]
GET /api/v1/stats/types           [{ type, count, percentage }]
GET /api/v1/stats/time-of-day     [{ hour, count }]
GET /api/v1/stats/summary         { monthly: [], yearly: [] }
GET /api/v1/stats/achievements    { earned: [], available: [], progress: {} }
GET /api/v1/stats/month/:year     { events, journals, words, activeDays }

Response Types

interface StreakStats {
  currentStreak: number;
  longestStreak: number;
  streakStartDate: string;
  daysToMilestone: number;
}

interface WordStats {
  total: number;
  dailyAverage: number;
  thisWeek: number;
  thisMonth: number;
  trend: 'up' | 'down' | 'stable';
  longestEntry: { date: string; count: number };
}

interface HeatmapData {
  date: string;
  count: number;
  level: 0 | 1 | 2 | 3 | 4;
}

interface Achievement {
  id: string;
  name: string;
  description: string;
  icon: string;
  earned: boolean;
  earnedAt?: string;
  progress?: number;
  target: number;
}

Database Schema Additions

Optional: Cached Stats Table

For expensive queries, cache results:

model UserStats {
  userId         String   @id
  currentStreak Int      @default(0)
  longestStreak Int      @default(0)
  totalWords    Int      @default(0)
  totalEvents    Int      @default(0)
  lastUpdated   DateTime @updatedAt

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}

Update Strategy: Recalculate on event create/delete or daily cron.


Frontend Implementation

New Page: /stats

// /frontend/src/pages/Stats.tsx
import { useState, useEffect } from 'react';
import { api } from '../lib/api';

export default function Stats() {
  const [streak, setStreak] = useState<StreakStats | null>(null);
  const [words, setWords] = useState<WordStats | null>(null);
  const [heatmap, setHeatmap] = useState<HeatmapData[]>([]);
  // ...
}

Chart Library Recommendation

  • Recharts: Good for line/bar charts (used with React commonly)
  • react-heatmap-grid: For activity heatmap
  • react-circular-progressbar: For goal progress rings
  • Build custom SVG for simple visualizations

Implementation Roadmap

Phase 1: Core Stats (Week 1)

  1. API endpoints for streak, word count, heatmap
  2. Basic stats page with counters
  3. Heatmap visualization

Phase 2: Visualizations (Week 2)

  1. Word count trend chart
  2. Event type distribution chart
  3. Time-of-day analysis

Phase 3: Gamification (Week 3)

  1. Achievement system
  2. Daily goals
  3. Streak UI improvements

Phase 4: Summaries (Week 4)

  1. Monthly/yearly summaries
  2. "This time last year" comparison
  3. Weekly report

Priority Recommendations

Feature Priority Impact Effort
Streak Tracking HIGH High Low
Heatmap HIGH High Medium
Word Count MEDIUM Medium Low
Event Types MEDIUM Low Low
Time Patterns LOW Low Medium
Monthly/Yearly MEDIUM Medium Low
Achievements MEDIUM High Medium
Goals MEDIUM Medium Low

Summary

Statistics feature transforms DearDiary from a passive journal into an active self-improvement tool. Key priorities:

  1. Streak tracking - strongest motivation driver
  2. Heatmap - visual engagement, GitHub-style
  3. Achievements - gamification layer
  4. Word count - progress visualization

All data stays local - no privacy concerns. Implementation can be phased with clear value at each step.