I keep a public training log at fortyandforged.com. Every session is a post: a date, a few headings, and a table of loads and reps per exercise. After about 150 of those posts, the one thing the site could not do was answer a simple question: how has my bench press moved over the last six months? This is a short, plain account of the WordPress plugin I built to answer it, and how it works.
The purpose
The log already held all the data. I did not want a second place to type numbers into, a spreadsheet to keep in sync, or a third-party fitness service holding my history. The rule for the plugin was simple: the published posts are the only source of truth, and the chart has to be derived from them. If a point on the chart cannot be traced back to a sentence in a post, it should not be on the chart.
What it does
The plugin is called Training Progression. It reads the published posts in the training-log category, finds the exercise tables inside them, works out the heaviest completed working set for each movement in each session, and stores that as an index. A block then draws those sessions as a line chart on any page. Every point on the chart links back to the post it came from.
That is the whole idea. Everything else in the plugin exists to make the extraction trustworthy and the chart pleasant to read.
How it works
1. Extraction
A PHP parser walks each post’s HTML with the DOM extension. It looks for a date paragraph, then for exercise headings followed by tables with Load, Sets × Reps and Notes columns. Warm-up sections are skipped. For each movement it keeps the heaviest working set that was actually completed, and it keeps the load convention separate: a barbell total, weight added to bodyweight, a per-hand dumbbell load and a machine setting are never mixed on one axis.
The parser is deterministic. It understands the formats I actually use and refuses to guess at the rest. A row it cannot classify is held for review rather than charted wrong.
2. The index
Extracted rows are saved as post meta on the post they came from. There are no custom database tables and the post content is never modified. Saving or re-categorising a post updates its rows automatically, and an admin page can re-index the whole archive in resumable batches. Manual corrections survive a re-index.
3. Review
Each post gets a Training Progression panel in the editor. It lists what the parser found, flags anything ambiguous, and lets me confirm, correct or exclude a row. The chart only reads approved rows. If the post text changes after a correction, the correction pauses until it is reconfirmed.
4. The block
The front end is a Gutenberg block built with the standard WordPress scripts toolchain, with Chart.js bundled locally. It fetches approved rows from two REST endpoints under its own namespace, both sent with no-store so a cache never serves stale lifts. A shortcode exists for places where a block cannot go:
[forty_forged_progression exercises="bench-press,back-squat"]
Features
- Up to six movements on one chart, chosen in the editor.
- Rep filters (any, 3+, 5+ completed reps) and assistance filters (raw, wrapped, mixed) so a chart can show only the sets that count for a given goal.
- Date range fields with 4- and 8-week presets, zoom, drag to pan, pinch on touch.
- A detail card for any point, with the session’s sets and a link to the source post.
- A keyboard-accessible data table underneath the chart with every source link.
- Optional goal lines per movement, drawn above the data and below the tooltip.
- Panels grouped by load convention, with a per-exercise switch to give any movement its own panel.
- Long gaps drawn dotted, warm-up-only sessions annotated, no interpolation and no zeros for missing days.
- A palette that matches the site by default and can be edited from the settings page.
Conventions it depends on
Because the parser is strict, the log has to be written consistently: an explicit date paragraph, separate Warm-Up and Working Sets headings, numeric loads in kilograms, and Sets × Reps in one column. One convention is personal. I lift raw unless a post says otherwise, so a session with no mention of elbow wraps is indexed as raw. A setting can flip that to “unspecified” for someone who logs differently.
Where it stands
The block is live on the fortyandforged.com home page. The plugin was built and tested in WordPress Studio, is at version 1.4.0, and ships its built assets so installing it needs no Node runtime. The versions so far:
| Version | What changed |
|---|---|
| 1.0.0 | Source-linked exercise chart, extraction, index, review panel. |
| 1.1.0 | Site-matched default palette with editable chart colors. |
| 1.2.0 | Raw-by-default assistance rule and wrist-wrap distinction. |
| 1.3.0 | Per-exercise graph panels. |
| 1.3.1 | Goal lines drawn beneath the tooltip. |
| 1.4.0 | Clicking a point scrolls its detail card into view. |
Nothing leaves the site. No analytics, no external chart service, no AI call at request time. The chart is a view over posts I already wrote, which is exactly what I wanted it to be.