Your career: the additional sections of the list, one shape for all of them and one checklist #692

Closed
opened 2026-10-03 01:59:04 +00:00 by tiagoagueda · 1 comment
Owner

The maintainer's list, under Your career, ends with “Additional accolades:” and eight names: “Presentations, Conferences, Seminars, Honours and awards, Memberships, References, Citations, Courses”. Postulo has a place for none of them. This is the tracker for the five issues that add them. It decides once how a new section is shaped and holds the list of every place a section has to be added, so each of the five says only what is different about it.

What exists today

  • The career is eight kinds in one registry. SECTIONS (src/postulo/resume/registry.py:28-80) gives each its slug, model, form and headings; OVERVIEW_ORDER (:83-91) is the seven the career page shows, the skills being shown inside their groups. Each is a ResumeItem (src/postulo/resume/models.py:34-62). None of the eight names exists; the nearest are LinkKind.PUBLICATION (:280), Certification (:215-228), Project (:198-212) and, for a person, jobs.Contact (src/postulo/jobs/models.py:604-646).
  • One set of views serves every kind, so a section brings no URL: create, edit, delete, move and “Other languages” take the slug from the address and answer 404 for an unknown one (src/postulo/resume/views.py:45-50, resume/urls.py). The isolation sweep therefore names one section, experience (tests/security/test_isolation_sweep.py:405-409); a new model has no isolation test unless one is written.
  • The registry says a new kind is one entry. It is about a dozen places (“adding a seventh kind later means adding one entry here”, registry.py:1-6). A few are held in step by a test (the candidate reader against RESUME_FIELDS, tests/test_candidate_file.py:393-398; the sidebar against OVERVIEW_ORDER, tests/test_career_sections.py; every create form, tests/test_career_ordering.py:230-250). Most are held by nothing.
  • A kind a renderer has no branch for falls into an else written for education. The plain-text and Word output reads item.name and item.role (src/postulo/documents/rendering.py:303-319), and its portfolio branch reads item.qualification and item.institution (:374-383), so a model without those fields raises AttributeError. The HTML themes print entry.item.name, role and the dates in the final else of base_cv.html:127-162, and the qualification and institution in that of base_portfolio.html:131-152. The wiki tells a plugin theme to extend these bases (Writing a plugin, “A theme, if you set documents”), so such a theme inherits whatever the bases do.
  • Search lists five of the models and its links miss the sections. search_career covers experience, education, projects, certifications and skills (src/postulo/core/search.py:458-515); its links end #experience, #education, #projects (:510), while the overview's anchors are section-<slug> (src/postulo/templates/resume/overview.html:39, resume/views.py:77-78), so a hit lands at the top of the page. Recorded as a fact; the five existing links are outside this list.
  • No career API, no removal dialog. src/postulo/api/routers/ has no resume module. #303's dialog covers the rows of Your details (src/postulo/accounts/removals.py); a career entry is deleted on a confirmation page (resume/views.py:304-313), which a new section gets for free.
  • A heading is a catalogue message. SectionSpec.label and .plural (registry.py:18-25) head the career page; SECTION_LABELS (documents/rendering.py:41-49) heads a CV, drawn in the CV's language (:259). The person cannot retitle a section. #689's built-in names are about languages and carry no section names.
  • What Europass does with the same names today. The plugin reads EmploymentHistory, EducationHistory, languages, skills, Projects and Others, the last two as Projects (src/postulo/plugins/europass/candidate.py:136-147, :603-619); any other part of the profile holding text is named in the review page's notice “nowhere to go in Postulo yet” (:622-644, tests/test_europass_candidate.py:550-577). The older format's Achievement, of every type, becomes one Project named after its heading (plugins/europass/reader.py:471-483, JSON :741-751).
  • The names are Europass's. The 2002 to 2017 Europass CV template lists under Additional information: Publications, Presentations, Projects, Conferences, Seminars, Honours and awards, Memberships, References, Citations, Courses and Certifications (Cedefop, Instructions for filling in the Europass CV, © European Union 2002-2017, p. 5). The note's list is that one, less what Postulo has or tracks elsewhere (#687, projects, certifications).

Checked against main at f09b6010b.

Proposal

  1. A guard before the first section. A test over SECTIONS (and over OVERVIEW_ORDER for the two lists a CV reads, SECTION_LABELS and the importer's CV-entry map, since a single skill is on a CV only inside its group) that fails, naming the section and the list, when a registered section is missing from TRANSLATION_SECTIONS, RESUME_FIELDS, RESUME_MODELS, candidate.KINDS_BY_BLOCK, SECTION_LABELS or the importer's two maps, unless it is on a short list of named exceptions each with its reason (one today: link is missing from the importer's CV-entry map, so a link on a CV is skipped on restore, which #470 fixes and so removes the exception; #696 adds one: a reference holds data about another person and the candidate file carries none). It also asks that each model has an explicit decision in TRANSLATABLE (an entry, or a named “translates nothing”) and that a CV holding one entry of every section renders to HTML, plain text and Word. The importer's maps are local variables (src/postulo/core/importer.py:532, :905); they become module-level, or are derived from export.RESUME_MODELS. The registry docstring is rewritten to point at the test. This is the one piece of work in this issue itself.
  2. The five issues, each landing alone on main with its tests and wiki lines:
    • #693 Honours and awards, and memberships
    • #694 Presentations, conferences and seminars
    • #695 Courses
    • #696 References: the people who will vouch for you
    • #697 Citations (1.0.0)
  3. The standing checklist, once. A child names only what differs.
    • Model: a ResumeItem with ordering = ("order", "pk"), a migration, an admin entry (resume/admin.py), demo data (core/management/commands/seed_demo.py). Form: resume/forms.py, help text in the project's voice (#205). Registry: SECTIONS and OVERVIEW_ORDER, which drive the career page, the sidebar anchors and counts, every view above and the CV picker (documents/forms.py:386-432).
    • Page: a row in templates/resume/overview.html where the entry's name and highlights are not what the row should say (it branches for links and skill groups, :74-99); the plain preview (templates/resume/preview.html, ResumePreviewView, resume/views.py:334-353, hard-coded per kind); the counts dictionary (:98-106), which no template under templates/resume/ reads.
    • Translations: resume/translatable.py:27-35 (absent means nothing is translatable, as for Certification, pinned by tests/test_career_languages.py:83-86) and TRANSLATION_SECTIONS (core/export.py:153-162).
    • The CV: SECTION_LABELS; _section_blocks and _entry_blocks (plain text and Word); a branch in base_cv.html and one in base_portfolio.html; and how a plugin theme that writes its own section loop would print an unknown kind.
    • The archive: RESUME_FIELDS and RESUME_MODELS (core/export.py:343-415), the importer's section_models and date_fields (core/importer.py:532-541) and its CV-entry map (:905-912), which is keyed by model name and has no link today (#470 completes it); FORMAT_VERSION (:80, 29 at f09b6010b) up by one from whatever it is when the commit lands, with its line in the history comment.
    • The candidate file: a Kind in resume/candidate.py:176-276 (names and dates say what makes two entries the same one; label, sub and ongoing what the review page prints), NOT_READ, CANDIDATE_FORMAT (core/export.py:104, 4) up by one with its line in SHAPES (tests/test_candidate_file.py:372-387), the counts on the file page (resume/views_candidate.py:35-49).
    • Search: a group in search_career if the section should be found, linking to the real #section-<slug>.
    • Europass: the reading, in the same commit, for the element in Prior art; Record (resume/importing.py:26-67, on postulo.plugins.api) gains a list with a default, and is_empty, counts, apply (:115) and the review page's COUNT_LABELS, _summarise, _to_session, _from_session (resume/views.py:503-661) follow.
    • API: none exists and none is added (one line in the commit says so). Everything else: CV print choice (#308) does not apply, as an entry is on a CV only when put there; an owner-scoping test for the model; account deletion; the wiki's Your career record (and CVs, and Writing a plugin if Record changed); the CHANGELOG line; strings in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349).
  4. Section names are English messages translated through the catalogues, with a pgettext context where the English word is ambiguous (below).

Decisions

These are recommendations the maintainer has not confirmed.

  • One model per section, or one table with a kind. Options: (a) a model per section with typed fields; (b) one table with a kind and general columns (title, organisation, role, date, place, link, text), which is how Europass held them until 2020, one Achievement per heading with a code and a text; (c) a table per shape (an event, a body, a person). Recommended: (a). Europass's current format types them (EventType, Referee, OrganizationAffiliation), so typed fields map to it one to one and a kind table would have to be taken apart again to be written. The sections differ in what is required and what is personal data (hours on a course, a person's agreement on a referee, a role at an event); a kind table makes every column nullable and every rule an if kind == in a form, a candidate reader and a CV renderer that are all written per model today, and the candidate reader and archive already name their blocks and identity fields per section (Kind.names, RESUME_FIELDS). The cost is six models and the checklist six times, which the guard is for.
  • How many sections eight names make. Recommended: six: Honours and awards, Memberships, one section for Presentations, conferences and seminars (#694), Courses, References, Citations. Europass's current format folds conferences and seminars into one element and keeps the person's own talks in another; one section with a type and a role covers the three names. Five issues, because honours and memberships are two small sections that share a decision about organisations.
  • Where they sit and how long the page gets. The career page goes from seven headings to thirteen (fourteen with #687). Options: (a) flat, in OVERVIEW_ORDER after Languages; (b) grouped under one “Additional information” heading, as Europass does; (c) empty ones hidden behind an “Add a section” menu. Recommended: (a), in the note's order with References moved to the end, where a CV puts them: events, honours and awards, memberships, courses, citations, references. The sidebar (#175) exists for this and its rule that an empty section is listed stays; an empty section costs one heading and one line. OVERVIEW_ORDER also orders the CV picker's groups; what a CV prints follows its own entries (documents/rendering.py:117-148). Revisit (b) or (c) if the page proves heavy.
  • Organisations: text or company. Memberships, honours and courses each name a body, and the career's organisation, institution and issuer are free text on purpose (#131, resume/models.py:65-74) that #683, #685 and #686 reopen. An association is not an employer. Recommended: each of the three starts as text, as Certification.issuer is today, so none waits on g1. Then it takes g1's mechanism unchanged, in a later commit of its own issue: the text stays the CV's wording and an optional link to one of the owner's companies is added, a company the career form creates being marked “from the career”, which leaves it out of the posting, contact and agency pickers, the table and the map until a posting, contact or application is attached. That marker is how a club or a prize body is kept from becoming somewhere to apply. #671's role registry offers them first, never required: division 94, “Activities of membership organisations” (section T in src/postulo/jobs/data/nace-2.1.json), for memberships; the “awarding body” role of #686 for honours; the “place of learning” role of #685 for courses. The note asks for companies only for experience, learning and certification, so this extension is the drafters' and the maintainer may decline it.
  • What the person can title. Options: (a) catalogue messages per language; (b) the Europass wording in each language, copied; (c) let the person name each section, as the Europass portal does for sections it has no field for (its FAQ: new sections can be added “with a title you choose”). Recommended: (a), with the Europass English headings as the source wording. Where a word is ambiguous a pgettext context fixes it: msgid "Reference" already means a reference letter (documents/kinds.py:65; “Référence” in the fr-FR draft) and msgid "Role" a job (“Poste”). (b) was not checked language by language and the portal's wording may change; (c) is a feature of its own.
  • Europass: when. Recommended: each child reads its own section from the Candidate XML in the commit that adds it, since a section nobody can import into is half done; the older Achievement paragraphs keep arriving as one Project each, because splitting prose into entries is a guess and an import saves nothing on a guess. No Europass writer is proposed (Postulo writes none, plugins/europass/__init__.py is an importer), but the fields are chosen so one could be written.
  • What a CV prints. Recommended: exactly what the person puts on it. The picker is opt-in per entry, so nothing from these sections prints by default. #696 adds the rules for a person other than the account holder.
  • The guard. Recommended: yes, first. A list kept in step by memory is how link came to be missing from the importer's CV map.

Prior art

No dependency is proposed: this is a set of tables on the project's own registry, with section names and fields taken from Europass's schemas rather than invented (the rule of #266 and #653).

Verified, by reading in this session the Commission's Europass XML schema definition v4.0.1 (Candidate.xsd “Europass2 1.0.1, 02 June 2020”), its EUROPASS2 CV XML Schema Documentation v3.0.0 (DG EMPL, 2020), the older Europass XML schema documentation v3.4.0 (§4.7.6) and Cedefop's instructions of 2002-2017:

Name in the list Template heading, 2002-2017 Old XML Achievement code Candidate XML (2020) Read by the plugin
Presentations yes none in the list SpeakingHistory/SpeakingEvent (EventName, Description); or ConferencesAndSeminars no, named
Conferences yes conferences ConferencesAndSeminars/ConferenceAndSeminar (EventType: Title, Date, Location, Description, Link) no
Seminars yes seminars, workshops the same element no
Honours and awards yes honors_awards no element of its own; HonourAwardDate is a child of the activity type of NetworksAndMemberships and SocialAndPoliticalActivities; otherwise Others Others as a Project
Memberships yes memberships NetworksAndMemberships/NetworkAndMembership (activity: Title, Date, Description, Link, Location) and OrganizationAffiliations/OrganizationAffiliation (OrganizationName with validFrom and validTo attributes, Location, Link) no
References yes references EmploymentReferences/Referee (RefereeTypeCode, PersonName, Communication, YearsKnownNumber, Comment, Role, Link) no
Citations yes citations no element; Others Others as a Project
Courses yes courses no element of its own no
  • The schema documentation describes NetworksAndMemberships as “memberships/involvement in professional associations, societies, political parties, sports clubs, etc.” and OrganizationAffiliations as including “membership in a union”, which is why #693 treats memberships as possibly special-category data.
  • Not verified: which of these elements the Europass portal fills for each additional section a person adds (its FAQ says only that a person may add sections with a title of their choosing; the editor is a script-driven application that was not read), whether real downloads carry them (#318 is the open check), and the Europass wording of these headings in languages other than English.
  • Found on the way, outside this list: Certifications, for which Postulo has a section, is not in the reader's READ set either, so a certification in a file is named in the notice and not read. The legacy Achievement description is one prose field per heading, not a list of entries.

Builds on

Issues #175 (the career in sections), #131 (entries in other languages; free-text organisations), #181 (the candidate file), #203 (the order of entries), #244 and #54 (the Europass reader), #318 (the real-download check), #303, #308, #349. New issues of this set: #683 (organisation as a company, “from the career”), #671 (roles and the divisions that qualify), #685, #686, #687 (publications), #689, #666 (reference letters), #669 (a document that proves an entry).

Needed by

#693, #694, #695, #696 and #697.

Done when

  • A guard test in tests/test_career_sections.py (new test, importer maps readable) fails on a section missing from any list in step 1 and passes with the eight kinds there today and the one named exception (link in the CV-entry map, until #470); a CV holding one entry of each kind renders in HTML, plain text and Word (tests/test_document_formats.py, tests/test_documents.py).
  • The registry docstring points at the test; no second list of places is kept to drift.
  • The five are closed, each with its checklist items ticked in its own “Done when”, and the wiki's Your career record table lists the new sections.
  • This issue adds no record, so the archive format (29) and the candidate format (4) are untouched by it; each child bumps both by one from whatever they are when it lands (tests/test_candidate_file.py fingerprint, tests/test_export.py).
  • tests/test_wiki_surface.py is untouched unless a child adds a name to postulo.plugins.api; tests/test_page_coverage.py needs no entry (no new URL); tests/security/test_isolation_sweep.py and a per-section test cover each new model; strings are in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349).

From the list of improvements of 2026-10-02.

The maintainer's list, under Your career, ends with “Additional accolades:” and eight names: “Presentations, Conferences, Seminars, Honours and awards, Memberships, References, Citations, Courses”. Postulo has a place for none of them. This is the tracker for the five issues that add them. It decides once how a new section is shaped and holds the list of every place a section has to be added, so each of the five says only what is different about it. ## What exists today - **The career is eight kinds in one registry.** `SECTIONS` (`src/postulo/resume/registry.py:28-80`) gives each its slug, model, form and headings; `OVERVIEW_ORDER` (`:83-91`) is the seven the career page shows, the skills being shown inside their groups. Each is a `ResumeItem` (`src/postulo/resume/models.py:34-62`). None of the eight names exists; the nearest are `LinkKind.PUBLICATION` (`:280`), `Certification` (`:215-228`), `Project` (`:198-212`) and, for a person, `jobs.Contact` (`src/postulo/jobs/models.py:604-646`). - **One set of views serves every kind**, so a section brings no URL: create, edit, delete, move and “Other languages” take the slug from the address and answer 404 for an unknown one (`src/postulo/resume/views.py:45-50`, `resume/urls.py`). The isolation sweep therefore names one section, `experience` (`tests/security/test_isolation_sweep.py:405-409`); a new model has no isolation test unless one is written. - **The registry says a new kind is one entry. It is about a dozen places** (“adding a seventh kind later means adding one entry here”, `registry.py:1-6`). A few are held in step by a test (the candidate reader against `RESUME_FIELDS`, `tests/test_candidate_file.py:393-398`; the sidebar against `OVERVIEW_ORDER`, `tests/test_career_sections.py`; every create form, `tests/test_career_ordering.py:230-250`). Most are held by nothing. - **A kind a renderer has no branch for falls into an `else` written for education.** The plain-text and Word output reads `item.name` and `item.role` (`src/postulo/documents/rendering.py:303-319`), and its portfolio branch reads `item.qualification` and `item.institution` (`:374-383`), so a model without those fields raises `AttributeError`. The HTML themes print `entry.item.name`, `role` and the dates in the final `else` of `base_cv.html:127-162`, and the qualification and institution in that of `base_portfolio.html:131-152`. The wiki tells a plugin theme to extend these bases (*Writing a plugin*, “A theme, if you set documents”), so such a theme inherits whatever the bases do. - **Search lists five of the models and its links miss the sections.** `search_career` covers experience, education, projects, certifications and skills (`src/postulo/core/search.py:458-515`); its links end `#experience`, `#education`, `#projects` (`:510`), while the overview's anchors are `section-<slug>` (`src/postulo/templates/resume/overview.html:39`, `resume/views.py:77-78`), so a hit lands at the top of the page. Recorded as a fact; the five existing links are outside this list. - **No career API, no removal dialog.** `src/postulo/api/routers/` has no `resume` module. #303's dialog covers the rows of *Your details* (`src/postulo/accounts/removals.py`); a career entry is deleted on a confirmation page (`resume/views.py:304-313`), which a new section gets for free. - **A heading is a catalogue message.** `SectionSpec.label` and `.plural` (`registry.py:18-25`) head the career page; `SECTION_LABELS` (`documents/rendering.py:41-49`) heads a CV, drawn in the CV's language (`:259`). The person cannot retitle a section. #689's built-in names are about languages and carry no section names. - **What Europass does with the same names today.** The plugin reads `EmploymentHistory`, `EducationHistory`, languages, skills, `Projects` and `Others`, the last two as Projects (`src/postulo/plugins/europass/candidate.py:136-147`, `:603-619`); any other part of the profile holding text is named in the review page's notice “nowhere to go in Postulo yet” (`:622-644`, `tests/test_europass_candidate.py:550-577`). The older format's `Achievement`, of every type, becomes one Project named after its heading (`plugins/europass/reader.py:471-483`, JSON `:741-751`). - **The names are Europass's.** The 2002 to 2017 Europass CV template lists under *Additional information*: Publications, Presentations, Projects, Conferences, Seminars, Honours and awards, Memberships, References, Citations, Courses and Certifications (Cedefop, *Instructions for filling in the Europass CV*, © European Union 2002-2017, p. 5). The note's list is that one, less what Postulo has or tracks elsewhere (#687, projects, certifications). Checked against `main` at `f09b6010b`. ## Proposal 1. **A guard before the first section.** A test over `SECTIONS` (and over `OVERVIEW_ORDER` for the two lists a CV reads, `SECTION_LABELS` and the importer's CV-entry map, since a single skill is on a CV only inside its group) that fails, naming the section and the list, when a registered section is missing from `TRANSLATION_SECTIONS`, `RESUME_FIELDS`, `RESUME_MODELS`, `candidate.KINDS_BY_BLOCK`, `SECTION_LABELS` or the importer's two maps, unless it is on a short list of named exceptions each with its reason (one today: `link` is missing from the importer's CV-entry map, so a link on a CV is skipped on restore, which #470 fixes and so removes the exception; #696 adds one: a reference holds data about another person and the candidate file carries none). It also asks that each model has an explicit decision in `TRANSLATABLE` (an entry, or a named “translates nothing”) and that a CV holding one entry of every section renders to HTML, plain text and Word. The importer's maps are local variables (`src/postulo/core/importer.py:532`, `:905`); they become module-level, or are derived from `export.RESUME_MODELS`. The registry docstring is rewritten to point at the test. This is the one piece of work in this issue itself. 2. **The five issues**, each landing alone on `main` with its tests and wiki lines: - [ ] #693 Honours and awards, and memberships - [ ] #694 Presentations, conferences and seminars - [ ] #695 Courses - [ ] #696 References: the people who will vouch for you - [ ] #697 Citations (1.0.0) 3. **The standing checklist**, once. A child names only what differs. - *Model*: a `ResumeItem` with `ordering = ("order", "pk")`, a migration, an admin entry (`resume/admin.py`), demo data (`core/management/commands/seed_demo.py`). *Form*: `resume/forms.py`, help text in the project's voice (#205). *Registry*: `SECTIONS` and `OVERVIEW_ORDER`, which drive the career page, the sidebar anchors and counts, every view above and the CV picker (`documents/forms.py:386-432`). - *Page*: a row in `templates/resume/overview.html` where the entry's name and highlights are not what the row should say (it branches for links and skill groups, `:74-99`); the plain preview (`templates/resume/preview.html`, `ResumePreviewView`, `resume/views.py:334-353`, hard-coded per kind); the `counts` dictionary (`:98-106`), which no template under `templates/resume/` reads. - *Translations*: `resume/translatable.py:27-35` (absent means nothing is translatable, as for `Certification`, pinned by `tests/test_career_languages.py:83-86`) and `TRANSLATION_SECTIONS` (`core/export.py:153-162`). - *The CV*: `SECTION_LABELS`; `_section_blocks` and `_entry_blocks` (plain text and Word); a branch in `base_cv.html` and one in `base_portfolio.html`; and how a plugin theme that writes its own section loop would print an unknown kind. - *The archive*: `RESUME_FIELDS` and `RESUME_MODELS` (`core/export.py:343-415`), the importer's `section_models` and `date_fields` (`core/importer.py:532-541`) and its CV-entry map (`:905-912`), which is keyed by model name and has no `link` today (#470 completes it); `FORMAT_VERSION` (`:80`, 29 at `f09b6010b`) up by one from whatever it is when the commit lands, with its line in the history comment. - *The candidate file*: a `Kind` in `resume/candidate.py:176-276` (`names` and `dates` say what makes two entries the same one; `label`, `sub` and `ongoing` what the review page prints), `NOT_READ`, `CANDIDATE_FORMAT` (`core/export.py:104`, 4) up by one with its line in `SHAPES` (`tests/test_candidate_file.py:372-387`), the counts on the file page (`resume/views_candidate.py:35-49`). - *Search*: a group in `search_career` if the section should be found, linking to the real `#section-<slug>`. - *Europass*: the reading, in the same commit, for the element in *Prior art*; `Record` (`resume/importing.py:26-67`, on `postulo.plugins.api`) gains a list with a default, and `is_empty`, `counts`, `apply` (`:115`) and the review page's `COUNT_LABELS`, `_summarise`, `_to_session`, `_from_session` (`resume/views.py:503-661`) follow. - *API*: none exists and none is added (one line in the commit says so). *Everything else*: CV print choice (#308) does not apply, as an entry is on a CV only when put there; an owner-scoping test for the model; account deletion; the wiki's *Your career record* (and *CVs*, and *Writing a plugin* if `Record` changed); the CHANGELOG line; strings in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349). 4. **Section names** are English messages translated through the catalogues, with a `pgettext` context where the English word is ambiguous (below). ## Decisions These are recommendations the maintainer has not confirmed. - **One model per section, or one table with a kind.** Options: (a) a model per section with typed fields; (b) one table with a `kind` and general columns (title, organisation, role, date, place, link, text), which is how Europass held them until 2020, one `Achievement` per heading with a code and a text; (c) a table per shape (an event, a body, a person). **Recommended: (a).** Europass's current format types them (`EventType`, `Referee`, `OrganizationAffiliation`), so typed fields map to it one to one and a kind table would have to be taken apart again to be written. The sections differ in what is required and what is personal data (hours on a course, a person's agreement on a referee, a role at an event); a kind table makes every column nullable and every rule an `if kind ==` in a form, a candidate reader and a CV renderer that are all written per model today, and the candidate reader and archive already name their blocks and identity fields per section (`Kind.names`, `RESUME_FIELDS`). The cost is six models and the checklist six times, which the guard is for. - **How many sections eight names make.** **Recommended: six**: Honours and awards, Memberships, one section for Presentations, conferences and seminars (#694), Courses, References, Citations. Europass's current format folds conferences and seminars into one element and keeps the person's own talks in another; one section with a type and a role covers the three names. Five issues, because honours and memberships are two small sections that share a decision about organisations. - **Where they sit and how long the page gets.** The career page goes from seven headings to thirteen (fourteen with #687). Options: (a) flat, in `OVERVIEW_ORDER` after Languages; (b) grouped under one “Additional information” heading, as Europass does; (c) empty ones hidden behind an “Add a section” menu. **Recommended: (a)**, in the note's order with References moved to the end, where a CV puts them: events, honours and awards, memberships, courses, citations, references. The sidebar (#175) exists for this and its rule that an empty section is listed stays; an empty section costs one heading and one line. `OVERVIEW_ORDER` also orders the CV picker's groups; what a CV prints follows its own entries (`documents/rendering.py:117-148`). Revisit (b) or (c) if the page proves heavy. - **Organisations: text or company.** Memberships, honours and courses each name a body, and the career's organisation, institution and issuer are free text on purpose (#131, `resume/models.py:65-74`) that #683, #685 and #686 reopen. An association is not an employer. **Recommended:** each of the three starts as text, as `Certification.issuer` is today, so none waits on g1. Then it takes g1's mechanism unchanged, in a later commit of its own issue: the text stays the CV's wording and an optional link to one of the owner's companies is added, a company the career form creates being marked “from the career”, which leaves it out of the posting, contact and agency pickers, the table and the map until a posting, contact or application is attached. That marker is how a club or a prize body is kept from becoming somewhere to apply. #671's role registry offers them first, never required: division 94, “Activities of membership organisations” (section T in `src/postulo/jobs/data/nace-2.1.json`), for memberships; the “awarding body” role of #686 for honours; the “place of learning” role of #685 for courses. The note asks for companies only for experience, learning and certification, so this extension is the drafters' and the maintainer may decline it. - **What the person can title.** Options: (a) catalogue messages per language; (b) the Europass wording in each language, copied; (c) let the person name each section, as the Europass portal does for sections it has no field for (its FAQ: new sections can be added “with a title you choose”). **Recommended: (a)**, with the Europass English headings as the source wording. Where a word is ambiguous a `pgettext` context fixes it: `msgid "Reference"` already means a reference *letter* (`documents/kinds.py:65`; “Référence” in the fr-FR draft) and `msgid "Role"` a job (“Poste”). (b) was not checked language by language and the portal's wording may change; (c) is a feature of its own. - **Europass: when.** **Recommended: each child reads its own section from the Candidate XML in the commit that adds it**, since a section nobody can import into is half done; the older `Achievement` paragraphs keep arriving as one Project each, because splitting prose into entries is a guess and an import saves nothing on a guess. No Europass writer is proposed (Postulo writes none, `plugins/europass/__init__.py` is an importer), but the fields are chosen so one could be written. - **What a CV prints.** **Recommended:** exactly what the person puts on it. The picker is opt-in per entry, so nothing from these sections prints by default. #696 adds the rules for a person other than the account holder. - **The guard.** **Recommended: yes, first.** A list kept in step by memory is how `link` came to be missing from the importer's CV map. ## Prior art No dependency is proposed: this is a set of tables on the project's own registry, with section names and fields taken from Europass's schemas rather than invented (the rule of #266 and #653). **Verified**, by reading in this session the Commission's *Europass XML schema definition v4.0.1* (`Candidate.xsd` “Europass2 1.0.1, 02 June 2020”), its *EUROPASS2 CV XML Schema Documentation* v3.0.0 (DG EMPL, 2020), the older *Europass XML schema documentation* v3.4.0 (§4.7.6) and Cedefop's instructions of 2002-2017: | Name in the list | Template heading, 2002-2017 | Old XML `Achievement` code | Candidate XML (2020) | Read by the plugin | | --- | --- | --- | --- | --- | | Presentations | yes | none in the list | `SpeakingHistory/SpeakingEvent` (`EventName`, `Description`); or `ConferencesAndSeminars` | no, named | | Conferences | yes | `conferences` | `ConferencesAndSeminars/ConferenceAndSeminar` (`EventType`: `Title`, `Date`, `Location`, `Description`, `Link`) | no | | Seminars | yes | `seminars`, `workshops` | the same element | no | | Honours and awards | yes | `honors_awards` | no element of its own; `HonourAwardDate` is a child of the activity type of `NetworksAndMemberships` and `SocialAndPoliticalActivities`; otherwise `Others` | `Others` as a Project | | Memberships | yes | `memberships` | `NetworksAndMemberships/NetworkAndMembership` (activity: `Title`, `Date`, `Description`, `Link`, `Location`) and `OrganizationAffiliations/OrganizationAffiliation` (`OrganizationName` with `validFrom` and `validTo` attributes, `Location`, `Link`) | no | | References | yes | `references` | `EmploymentReferences/Referee` (`RefereeTypeCode`, `PersonName`, `Communication`, `YearsKnownNumber`, `Comment`, `Role`, `Link`) | no | | Citations | yes | `citations` | no element; `Others` | `Others` as a Project | | Courses | yes | `courses` | no element of its own | no | - The schema documentation describes `NetworksAndMemberships` as “memberships/involvement in professional associations, societies, political parties, sports clubs, etc.” and `OrganizationAffiliations` as including “membership in a union”, which is why #693 treats memberships as possibly special-category data. - **Not verified:** which of these elements the Europass portal fills for each additional section a person adds (its FAQ says only that a person may add sections with a title of their choosing; the editor is a script-driven application that was not read), whether real downloads carry them (#318 is the open check), and the Europass wording of these headings in languages other than English. - Found on the way, outside this list: `Certifications`, for which Postulo has a section, is not in the reader's `READ` set either, so a certification in a file is named in the notice and not read. The legacy `Achievement` description is one prose field per heading, not a list of entries. ## Builds on Issues #175 (the career in sections), #131 (entries in other languages; free-text organisations), #181 (the candidate file), #203 (the order of entries), #244 and #54 (the Europass reader), #318 (the real-download check), #303, #308, #349. New issues of this set: #683 (organisation as a company, “from the career”), #671 (roles and the divisions that qualify), #685, #686, #687 (publications), #689, #666 (reference letters), #669 (a document that proves an entry). ## Needed by #693, #694, #695, #696 and #697. ## Done when - A guard test in `tests/test_career_sections.py` (new test, importer maps readable) fails on a section missing from any list in step 1 and passes with the eight kinds there today and the one named exception (`link` in the CV-entry map, until #470); a CV holding one entry of each kind renders in HTML, plain text and Word (`tests/test_document_formats.py`, `tests/test_documents.py`). - The registry docstring points at the test; no second list of places is kept to drift. - The five are closed, each with its checklist items ticked in its own “Done when”, and the wiki's *Your career record* table lists the new sections. - This issue adds no record, so the archive format (29) and the candidate format (4) are untouched by it; each child bumps both by one from whatever they are when it lands (`tests/test_candidate_file.py` fingerprint, `tests/test_export.py`). - `tests/test_wiki_surface.py` is untouched unless a child adds a name to `postulo.plugins.api`; `tests/test_page_coverage.py` needs no entry (no new URL); `tests/security/test_isolation_sweep.py` and a per-section test cover each new model; strings are in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349). From the list of improvements of 2026-10-02.
tiagoagueda added this to the 0.6.0 milestone 2026-10-03 01:59:04 +00:00
Author
Owner

Landed on main with a green Forgejo CI run. Its entry in the CHANGELOG's Unreleased section ends in (#692).

Landed on main with a green Forgejo CI run. Its entry in the CHANGELOG's Unreleased section ends in (#692).
Sign in to join this conversation.
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#692
No description provided.