An education entry states its EQF level, as a number from 1 to 8 #684
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#684
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The maintainer's list says, under Your career: “Learning can have EQF level or equivalent.” Postulo has nowhere to put a European Qualifications Framework level: the one place it survives is the text “EQF 6”, written into the grade by the Europass import, and only when the file gives no grade.
What exists today
Educationholds the institution, the qualification, the field of study, a place, two dates, a free-textgrade(100 characters) and highlights (src/postulo/resume/models.py:101-121).grepfinds EQF only in the Europass reader.EducationLevelCodewhere the list is the EQF one, with^(?:EQF[\s_-]*)?([1-8])$, and returns"EQF 6", but only when the file gives no final grade (the docstring: the grade “where the file gives one, and otherwise the EQF level”): a final grade wins and the level is dropped (src/postulo/plugins/europass/candidate.py:154,:500-521;tests/test_europass_candidate.py:339-347,:349-365). A bare number on the ISCED list is not taken as EQF (:367-377). The older XML and JSON readers copy the format'sLevellabel into the grade as it is, whatever it says (plugins/europass/reader.py:429,:704;tests/data/europass.xml:48hasEQF 7). The Candidate schema offers two scales for this code, ISCED-2011 or EQF (tests/data/europass-candidate-sample.xml:115-119).base_cv.html:136-138prints the institution and the place and nothing else of an education entry;base_portfolio.html:139-143the same; the text and the Word file are held to what the page prints (documents/rendering.py:303-319,tests/test_document_formats.py:195-205, wiki CVs: “Something the themes do not print — a grade, a field of study — is not in the text either”). Yet the grade's help says “As the institution words it. It prints only if you fill it in” (resume/forms.py:122). A level that “shows on the CV” therefore needs the themes to learn a new line, not only a new column.RESUME_FIELDS["education"](core/export.py:355-366) is written into the archive and into the candidate file by the same code, andtests/test_candidate_file.py:354-387fingerprints it, so a new field bumps both formats (FORMAT_VERSION29 andCANDIDATE_FORMAT4 atf09b6010b). A candidate file is read throughEducationForm(resume/candidate.py:198-217); the archive importer is not, and hands every key tomodel.objects.create(core/importer.py:556), so a value outside 1 to 8 would be stored as written.gradeis translatable per CV language (resume/translatable.py:29); a level is a number and is not.documents/printing.py(#308): a yes-or-no per detail inSWITCHES(:156), each a column onCV(documents/models.py:316-326), looped over by the CV form (documents/forms.py:294), the API (api/routers/documents.py:78,:191) and the archive importer (core/importer.py:955). The themes are handed thecvitself (documents/rendering.py:259-271).src/postulo/api/routers/).Checked against
mainatf09b6010b.Proposal
Two commits, in this order.
Education.eqf_level, a nullable small integer with the choices 1 to 8 and “Not stated” first, in the form beside the qualification (“EQF level”, with a sentence: the level of the qualification in the European Qualifications Framework, which a diploma or its supplement may state; Postulo never works it out from the qualification's name, as it does not for a language level, #235). Never translated. Shown on the career preview (templates/resume/preview.html:51). The archive and the candidate file carry it inRESUME_FIELDS["education"], both formats up by one; the candidate reader takes it through the form, so a nine or a word is a refused row on the review page; the archive importer keeps an integer from 1 to 8 and drops anything else with a line in its report.eqf_leveland leaves the grade alone, so a file with a grade and a level keeps both; the older readers do the same for aLevelthat is exactly “EQF n”, and keep any other label in the grade as before. Nothing is read from an ISCED code.printing.SWITCHESwith its column, form, API and importer entries (the API'sCVPrintsOutandCVPrintsInname each switch,api/schemas.py:739-753and:784-795, so both gain a line), and written in the archive'sprintsblock of the CV (printing.as_archived), which is a second archive bump beside the one for the field. The themes (base_cv.html,base_portfolio.html), the preview, the plain text and the Word file print one extra element on an education entry, “EQF level 7”, beside the institution and the place, in the document's language, only where the switch is on and the level is set. Plugin themes readentry.item.eqf_levelandcv.<switch>on their own and need no new name onpostulo.plugins.api.Decisions
Prior art
No library: eight integers. Standards: the EQF, Council Recommendation of 22 May 2017 on the European Qualifications Framework for lifelong learning (2017/C 189/03, OJ C 189, 15.6.2017; repeals the 2008 Recommendation, which is the
listVersionID="2008/C11/01"the Candidate sample still names); the Europass Candidate code lists (ISCED-2011 and EQF). The text of the 2017 Recommendation could not be fetched for this draft (EUR-Lex refused the automated request); its title and reference come from its EUR-Lex and Publications Office listings.Builds on
Issues #235 (a level nobody stated prints nothing), #181 (the candidate file), #308 (what a CV prints), #244 and #318 (the Europass reader and its check against real files).
Needed by
#669 (a diploma kept as a document may show the level of the entry it proves) and #670 (a credential's EQF level lands in this field).
Done when
tests/test_career_sections.pyor atests/test_eqf_level.py: the form saves and clears a level, refuses 0 and 9, never translates it (tests/test_career_languages.pyfields list), the preview shows it.tests/test_europass_candidate.py: a file with a final grade and an EQF code keeps both; an ISCED code sets nothing; the older readers move exactly “EQF n” and leave other labels in the grade (tests/test_europass.py).tests/test_cv_prints.pyandtests/security/test_cv_prints.pyfor the switch (default off, chosen, in the archive and in the API's CV answer and patch,tests/test_api.py);tests/test_documents.pyandtests/test_document_formats.py: with the switch off the page, the text and the Word file are what they were, with it on all three print the same line, andtest_every_line_of_the_text_is_on_the_pagestill holds.tests/test_export.pyand the archive round trip carry the field at the new format;tests/test_candidate_file.pyholds the newSHAPESline and a refused row for a level of 9 (tests/security/test_candidate_file.pywith a hostile value);tests/test_importers.pydrops an out-of-range value with a report line. A migration test for “EQF 6” moved and “6.0 GPA” left.core/gdpr.py). The standing checklist otherwise applies: archive format one more than whatever it is when each commit lands (29 atf09b6010b; the field and the switch are two bumps), candidate format likewise for the field (4; the switch is not in the candidate file), strings in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349). The abbreviation of the framework's name in each language is for the translators to choose and was not verified here.From the list of improvements of 2026-10-02.
Landed on main with a green Forgejo CI run. Its entry in the CHANGELOG's Unreleased section ends in (#684).