A personal analytics dashboard built with Streamlit to track workouts, sleep, and recovery over time, with a strong focus on data integrity, transparency, and rehabilitation monitoring.
🧪 No data? Click "Try with synthetic data" inside the app — it ships with a fully synthetic demo dataset (zero real health data), so every tab, model and SHAP plot is explorable in one click.
demo-readme.mp4
This app was created out of a real personal need.
After experiencing a Cerebrovascular Accident (CVA), I needed a reliable way to:
- track physical rehabilitation progress,
- monitor training load and recovery,
- understand how sleep and physiological signals affect performance,
- and avoid misinterpreting incomplete or delayed data.
Most fitness apps show numbers without context. This dashboard is designed to show what the data actually represents, how recent it is, and how confident we should be when interpreting it.
Moreover, to practice statistical tests and machine learning models to perform a diagnostic analysis where the key question is why it happened.
The app integrates three independent data sources:
- Exercise-level tracking
- Estimated 1RM (Epley formula)
- Weekly volume and RPE trends
- Pre vs post CVA comparisons
- Sleep score
- Sleep stages (REM / Light / Deep)
- Wake count
- Longitudinal trends
- Composite recovery score (0–1)
- Components such as HRV, stress, resting heart rate
- Transparent visualization of trends and variability
- Performs a time series analysis
- ACF and PACF plots
- Statistical tests (ADF, KPSS) for stationarity
- An insight based on the TSA on whether to use time series models
- Location estimate
- Variability estimate
- Outliers detection
- Hypothesis testing
- Use ML to interpret and explain what happened with a regression problem (Sleep Score) and a classification problem (Good or Bad sleep)
- Supervised models for regression and classification
- Unsupervised models and Dimensionality Reduction for plotting
- Weekly snapshot (not noisy daily metrics)
- Data freshness badges Clearly shows how many days old each data source is
- Integrity-first design The app never assumes “today’s data” if it doesn’t exist
- Collapsed upload panel after data is loaded
- Correlation analysis using weekly aggregation (appropriate for 3–4 training days/week)
- Dark mode support 🌙
This dashboard is intentionally conservative.
- If data is delayed, it is shown as delayed
- No forward-filling or artificial alignment
- Weekly aggregation is preferred when frequencies differ
- Visual cues explain how confident we should be in the metrics
This is especially important in a rehabilitation context, where misinterpreting trends can lead to poor decisions.
The project separates a private ingestion pipeline from a public dashboard.
Google Sheets ─┐
Garmin (xlsx) ─┼─► rehab_strength.ingest ─► data/processed/*.csv ─► Streamlit app
Strong (csv) ─┘ (local only) (git-ignored) (manual upload)
Inside the app:
- Upload → normalize → store in
st.session_state - UI widgets never re-read raw files
- All downstream logic reads from validated in-memory data
- Deterministic behavior across reruns and tab changes
├── streamlit_app.py # Dashboard entrypoint (deployed to Streamlit Cloud)
├── app/
│ ├── helpers/ # Pure stats/transforms + plotting helpers
│ └── tabs/ # One module per tab; models/ split into
│ # regression/, classification/, unsupervised
├── src/rehab_strength/
│ ├── config.py # Paths & settings, all relative to the repo root
│ ├── gsheets.py # Google auth — credentials resolved from the environment
│ └── ingest/
│ ├── sleep.py # Sheets + Garmin → sleep & recovery datasets
│ ├── strong.py # Strong export → clean workouts
│ └── run_all.py # Full pipeline
├── data/{raw,processed,external}/ # Health data — git-ignored
├── data/synthetic/ # Synthetic demo dataset — versioned (no real data)
├── models/ # Trained models — git-ignored
├── reports/figures/ # Generated plots — git-ignored
├── assets/ # Curated screenshots for this README — versioned
├── notebooks/ # Exploration — outputs stripped, .ipynb git-ignored
└── tests/
- Python 3.13 — pinned via
.python-versionfor parity with Streamlit Cloud - Streamlit — dashboard
- Pandas / NumPy — data wrangling
- Matplotlib / Seaborn — visualization
- scikit-learn / statsmodels / SHAP — modeling and diagnostics
- Google Sheets & Drive API — sleep ingestion
- pytest / ruff / pre-commit / GitHub Actions — quality gates
git clone git@github.com:POLUX89/rehab-strength.git
cd rehab-strength
make setup # venv + dependencies + pre-commit hooks
source .venv/bin/activate
make app # http://localhost:8501The dashboard runs on uploaded CSVs — no credentials required. Drop your three
cleaned CSVs into the upload panel and every tab unlocks. Or skip the uploads
entirely: "🧪 Try with synthetic data" loads the bundled synthetic dataset
(regenerate it anytime with make synthetic).
Requires a Google service account with read access to the Health Metrics sheets:
cp .env.example .env # point GOOGLE_APPLICATION_CREDENTIALS at your key
make ingest # → data/processed/*.csvPlace the Garmin exports (HRV_status.xlsx, Sleep_garmin.xlsx) and the Strong
export (strong.csv) in data/raw/ first. See data/README.md.
Then upload the three regenerated CSVs to the app — the dashboard never reads the filesystem, only what you hand it.
make test # run the test suite
make lint # style checks
make check-secrets # scan for leaked credentialsscripts/run_pipeline.sh runs the full ingestion, logs to
~/Library/Logs/rehab_strength_pipeline.log and fires a macOS notification — including
when it fails, so stale data never passes silently.
Wire it to run whenever the Mac wakes up, using sleepwatcher:
brew install sleepwatcher
brew services start sleepwatcher
cat > ~/.wakeup <<'SH'
#!/bin/zsh
"$HOME/dev/rehab-strength/scripts/run_pipeline.sh"
SH
chmod +x ~/.wakeupThe script resolves the repo root from its own location, so it works from any clone.
Override the notification app with REHAB_NOTIFY_APP; without one it falls back to
osascript.
All changes are tracked in CHANGELOG.md, following semantic versioning principles.
This project is for personal use, and it is built so the code can be public while the data never is.
- No personal data in the repo. Every health file lives under
data/, which is git-ignored. CSV, XLSX and PDF are blocked repo-wide by default. - No credentials in the repo. Google service account keys are resolved at runtime
from environment variables or Streamlit secrets — never from a file in the tree.
See
.env.exampleand.streamlit/secrets.toml.example. - The deployed app holds no secrets. It reads manually uploaded CSVs and never contacts Google.
- Defense in depth.
pre-commitrunsgitleaksandnbstripouton every commit; CI runsgitleaksacross the full history. - Notebook outputs are stripped, since rendered cells embed personal health data.
The ingestion code is published; the data it ingests is not.
This tool is not a medical device. It is intended for personal tracking and insight, not diagnosis or medical advice.
This project represents a transition from:
“just tracking numbers” to understanding recovery, uncertainty, and progress over time.
It is both a technical project and part of an ongoing rehabilitation journey.


















