Skip to main content

🏋️ KaiserLift

PyPI version Python 3.8+ License: MIT

A smarter way to choose your next workout: data-driven progressive overload

🎯 Never guess your next workout again — KaiserLift analyzes your training history and tells you exactly which exercise, weight, and rep range will give you the easiest PR.

✨ Quick Start

Installation

pip install kaiserlift

Or with uv (recommended):

uv pip install kaiserlift

Run the Web Interface

pip install "kaiserlift[server]"
kaiserlift-cli

Then open http://localhost:8000 in your browser and upload your FitNotes CSV export.

Use as a Python Library

from kaiserlift import pipeline

# Generate interactive HTML with workout recommendations
html = pipeline(["your_fitnotes_export.csv"])
with open("workout_plan.html", "w") as f:
    f.write(html)

CSV Data Format

Your CSV should follow the FitNotes export format:

Date,Exercise,Category,Weight,Reps
2022-09-14,Flat Barbell Bench Press,Chest,45.0,10
2022-09-14,Dumbbell Curl,Biceps,35.0,10

Why keep doing 10 rep sets? Are you pushing in a smart way?

The core idea I’m exploring is simple: I want a science-based system for determining what’s the best workout to do next if your goal is muscle growth. The foundation of this is progressive overload—the principle that muscles grow when you consistently challenge them beyond what they’re used to.

One of the most effective ways to apply progressive overload is by taking sets to failure. But doing that intelligently requires knowing what you’ve done before. If you have a record of your best sets on a given exercise, you can deliberately aim to push past those PRs. That history becomes your benchmark.

Rawdata Curl Pulldown

Now, there’s another dimension to this: rep range adaptation. If you always train for, say, 10 reps, your muscles can get used to that rep range—even if you’re still pushing for PRs. Switching things up and going for, say, 20-rep maxes (even with lighter weight) can stimulate growth by forcing the muscle to adapt to new challenges. Then, when you go back to 10 reps, you might find you’ve blown through a plateau.

To make this system more precise, I propose using a one-rep max (1RM) equivalence formula—a way of mapping rep and weight combinations onto a single curve. It gives you a way to compare different PRs across rep ranges. Using that, you can identify which rep range you’re weakest in—meaning, which PR has the lowest 1RM equivalent. That’s where your next opportunity lies.

Curl Pulldown with Pareto

For the 1 rep max we use The Epley Formula: $$ \text{estimated_1rm} = \text{weight} \times \left(1 + \frac{\text{reps}}{30.0}\right) $$

Here's how to operationalize it:

  1. Collect your full workout history for a given exercise—every weight and rep combo you’ve done.
  2. Calculate the Pareto front of that data. This is the set of “non-dominated” performances: the heaviest weights at each rep range that can’t be beaten in both weight and reps at the same time.
  3. For each point on the Pareto front, compute the 1RM equivalent using a decay-style formula.
  4. Identify the Pareto front point with the lowest 1RM equivalent—that’s your weakest spot.
  5. Now, generate a “next step” PR target: a new set that just barely beats that weakest point, by the smallest reasonable margin (e.g., +1 rep or +5 lbs). That becomes your next workout goal.

This method gives you a structured, data-driven way to chase the easiest possible PR—which is still a PR. That keeps you progressing without burning out.

You can extend this concept across exercises too. Let’s say it’s biceps day. Instead of defaulting to the same curl variation you always do, you can rotate in a bicep exercise you haven’t done recently in order to assure a new PR. This introduces variability, which is another powerful way to drive adaptation while still targeting the same muscle group.

Curl Pulldown with Targets

The end goal here is simple: use your data to intelligently apply progressive overload, break through plateaus, and train more effectively—with zero guesswork.

To this end I also made an HTML page that can organize these in a text searchable way and can be accessed from my phone at the gym. This table of taget sets can be ordered by 1RPM and so easilly parsed.

Curl Pulldown Example - HTML

Advanced Usage

Development

Set up a local environment with uv:

uv venv
uv sync --extra dev

# or, with pip:
pip install ".[dev]"

Run the full test suite (includes benchmarks via pytest-benchmark):

uv run pytest

Before committing, run the pre-commit hooks locally to mirror CI and avoid formatting failures:

uvx pre-commit run --all-files

This installs the pinned ruff-format version and rewrites files if needed, so rerun the command until it reports no changes.

Import data and run the pareto calculations:

import glob

