Skip to content

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.