Skip to content

chore: bring master into #2231 - #2400

Merged
Nayor merged 148 commits into
feature/outings-heatmapfrom
sync/feature/outings-heatmap
Oct 26, 2026
Merged

Nayor merged 148 commits into
feature/outings-heatmapfrom
sync/feature/outings-heatmap

Conversation

@lbesson

@lbesson lbesson commented Oct 24, 2026

Copy link
Copy Markdown
Member

Brings master into #2231 and adapts the branch to it. This PR targets feature/outings-heatmap: merging it updates #2231.

  • Merge of master: no conflicts.
  • Adaptation commit (8923d583):
    • tests converted to plain assert; assertAlmostEqual(places=N) became pytest.approx(abs=5e-(N+1)), the same tolerance;
    • 7 docstrings given a one-line summary;
    • f-string fixes (UP032) and an unused noqa removed.

What changed on master that this PR adapts to

  • Tests are plain pytest classes: BaseTestCase no longer derives from unittest.TestCase, so self.assertEqual, self.assertTrue, self.assertRaises, self.subTest… no longer exist. Use plain assert, pytest.raises, pytest.approx and the subtests fixture (pytest 9).
  • ruff: line length 88, single quotes, ruff format enforced in CI. More rules are enabled: isort (I), bugbear (B), pydocstyle with the pep257 convention (D), naive datetimes (DTZ) and f-strings (UP031/UP032). Docstrings need a one-line summary, then a blank line.
  • Stack: Python 3.15, SQLAlchemy 2.1, geoalchemy2 0.20.

make lint and the full test suite pass on this branch.

🤖 Generated with Claude Code

