Backend half of the dashboard stat-card redesign. Three new data
points the old DashboardResponse had no way to compute:
- Week-over-week rating deltas: UserRating/CcsUserRating only ever
stored the current Elo (mutated in place, UpdatedUtc just means
"last touched"), so there was no way to know what a rating was 7
days ago. Added UserRatingSnapshot (new collection, unique on
UserId+DateUtc) and UserRatingSnapshotService, a BackgroundService
following AnswerStatisticsMaintenanceService's exact shape (a
PeriodicTimer plus a Mongo lease so replicas behind the HPA don't
race), writing one snapshot per user per day. No historical
backfill - deltas are simply null for a user's first ~7 days after
this ships, which the DTO represents honestly via nullable ints
rather than a fake "+0".
- Percentile rank: DashboardStore now counts how many users have a
higher OverallElo/Elo than the current user out of the total.
- Exam Readiness + projected pass rate: reuses the existing, tested
IRT-based ExamSimulationScore.PassProbability (a real normal-CDF
estimate of scoring >=200, computed in ExamSimulationScoring.Calculate)
rather than inventing a new prediction model. DashboardStore pulls
the latest completed simulation's score directly and maps
PassProbability to a High/Medium/Low label via tunable thresholds.
- Average Score's "+6% vs last 100" needed no new data source at all -
just one more slice (Skip(100).Take(100)) over the QuestionAttempts
already loaded for FirstAttemptAccuracy.
Added DashboardStore.Percentile/ReadinessLabel as pure static helpers
(matching the existing Percentage/CalculateCurrentStreak pattern) and
unit tests for both. Full backend suite: 165 passed, 0 failed.
Adds a full flashcards system: FSRS-based scheduling, cloze/basic
card types, CSV import/export, media storage, and study UI, wired
into navigation and the dashboard alongside existing question flows.