Export a CV as Europass and as JSON Resume #1235

Closed
tiagoagueda wants to merge 2 commits from f-1100 into main
Owner

Closes #1100: a CV can be downloaded as Europass and as JSON Resume.

  • Two new sub-modules of Extend file formats, "Europass export" and "JSON Resume", each with its own switch; off, the format is not offered and its address answers 404. Letters are offered neither.
  • On a CV's page, under "In other formats", each row says what the format leaves out for this CV ("Left out, because this format has no place for them: …"), the issue's decision.
  • The files hold only what the CV prints, in its order and language.
  • JSON Resume is validated against the published schema 1.2.1 (vendored, MIT, with its source beside it). Europass is the SkillsPassport JSON 3.3.0, the format Postulo's own Europass reader reads: a CV written and read back gives back the person, jobs, education, languages, skill groups and projects. Its schema has no licence found, so it is not vendored; the test spells out the constraints the writer relies on.
  • Test-only dependency fastjsonschema 2.22.3 (BSD-3-Clause, pure Python, no advisories).

For review: europass.europa.eu today writes a Candidate XML, not this JSON; SkillsPassport JSON was chosen as the only Europass with a JSON Schema and the one Postulo reads back. Postulo's reader turns every Europass achievement block into a project, so certifications and the like come back as projects (a possible follow-up).

Tested: the new tests fail without the change (26 pass with it); documents, Europass, formats, manifests, icons and query-budget tests, and the browser tests of the CV pages, accessibility and reflow. No migration, no archive change; new strings extracted for Weblate.

🤖 Generated with Claude Code

Closes #1100: a CV can be downloaded as Europass and as JSON Resume. - Two new sub-modules of *Extend file formats*, "Europass export" and "JSON Resume", each with its own switch; off, the format is not offered and its address answers 404. Letters are offered neither. - On a CV's page, under "In other formats", each row says what the format leaves out for this CV ("Left out, because this format has no place for them: …"), the issue's decision. - The files hold only what the CV prints, in its order and language. - JSON Resume is validated against the published schema 1.2.1 (vendored, MIT, with its source beside it). Europass is the SkillsPassport JSON 3.3.0, the format Postulo's own Europass reader reads: a CV written and read back gives back the person, jobs, education, languages, skill groups and projects. Its schema has no licence found, so it is not vendored; the test spells out the constraints the writer relies on. - Test-only dependency `fastjsonschema` 2.22.3 (BSD-3-Clause, pure Python, no advisories). For review: europass.europa.eu today writes a Candidate XML, not this JSON; SkillsPassport JSON was chosen as the only Europass with a JSON Schema and the one Postulo reads back. Postulo's reader turns every Europass achievement block into a project, so certifications and the like come back as projects (a possible follow-up). Tested: the new tests fail without the change (26 pass with it); documents, Europass, formats, manifests, icons and query-budget tests, and the browser tests of the CV pages, accessibility and reflow. No migration, no archive change; new strings extracted for Weblate. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A CV could be read in from Europass but never written back to it, and
portals across Europe take Europass while many tools read JSON Resume:
a CV that left only as a PDF or a Word file was retyped into each.

Both are now formats of a CV beside text, OpenDocument and Word, each a
sub-module of Extend file formats ("Europass export", "JSON Resume").
A format may now be handed the CV as values (documents.structured)
instead of its outline: the variant's chosen entries in its order and
language, and the personal details it prints, nobody where its contact
block is off. Such a format is never offered for a letter.

The Europass writer sits beside the reader in the europass plugin and
writes the SkillsPassport JSON (schema 3.3.0) with the reader's own
mapping, so a CV written as Europass reads back through Postulo's
importer as what was written; a test holds that. Long fields are HTML,
as the format's are, with what was typed escaped. JSON Resume (schema
1.2.1) is written in its own fields.

What a format has no place for is left out of the file and named under
its download on the CV's page. The JSON Resume schema (MIT) is vendored
under tests/data and the file is checked against it; the Europass
schema states no licence, so the test writes out the constraints of it
the writer relies on. fastjsonschema (BSD-3-Clause, pure Python, no
dependencies, no advisories) is a new test-only dependency.

Closes #1100

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Record #1100 in the changelog and extract its strings
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 14m1s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 1m55s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m11s
CI / Security audit (pull_request) Successful in 25s
CI / Browser tests (Chromium) (pull_request) Successful in 34m19s
958aa124c6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tiagoagueda force-pushed f-1100 from 958aa124c6
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 14m1s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 1m55s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m11s
CI / Security audit (pull_request) Successful in 25s
CI / Browser tests (Chromium) (pull_request) Successful in 34m19s
to bf8e4a9955
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 13m58s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m4s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m4s
CI / Security audit (pull_request) Successful in 20s
CI / Browser tests (Chromium) (pull_request) Successful in 33m9s
2026-11-12 01:51:14 +00:00
Compare
Author
Owner

Superseded by #1237, which stacks this branch with two other features so they share one CI run (each merge here is fast-forward-only, so landing them one by one would mean a CI run each after every rebase). The commits are the same.

🤖 Generated with Claude Code

Superseded by #1237, which stacks this branch with two other features so they share one CI run (each merge here is fast-forward-only, so landing them one by one would mean a CI run each after every rebase). The commits are the same. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tiagoagueda closed this pull request 2026-11-12 02:11:18 +00:00
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 13m58s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m4s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m4s
CI / Security audit (pull_request) Successful in 20s
CI / Browser tests (Chromium) (pull_request) Successful in 33m9s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo!1235
No description provided.