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

540 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
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:
```prisma
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
```tsx
// /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.