from kaiserlift import (
    process_csv_files,
    highest_weight_per_rep,
    df_next_pareto,
)
csv_files = glob.glob("*.csv")
df = process_csv_files(csv_files)
df_pareto = highest_weight_per_rep(df)
df_targets = df_next_pareto(df_pareto)

Plotting the data:

from kaiserlift import plot_df

# Plot recorded sets
fig = plot_df(df, Exercise="Dumbbell Curl")
fig.write_image("build/Dumbbell_Curl_Raw.png")  # Requires the optional kaleido package

# Plot the Pareto front
fig = plot_df(df_pareto=df_pareto, Exercise="Dumbbell Curl")
fig.write_image("build/Dumbbell_Curl_Pareto.png")

# View with pareto and targets (the green x's)
fig = plot_df(df_pareto=df_pareto, df_targets=df_targets, Exercise="Dumbbell Curl")
fig.write_image("build/Dumbbell_Curl_Pareto_and_Targets.png")

Generate views:

from kaiserlift import (
    print_oldest_exercise,
    gen_html_viewer,
)

# Console print out with optional args
output_lines = print_oldest_exercise(df, n_cat=2, n_exercises_per_cat=2, n_target_sets_per_exercises=2)
with open("your_workout_summary.txt", "w") as f:
    f.writelines(output_lines)

# Print in HTML format for ease of use
full_html = gen_html_viewer(df)
with open("your_interactive_table.html", "w", encoding="utf-8") as f:
    f.write(full_html)

# Console print of the HTML
from IPython.display import display, HTML
display(HTML(full_html))

Example HTML generation in CI

An example dataset and helper script live in tests/example_use. You can generate the interactive HTML table locally with:

python tests/example_use/generate_example_html.py

The "Generate example HTML" job in the CI workflow runs the same script. When run on the main branch, the generated pages are deployed to GitHub Pages:

  • Landing page: index.html
  • Lifting demo: lifting/index.html
  • Running demo: running/index.html

The HTML is also available as a downloadable artifact for all branches.

Graph-first dashboards

The lifting dashboard opens with an exercise selected and its interactive graph above the target table. A star marks the proposed target with the lowest estimated 1RM; select a target marker or table row to inspect its weight and reps. The recommendation panel follows your selection. Formula and chart explanations are available under How to read this graph.

Running dashboards also show performance graphs before the target table. Both views share responsive styling. When using the FastAPI server, open /lifting/ or /running/ for sample dashboards. Static exports use the same chart initializer, including after importing a lifting CSV or restoring cached results.

Neo-retro training labs

The homepage, lifting lab, and running lab share a minimal early-web design: numbered navigation, a page directory, bold headings, square instrument panels, and a target register. Charts use the same paper, ink, cobalt, and green palette; recorded performances and proposed targets keep distinct marker shapes.

Each plot has Reset view and Save plot controls. Import data lives below the target register and is also linked from the page directory and homepage. The browser importer supports both lifting and running exports, keeps their saved histories separate, and preserves the current graphs when an import fails. Clearing saved data returns to the example performance map. The static homepage and FastAPI homepage use the same renderer; the server homepage also offers direct CSV upload forms.

Metadata

Release files for kaiserlift 0.1.104

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kaiserlift 0.1.104
File Size Uploaded Provenance
kaiserlift-0.1.104.tar.gz 422.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for kaiserlift 0.1.104
File Interpreter ABI Platform
kaiserlift-0.1.104-py3-none-any.whl Python 3 none any Details

Total release size: 474.4 kB

Release files / kaiserlift-0.1.104.tar.gz

Download URL kaiserlift-0.1.104.tar.gz
Size 422.2 kB
Tags Source
SHA-256 checksum
How to use checksums
fc95eb8af157a23c76654755e78821814a3a77732acc5b21b2b6a85d48bbc496
BLAKE2b-256 checksum
How to use checksums
88cc8b59b3d31adfa13b540c6763110f26ff15cc2b196cc5717216e5e8987937
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / kaiserlift-0.1.104-py3-none-any.whl

Download URL kaiserlift-0.1.104-py3-none-any.whl
Size 52.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
65f906470dbf93f6fda6dcd3a7e6ab581fa6c5f1eccda03944f69e964ce7cc11
BLAKE2b-256 checksum
How to use checksums
b57d95d81bc8f1d30eb8e6d6405dac1c29d7925564219a8a42b17ab0a08e5de2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for kaiserlift 0.1.104

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.104 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page