lbesson and others added 30 commits October 21, 2026 15:13
bcrypt 5 raises ValueError for passwords longer than 72 bytes instead
of silently truncating them; PasswordUtil already truncates to 72
bytes before hashing and checking (#2299), so existing hashes and long
passwords keep working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Security and maintenance releases of the HTML sanitiser used for
markdown rendering (6.2.0 -> 6.4.0). bleach is deprecated upstream but
still gets fixes; moving to nh3 is planned separately (nh3 removes
disallowed tags where bleach, configured with strip=False, escapes
them, so it changes the rendered output).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ruff lint rule I (isort), first-party packages c2corg_api and
  es_migration; make format / ruff check --fix sort them
- devcontainer: organize imports on save; installs the pre-commit hook
- .pre-commit-config.yaml: a local hook running `uv run --frozen ruff
  check --fix` and `ruff format`, i.e. always the ruff version locked
  in uv.lock (no second pin to keep in sync); pre-commit 4.6.2 as a dev
  dependency
- docs

The imports themselves are sorted in the next commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mechanical: `ruff check --select I --fix c2corg_api es_migration`
(then `ruff format`), with the isort settings from the previous commit.
Listed in .git-blame-ignore-revs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
style: sort imports with ruff, pre-commit hook
`make install` runs `uv run --frozen pre-commit install` after
`uv sync`, like the devcontainer setup already does, so the ruff hook
is set up without an extra step. HOWTO-DEV uses `make install`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build: make install also installs the pre-commit hook
6.4.0 is the highest version that kombu 5.6.2 supports (redis <6.5).
With redis-py 7+, kombu 5.6.2 silently drops health_check_interval, so
the syncer's BRPOP connections would lose their health checks. Kombu
5.7 fixes this and allows redis <9; bump both together once 5.7 is
final.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Checked against the production command line (sync workers, --paste,
--capture-output, --statsd-host, --proxy-protocol, --proxy-allow-from):
since 24.1, --proxy-protocol takes a mode, and the bare flag means
`auto` (PROXY v1 or v2), which still matches HAProxy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /forum/private-messages/unread-count has no caller in c2c_ui.
Forum notification counts will be read by the UI directly from
Discourse (session/current.json) instead of going through the admin
API key. Also drop APIDiscourseClient.get_username and its cache,
which were only used by this endpoint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Markdown stays on 3.10.x: every 3.11 release drops the text after an
unmatched backtick run (Python-Markdown#1668, fixed on master but not
released). 3.10.3 fixes several cases where 3.8.1 lost text after a
stray `<`, `</` or `<!--`. The new html_edge_cases fixtures pin both,
so the next bump shows any change.

pymdown-extensions: only its emoji1_db data is used, unchanged in 12.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build: bump Markdown to 3.10.3 and pymdown-extensions to 12.1
chore: remove unused forum private messages unread count endpoint
Discourse now binds the DiscourseConnect nonce to the browser session that
requested it (discourse_connect_csrf_protection, on by default), so the nonce
the API fetches server-side for the login response (redirect_internal) is
rejected with a 419 when the browser's hidden iframe uses it: logging in on
the topoguide no longer opens a forum session.

The UI will instead let the browser start the handshake on the forum, which
sends it to /auth-sso with a signed sso/sig payload. This endpoint answers it
for the already logged-in user with the signed sso_login URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(sso): add /users/discourse-redirect for browser-initiated forum SSO
dogpile.cache 1.5.0, pyramid_tm 2.6, python-json-logger 4.2.0,
python-slugify 9.1.3, requests 2.34.2, transaction 5.1, pytz 2026.5.

- production.ini: use pythonjsonlogger.json.JsonFormatter, the
  deprecated pythonjsonlogger.jsonlogger alias still exists in 4.x.
- pyramid_tm >= 2.4 annotates transactions with
  request.authenticated_userid, which runs groupfinder (one query) on
  every authenticated request. Turn tm.annotate_user off: the annotation
  is only used by ZODB-style transaction logs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build: bump small runtime libraries
Pin pytest and pytest-cov exactly like the other dependencies, and
upgrade ipython (an ipdb dependency) from 8.18.1 to 9.17.1 in the lock.

Closes #2356

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build: bump pytest to 9.1.1, pytest-cov to 7.1.0, WebTest to 3.0.7
- `make audit` runs `uv audit` on the lock; a new CI job runs it.
  Advisories that can't be fixed yet are listed in the Makefile with
  their reason: urllib3 1.26 (elasticsearch 2.4.1 requires urllib3<2)
  and setuptools < 83 (held below 81 for Pyramid's pkg_resources).
- Fix the rest in the lock: mako 1.4.3, soupsieve 2.10.
- Drop the six Snyk floor pins on indirect dependencies: the lock
  already satisfies them, and zipp was not needed by anything.

Part of #2357

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pytz was only used for its UTC timezone. The standard library's
datetime.UTC is the same, and datetime.now(UTC) also replaces the
deprecated datetime.utcnow() in the two places that localized it.

Part of #2357

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- uv: minor/patch updates of dev tools, the SQLAlchemy family and the
  Pyramid family come as one PR each; all other patch updates as one
  PR. Major updates keep their own PR.
- uv: ignore the versions held back on purpose (elasticsearch 2.4,
  pyramid 2, setuptools >= 81, apscheduler 4, redis-py >= 7), each
  with its reason.
- GitHub Actions: one grouped PR.

Part of #2357

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
refactor: replace pytz with datetime.UTC
build: audit dependencies in CI, drop stale Snyk pins
ci: group Dependabot updates, ignore held-back versions
lbesson and others added 22 commits October 26, 2026 08:25
Part of the Colander → Pydantic step (#2364), stacked on coverages.

types.py gets Date and Bool, which validate like colander.Date (the
ISO 8601 format of iso8601.parse_date, ported; any falsy value is
missing) and colander.Boolean (only 'false' and '0', in any case, are
false). The parity harness generates date and boolean cases.

c2corg_api/schemas/xreport.py: the xreport schema with its locale
fields, its create and update schemas, and the ModelSchemas of the
xreport, of its listing and of the xreport without personal
information, used by the xreport views. The Colander xreport schemas
stay for the parity tests only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
refactor: validate and map xreports with Pydantic
Part of the Colander → Pydantic step (#2364), stacked on xreports.

types.py gets FiniteFloat (like colander_ext.FiniteFloat: NaN and
infinity give null) and DateTime (like the ColanderAlchemy DateTime:
ISO 8601, naive without a time zone). The parity harness generates
float and datetime cases.

c2corg_api/schemas/image.py: the image schema (a title may be empty),
its create and update schemas, the image list schema, and the
ModelSchema of the image and its listing, used by the image views. The
Colander image schemas stay for the parity tests only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
refactor: validate and map images with Pydantic
Part of the Colander → Pydantic step (#2364), stacked on images.

c2corg_api/schemas/user_profile.py: the profile schema (its locales
have no title: the title is the user's name), its update schema, and
the ModelSchemas of the profile, of its listing and the internal one
the update uses (with the locale titles the view sets to ''). The
Colander profile schemas stay for the parity tests only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part of the Colander → Pydantic step (#2364), stacked on profiles.

Seq takes a minimum length (activities needs at least one), with
Colander's message.

c2corg_api/schemas/outing.py: the outing schema with its locale
fields and line geometry, its create and update schemas, and its
ModelSchema, which the outing views and the per-activity schema
adaptors (ModelSchema.restrict) use. The Colander outing schemas stay
for the parity tests only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part of the Colander → Pydantic step (#2364), stacked on outings.

types.py gets Float (like colander.Float: NaN is kept, unlike
FiniteFloat). The parity harness treats NaN as equal to NaN.

c2corg_api/schemas/route.py: the route schema with its locale fields
(title_prefix included) and line geometry, glacier_gear defaulting to
'no' as its column did, its create and update schemas, and its
ModelSchema, used by the route views, the per-activity schema adaptors
and the navitia views. The Colander route schemas stay for the parity
tests only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part of the Colander → Pydantic step (#2364), stacked on routes. The
last document type.

c2corg_api/schemas/waypoint.py: the waypoint schema with its locale
fields and point geometry, its create and update schemas, and its
ModelSchema, used by the waypoint views, the per-type schema adaptors
and the navitia views. No view uses restrict_schema any more. The
Colander waypoint schemas stay for the parity tests only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part of the Colander → Pydantic step (#2364), stacked on waypoints.

The body schemas of protect/unprotect, merge, revert, version
mask/unmask, tags, follow/unfollow and block/unblock are now Pydantic
schemas, defined where the Colander ones were. test_action_schemas.py
keeps a copy of the Colander definitions and checks the new schemas
validate exactly like them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dantic

Part of the Colander → Pydantic step (#2364), stacked on the action
schemas.

- c2corg_api/schemas/association.py: the association schema and its
  ModelSchema (objectify), used by the association views;
- the feed preferences and forum topic body schemas, defined where the
  Colander ones were.

test_misc_schemas.py checks them against the Colander schemas (a copy
of the view definitions for the two that lived in views).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part of the Colander → Pydantic step (#2364), stacked on the
association, preferences and forum schemas.

types.py: string fields take a list of checks, which all run and join
their messages with '; ' like colander.All: minimum and maximum length,
choices, email (colander.Email's regular expression and message) and
function_check (like colander.Function). base.py: DROP, the default of
a field left out of the validated data when missing (colander.drop).

c2corg_api/schemas/user.py: the registration schema and the
ModelSchemas that build a user from it and serialize a user. The login,
Discourse redirect, preferred language, account update and SSO body
schemas are defined where the Colander ones were. test_user_schemas.py
checks them against the Colander schemas (the account update one in a
database test, as its checks look up the users).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Last part of the Colander → Pydantic step (#2364): nothing uses the
Colander schemas any more.

- The Colander schemas, ColanderAlchemy column hints and the
  schema_utils helpers are removed from the models; schema_utils keeps
  the field lists of the listing restriction.
- ext/colander_ext.py is gone, and with it the global geojson patch
  for 4D positions (the Pydantic geometry type checks 2/3/4D itself).
  The GeoJSON/WKB conversions move to ext/wkb.py.
- serialize() and is_missing() no longer know colander.null.
- colander, the ColanderAlchemy fork and iso8601 leave the
  dependencies.
- The parity tests needed Colander: test_types.py now pins the
  behaviours they established (messages, conversions, missing values,
  dates, booleans, floats, joined check messages, geometries), and
  test_model_schema.py the model mapping. The colander_ext tests move
  to test_wkb.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
refactor: remove Colander and ColanderAlchemy
test: stop the Discourse client mocks leaking between tests
fix: reindex the outings of a route when its waypoints change
The fork (6.1.0+c2corg) only added cornice PR #359, which turns None
into colander.null before Colander validation. Nothing validates with
Colander any more (#2364), so upstream cornice 6.1.0, the latest
release, does the same job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build: use upstream Cornice instead of the c2corg fork
A geometry whose positions have different dimensions (a LineString
mixing [x, y] and [x, y, z, m] positions, say) failed in geomet during
the WKB conversion, and the API answered 500. c2c_ui never sends that
(OpenLayers geometries have one layout), but other clients can.

The geometry type now gives all the positions the lowest dimension found
in the whole geometry, dropping the trailing values: 2D + 3D gives 2D,
3D + 4D gives 3D, 2D + 4D gives 2D. A PostGIS geometry has a single
dimension, so this applies across the lines of a MultiLineString too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: give mixed-dimension geometries their lowest dimension
@Nayor

Nayor commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Merge has moved since this PR, I've updated it

lbesson and others added 5 commits October 26, 2026 09:37
The items of the array fields backed by an enum (activities,
categories, route_types, rock_types, best_periods, ...) were not
checked, by Colander before nor by the Pydantic schemas that kept that
behaviour: an invalid value reached the database enum type and the API
answered 500. The routes and outings checked only their first activity.

The schemas now check every item against its enum and answer 400 with
the error on the item (e.g. activities.1: "x" is not one of ...).

For routes and outings, an invalid first activity used to give the
error {name: activities, description: 'invalid value: x'}; it is now
the schema error above. As with any schema error, the association
checks also report their missing data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Real profiles get a version at registration; the profiles of the global
test users did not, so they could not be updated and the profile PUT
contract test had to register a new user. The test data now creates
those versions like the registration does.

The contract test of PUT /{type}/{id} now covers profiles too. The
tests that created one of these versions themselves, or counted
versions, history entries and cache versions, are adjusted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: give the global test users' profiles their first version
fix: validate the items of the enum arrays
@Nayor
Nayor merged commit ee775d1 into master Oct 26, 2026
@Nayor
Nayor deleted the sync/feature/outings-heatmap branch October 26, 2026 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

modernization Stack upgrade: Python, libraries, SQLAlchemy 2, Pydantic, FastAPI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants