Your details: date and place of birth, optional, and printed on a CV only when a CV says so #679
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#679
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?
Your details has no date of birth and no place of birth. The maintainer's list asks for both ("Add place and date of birth", under Your details). They are the most identifying things a person can add to a profile, so they are optional, never worked out from anything, and kept off every document until a CV is set to print them, as the form of address and the pronouns were (#309, #308).
What exists today
Profileholds the name's companionsform_of_addressandpronouns, a headline and a free-textlocation(src/postulo/accounts/models.py:265-295);grepfinds no birth, nationality or gender field anywhere insrc.Profile; the form on Your details, in the Your name card (templates/accounts/profile.html:87);PROFILE_FIELDSin the archive (core/export.py:111) and, if it is what a CV may print,CANDIDATE_PROFILE_FIELDS(:143);ProfileOutandProfilePatchin the API (api/schemas.py:891,:940,profile_out:961); ashow_*switch per detail onCV(documents/models.py:316-326), listed inprinting.SWITCHES(documents/printing.py:156) which the form, the API and the importer all loop over (documents/forms.py:294,api/routers/documents.py:78,191,core/importer.py:955); and a key on thecontactdict that every theme, the preview, the plain text and the Word file are handed (documents/rendering.py:218-229). Off by default: "neither is printed anywhere unless a CV says so".for name, value in profile_data.items(): if hasattr(profile, name) and value: setattr(profile, name, value)(core/importer.py:478-482). A new field with a format would be stored as the file wrote it unless its own reader is put in front of it.resume/candidate.py:750-790,_details), so a validator on the column is the one place a file's value is held to the page's rule.FORMAT_VERSION(core/export.py:80, 29 atf09b6010b) andCANDIDATE_FORMAT(:104, 4). The candidate fingerprint is held bytests/test_candidate_file.py:354-387.PostalAddress.municipalityand.country(a two-letter code,core/models.pyPostalAddress), the country list iscore/phones.pyCOUNTRIES(241 rows, English names), used by the address form (core/postal.py:355);jobs/places.pymatches a company's free text against GeoNames' cities of more than a thousand inhabitants, offline, as a guess, for the map (#108).docs/THREAT-MODEL.mdnames a home address as "the sharpest thing this application holds" and gives the rules that follow from it. A date and a place of birth are not on that list, and are exactly what identity checks ask for.core/gdpr.pyrecord_of_processing()is drawn from the installed plugins and the connections, so a field of the account holder's own profile needs no entry there. Whatcore/gdpr.pydoes carry per data kind is the document and the erasure of a contact (contact_document,erase_contact,retention_dry_run): it does not apply to the account holder's own profile, which the archive and account deletion (accounts/deletion.py) answer.BDAY, which takes a date with reduced accuracy (19960415,--0415) or text, and, from RFC 6474,BIRTHPLACE, a text or a URI (RFC 6350 section 6.2.5, RFC 6474). The Europass Candidate XML the importer is tested with carries the date of birth ashr:BirthDatedirectly underPerson, in the same reduced forms (YYYY-MM-DD,YYYY-MMorYYYY, rule BR-COM-06 in the sample's own comments;tests/data/europass-candidate-sample.xml:95-97) and has no place of birth;plugins/europass/candidate.py_read_person(:345-398) reads no birth date.Checked against
mainatf09b6010b.Proposal
Profile, all optional: a date of birth that may be a year, a year and month, or a whole date; a place of birth as the text typed (a town or city) and a country as a two-letter code, from the list the address form already offers. Blank means nothing, and nothing is worked out from anything (an age is never computed, stored or shown).1990,1990-03,1990-03-12), not in the future, not before 1900. The form is three boxes in afieldsetwith a legend (day, month, year, each a text input withinputmode="numeric"and thebday-day,bday-monthandbday-yearautocomplete tokens, SC 1.3.5), in the order the interface language writes dates; a year alone is allowed.section-personal,accounts/views.py_section_nav); #680 and #681 put their fields in the same card. A sentence says what the card is for and that none of it is printed until a CV is set to print it.printing.SWITCHES: Print your date of birth and Print your place of birth, with the CV form, the API and the importer picking them up from that one table;printing.is_defaultandPrintedcarry them;documents/rendering.contact_detailsaddsbirth_dateandbirth_placekeys, which a theme prints or ignores, and Postulo's own base themes print one “Born …” line when they are set, in the document's language and date style. The plain text and the Word file print the same line.PROFILE_FIELDS(one more than the format it is on when the commit lands, with its line in the history comment ofcore/export.py); the candidate file carries them too (CANDIDATE_PROFILE_FIELDS, format one more than now), read back "filled where blank, kept where not", each through the column's validator, so a malformed date in a file is a refused row on the review page and nothing stored. The API'sProfileOutandProfilePatchgain them, held to the same rule (a 422 naming the field), andCVPrintsOut/CVPrintsIngain the two booleans.setattris wider than this issue; this closes it for these fields and names the rest in the report of the commit.)docs/THREAT-MODEL.mdnames a date and place of birth beside the home address in What is most worth defending, with the same rules: owner-scoped like every row, never in a list, a log, a search result, a webhook or an error message, and printed only where a CV says so. The wiki's Your career record (the candidate file's contents), CVs (the new switches) and The capture API (the new fields) are updated in the same commit.Decisions
SelectDateWidgetneeds all three parts and offers only the next ten years unless given a range, so it is not the form; the GOV.UK Design System's three-box date input is the pattern followed.jobs/places.pyplaces a company at city level as a guess and refuses the street on purpose (#108, the threat model). A place of birth is a fact someone states, often a village GeoNames' cities-above-1,000 table lacks, and must never be pinned on a map. Recommended: the text typed and a country code, no lookup, no coordinates, never offered to the map (#702).core/phones.py's, in English, as the address form does it. Recommended: reuse it. A printed country in the document's language needs names Postulo does not have (#309 left "München, Germany" on a French CV for the same reason); #689 is where a general source lands.GET /profileis under thereadscope andPATCHunderwrite, as the name is; a browser extension's token holdscapturesand sees neither. Recommended: the same scopes, and nothing about these fields in any list endpoint, webhook or log.Prior art
BDAYand RFC 6474'sBIRTHPLACEfor the day #660 writes the person's own card (a stored value maps one to one; a year-only date isBDAY:1990).edtf(Library of Congress EDTF, MIT, 5.0.2 released 2026-09-17, no advisories on OSV, not a dependency): parses uncertain, approximate and interval dates. Rejected: what is needed is three shapes and a range check, about twenty lines; a dependency for the part of EDTF nobody asked for is more to keep current than the code it replaces. The maintainer may overrule: it is MIT, so compatible with AGPL-3.0.django-partial-date1.3.2 (MIT, released 2022-01-20, no advisory on OSV, depends onsix, not a dependency): a Django field for a date that may be only a year or a month. Rejected: not released since 2022, and it adds a field type where one validated text column is enough (decision (c)).applications/ical.pyanddocuments/docx.pyare written by hand on purpose; this is smaller than either.Builds on
#309 (form of address and pronouns: optional, never inferred), #308 (a CV chooses what it prints), #181 (the candidate file), #306 (postal addresses and countries), #337 (the language of a date's wording).
Needed by
#680 (nationalities) and #681 (gender) add to the same card, the same switches and the same format bumps. #660 (contacts as vCard 4) writes the two on the person's own card, as
BDAYandBIRTHPLACE; it exports that card and never reads it back.Done when
tests/test_birth_details.py: the validator (a year, a year and month, a date, refused: an impossible day, a future date, 1899, text, a NUL); the form saves and clears each; the sidebar lists the card; nothing is printed on a CV by default and the two switches print one “Born …” line in the document's language, in the preview, the plain text and the Word file.tests/test_cv_prints.pyandtests/security/test_cv_prints.pycover the two switches (default, chosen, the archive carrying them);tests/test_export.pyand the archive round trip carry the fields at the new format;tests/test_candidate_file.pyholds the new candidate fingerprint (SHAPES) and a review row for a good value and a refused one;tests/security/test_candidate_file.pya file with a malformed date; the archive importer drops one (tests/test_importers.py).tests/test_api.py:GET /profileandPATCH /profilecarry the fields with the page's bounds;tests/test_profile_sections.pyexpects the new anchor. Search is unaffected: nothing here is indexed (core/search.pyhas no profile group), and a test says so for the new fields.tests/security/test_isolation_sweep.pyand the account-deletion test (tests/test_account_deletion.py) show the fields gone with the account;tests/test_gdpr.pyneeds no change (the record of processing is drawn from plugins), which a one-line comment in the commit says.tests/test_wiki_surface.pyis unaffected unless a new name reachespostulo.plugins.api.docs/THREAT-MODEL.mdnamed above are changed in the same commit; the CHANGELOG line ends(#N).From the list of improvements of 2026-10-02.