Your career: courses, with a provider, a length and the certificate that proves them #695
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#695
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, under Your career, names “Courses”. A short course, a bootcamp, an evening class or an online programme is neither a degree nor a credential somebody can verify, so today it goes into Education with a made-up qualification or is left off. This issue adds a section for it, with a provider, a length and a way to point at the certificate a person holds, following the shape and the checklist settled in #692.
What exists today
src/postulo/resume/registry.py:28-80. The two that sit either side of a course are:Education: a qualification, an institution, a field of study, a place, dates, a grade and highlights (src/postulo/resume/models.py:101-121), shown as a degree-shaped entry on a CV (src/postulo/templates/documents/themes/base_cv.html:135-137). #684 gives it an EQF level.Certification: a name, an issuer, an issue date, an optional expiry and a credential link (:215-228), printed as “name, issuer” and the year (base_cv.html:117-125). #686 reopens its issuer.DocumentKind.CERTIFICATEis an upload-only kind (src/postulo/documents/kinds.py:61), and an uploaded document is tied to nothing in the career (src/postulo/documents/models.py:564-640). #669 proposesUploadedDocument.proves, a generic link to one of the owner's entries, limited to the models that can be proven (“EducationandCertificationnow, a section later by adding one line”).Education.institution,Certification.issuer; #131,src/postulo/resume/translatable.py:16-26), and #685 and #686 are reopening that for the two of them.EducationHistory/EducationOrganizationAttendanceis what the old template called Education and training, “add separate entries for each course” (Cedefop, Instructions for filling in the Europass CV, © European Union 2002-2017), and the plugin already reads each attendance as an Education entry (src/postulo/plugins/europass/candidate.py:473-497). The format before 2020 had anAchievementcodecoursesholding one prose paragraph; the older instructions say to attach “any certificates issued at the end of training courses which did not lead to a formal qualification” (Cedefop, Instructions for using the Europass curriculum vitae, © European Union 2002-2010 as printed). That paragraph arrives today as a Project named after its heading (plugins/europass/reader.py:471-483).documents/rendering.py:363-371); an education or experience entry prints the month and the year.Checked against
mainatf09b6010b.Proposal
Course(“Course”; heading “Courses”). AResumeItemwith:title(required, 200);provider(optional text, 200: a school, a company, a platform, an employer's own academy);start_dateandend_date(optional);hours(optional whole number from 1 to 10,000: the hours of teaching or study as the provider states them);summary(optional, a line or two: what it covered);url(optional: a public page for the course or its certificate). Slugcourse, placed inOVERVIEW_ORDERwhere #692 puts it. A new entry is placed byend_date, as an education entry is (ordering.DATE_FIELDS).Coursejoins the models a document may prove (one line there), the form on the document side offers the owner's courses, and a course's row on the career page says how many documents prove it and links to them. A diploma-like file is not what is meant here: an attendance or completion certificate is an upload of kind Certificate. Until d4 lands,urlis the only pointer.summaryonly. A course's title and its provider are the provider's wording, by the rule that keeps a credential's name as it is.courses, inRESUME_FIELDS,RESUME_MODELSandTRANSLATION_SECTIONS; the importer's maps; aKindwhosenamesare the title and the provider and whosedatesare the two dates. Both formats go up by one from whatever they are when the commit lands.hoursis read through the form's own bounds, so a file cannot store a value the page would refuse.EducationOrganizationAttendanceas education; deciding which of those are really courses would be a guess, and an import saves nothing on one. A course the person typed into a EuropassOtherssection stays a Project, as today. The record is shaped so an exporter could write one as anEducationOrganizationAttendancewith no degree.#section-course. Demo data: a course with hours and one without. Wiki: Your career record gains the section and the rule in step 2.Course.company, nullable,SET_NULL, besideprovider, as g1 does it: a company the form creates is marked from the career and given no industry (as #686's issuers are; only #685's education form assigns one, and a software company's academy is not in education), a file never creates a company, a company merge moves the links to the kept company and the company's delete dialog counts them (jobs/merging.py,jobs/views.py:574-610); both formats go up by one again.Decisions
These are recommendations the maintainer has not confirmed.
src/postulo/jobs/data/nace-2.1.json) orders the list and restricts nothing: a software company's academy sits in no education division.urlis only an address, not a promise the address stays up; the career page never fetches it, as it never fetches a link without being asked (the Check button,resume/links.py).Prior art
No dependency. The standard is Europass: no element for courses in the 2020 Candidate format, the
coursesAchievementcode and the template headings before it, as read for #692. Whether the current portal offers a Courses section by that name, and what it writes into the file for one, is not verified (its FAQ says only that sections can be added with a title the person chooses).Builds on
Issues #131 (entries in other languages; free-text bodies), #181 (the candidate file), #203 (the order of entries), #244 and #318 (the Europass reader and its real-download check). New issues of this set: #692 (shape and checklist), #694 (events), #669 (a document that proves an entry), #685 and #686 (institution and issuer as companies), #683, #671, #684 (EQF on education).
Needed by
Nothing waits on it. #669 gains
Coursein its list of provable models when both have landed.Done when
tests/test_courses.py: create, edit, delete and move through the registry;hoursaccepts 1 to 10,000 and refuses 0, a negative, text and a number beyond the bound; the CV prints “title, provider”, the hours in a plural-aware message and the year in the document's language in the HTML themes, the plain text and the Word file, a portfolio prints it without an empty heading, and an entry with only a title prints just that; a new entry lands by its end date; the form's help carries the rule against education and certification.tests/test_career_sections.py(the #692 guard) passes with the section;tests/test_career_languages.pygains “a course translates its summary, not its title or provider”.tests/test_merging.pymoves the links of courses,tests/test_career_companies.pygains the course cases, and a file never creates a company.tests/test_documents.py).core/export.pyFORMAT_VERSION, 29 atf09b6010b; one more than whatever it is when the commit lands) with a round trip intests/test_export.py, a CV holding one and an older archive that still loads; the candidate format (4) goes up by one with its line inSHAPES(tests/test_candidate_file.py:372-387), a review row for a good entry and a refused one (an hours value out of bounds), andtests/security/test_candidate_file.pyfor hostile values;tests/test_europass_candidate.pyasserts a course is not invented from an education entry; the API is unchanged (there is none for the career); search finds one (tests/test_search.py); CV print choice (#308) and the processing record (core/gdpr.py) do not apply;tests/test_wiki_surface.pyis untouched;tests/test_page_coverage.pyneeds no entry;tests/security/test_isolation_sweep.pyand a per-section test show another account's entry is a 404; account deletion removes them (tests/test_account_deletion.py); strings, including the plural message, are in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349).(#N)) are in the same commit.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 (#695).