Project
Primrose – Personal Reading Management System
Cross‑platform reading analytics app with adaptive time‑to‑finish estimates and structured session tracking.
C++17Qt 6SQLiteCMake
- Role
- Solo developer
- Reading time
- 2 min read
Highlights
- Designed an MVC‑based desktop app with modern C++ patterns (Factory, Observer, Strategy) for maintainable UI logic.
- Implemented a normalized SQLite schema with transaction‑safe CRUD and session‑level analytics.
- Built an adaptive reading‑speed estimator that predicts time‑to‑finish using session difficulty and genre weighting.
Problem
I read a lot of long‑form books and papers across devices, but most reading apps either track progress superficially or lock you into a single ecosystem. I wanted:
- Session‑level analytics (when I read, what I read, and how fast)
- A way to estimate how long I had left in a book based on my own behavior
- A local, privacy‑preserving system I fully control
Constraints
- Cross‑platform desktop support without rewriting the UI for each OS
- Strong type safety and predictable performance
- A schema that could evolve over time without breaking existing libraries
Approach
I implemented Primrose as a C++17 desktop app using Qt 6 and SQLite, structured around an MVC architecture:
- Model: SQLite database with tables for books, sessions, and tags, enforcing relational integrity and foreign keys.
- View: Qt widgets for library management, reading session controls, and analytics dashboards.
- Controller: Glue logic that coordinates user actions, database updates, and UI refreshes.
To keep the codebase maintainable, I leaned on classic design patterns:
- Factory for creating model objects from query results.
- Observer to keep views in sync when the underlying data changes.
- Strategy to swap in different reading‑speed estimation policies without touching the rest of the app.
On top of this, I added an adaptive estimator that learns from historical sessions. Each session logs:
- Pages read
- Duration
- Subjective difficulty
- Genre
The estimator uses these features to predict the remaining time for a book and surfaces that in the UI as “time to finish.”
Tradeoffs
- Building on Qt and C++ gives me performance and control, but increases the boilerplate compared to, say, a web stack.
- SQLite is simple and embedded, but I had to be deliberate about migrations and indexing as the schema evolved.
- A custom estimator is more tailored than off‑the‑shelf “pages per minute,” but it requires careful data logging and validation.
Results
- A stable cross‑platform app that tracks my reading sessions and trends over time.
- Reliable, transaction‑safe database operations, even when the app is closed mid‑session.
- Time‑to‑finish estimates that feel accurate enough to plan reading blocks, instead of just guessing based on page counts.