From a2353e6a0346f25a1f623aba3981eb4de923ee10 Mon Sep 17 00:00:00 2001 From: Etienne Pichot Damon Date: Wed, 4 Nov 2026 10:34:28 -0500 Subject: [PATCH] feat(specifications): add change history, adopters chart and shared mobility modes History section on every specification page: - Read from GitHub's public API in the visitor's browser, so an open pull request appears as soon as it is opened, without a site rebuild. - "Proposed changes": open pull requests touching the entry's file, with "Review in progress", who proposed it, the start of the description and a Field / Before / Proposed table (vocabulary labels, at most 6 rows). - "Recent changes": the last 4 changes merged into main, with who proposed and who merged them; the field table is folded under "What changed". Commits made without a pull request show as "Committed". - Responses are cached for 10 minutes in sessionStorage and shared between pages to stay within the 60 unauthenticated calls per hour. When GitHub cannot be reached, the section falls back to a link to the file history. - Before/after values are compared by parsing both revisions of the file with js-yaml 4.1.0 (MIT), vendored under overrides/assets/javascript/vendor and loaded only on specification pages. Known adopters chart in the Adoption section: - Built at build time from the git history of each data file (scripts/spec_history.py). One point per count date; edits that leave the count unchanged are ignored. - Shown only when there are at least 2 points. Time on the x axis, adopters from 0 on the y axis, no legend, latest value labelled, exact values on hover/focus and in a screen-reader table. - Without git history (shallow clone) the chart is simply left out. Modes: add bikeshare, shared_scooter and carsharing to the vocabulary and data/README.md. Co-Authored-By: Claude Opus 5.5 --- data/README.md | 2 +- hooks/specifications.py | 157 ++++++- overrides/assets/javascript/specifications.js | 426 ++++++++++++++++++ .../assets/javascript/vendor/js-yaml.LICENSE | 21 + .../assets/javascript/vendor/js-yaml.min.js | 2 + .../assets/stylesheets/specifications.css | 275 ++++++++++- scripts/spec_data.py | 10 +- scripts/spec_history.py | 119 +++++ 8 files changed, 1006 insertions(+), 6 deletions(-) create mode 100644 overrides/assets/javascript/vendor/js-yaml.LICENSE create mode 100644 overrides/assets/javascript/vendor/js-yaml.min.js create mode 100644 scripts/spec_history.py diff --git a/data/README.md b/data/README.md index 0745650..f9e865a 100644 --- a/data/README.md +++ b/data/README.md @@ -85,7 +85,7 @@ Travel information: Transit schedules, geography, fares. | `full_name` | Free text. | | `homepage_url` | URL. | | `status` | The coalition's overall finding: `compliant`, `partially_compliant`, `not_compliant`, `inactive` or `under_review`. | -| `modes` | Any of `public_transport`, `microtransit_drt`, `taxi_ridehail`, `bicycle`, `pedestrian_accessibility`, `parking`. Empty shows as "Unclassified". | +| `modes` | Any of `public_transport`, `microtransit_drt`, `taxi_ridehail`, `bicycle`, `bikeshare`, `shared_scooter`, `carsharing`, `pedestrian_accessibility`, `parking`. Empty shows as "Unclassified". | | `maintainers` | Organization ids: file names in `organizations/`. | | `licences` | A list of licences, each with an `id` from `licences.csv` and optional `scopes`: `specification`, `documentation`, `code`, `data`. Empty means no licence is stated. | | `licence_evidence_url` | URL of the licence text, when it is not the licence's own page. | diff --git a/hooks/specifications.py b/hooks/specifications.py index b0d5794..fdfd75d 100644 --- a/hooks/specifications.py +++ b/hooks/specifications.py @@ -38,6 +38,7 @@ import spec_api # noqa: E402 (path set above) import spec_data # noqa: E402 +import spec_history # noqa: E402 SECTION = "specifications" @@ -642,6 +643,7 @@ def render_detail(spec, data, base): sections.append(("adoption", "Adoption", "account-group")) if spec["maintainers"]: sections.append(("maintainers", "Maintainers", "domain")) + sections.append(("history", "History", "history")) o.append('