Diploma, diploma supplement and certificate as kinds of document, each tied to the entry it proves #669

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

From the list of 2026-10-02, under Documents: "Add diploma, supplement diploma, and certificates, including EDC." A certificate can be uploaded today. A diploma and a diploma supplement cannot be told apart from "Other", and none of them is tied to the entry of the career record it is the proof of. The EDC (a European Digital Credential, assumed here to be the Europass one) is #670.

What exists today

  • One of the three exists. DocumentKind.CERTIFICATE ("Certificate") is a kind Postulo holds rather than writes: "somebody else issues these, and Postulo keeps the file" (src/postulo/documents/kinds.py:61, :179). There is no diploma and no diploma supplement. The registry takes a new kind without a migration, because the pickers read it through a callable (kinds.py:91-105, :131-133); tests/test_document_kinds.py:33-47 fails if a stored kind is not described and the other way round.
  • Each kind is another switch on every store connection. kind_specs builds one per registered kind, on unless unset (documents/stores.py:112-125).
  • An upload is not tied to anything in the career. An UploadedDocument has a title, a kind, a file, notes, a language, a checksum and a version chain (documents/models.py:564-640). Education (qualification, institution, place, dates, grade; resume/models.py:101-117) and Certification (name, issuer, issued on, expires on, credential link; :211-222) have no document. The career record travels without documents (the candidate file), and the wiki says what it leaves out.
  • There is already a way to point at a career entry without a column per kind. CVItem uses a generic relation "because … six nullable foreign keys … would need widening every time a new kind of item is added" (documents/models.py:378-413), and the importer resolves it by section and local id (core/importer.py:966-977). The accolades in #693 to #697 will add more sections.
  • What the Files list offers. No filter by kind (D2, #667), and an extension list with no json or xml (documents/models.py:585-589).
  • What a diploma supplement is. A document a higher-education institution issues with a diploma in the Bologna countries: eight sections (the holder, the institution, the level, the content, the function, the certification, the national system and further information), in a model the European Commission, the Council of Europe and UNESCO agreed. "It does, however, not represent a Curriculum Vitae or a substitute for the original qualification" (https://education.ec.europa.eu/education-levels/higher-education/inclusion-and-connectivity/diploma-supplement). So it is a second document beside the diploma, not a version of it.

Checked against main at f09b6010b.

Proposal

  1. Two kinds. DocumentKind.DIPLOMA ("Diploma") and DocumentKind.DIPLOMA_SUPPLEMENT ("Diploma supplement"), registered with the others as held, not authored, with labels in the catalogues. Certificate stays as it is. The "Reference" kind of #666 is unrelated.
  2. A link from a document to the entry it proves. UploadedDocument.proves: a generic link (content_type, object_id, optional) to one of the owner's career entries, as CVItem does, limited to the models that can be proven (Education and Certification now, a section later by adding one line). Written only through a form field that offers the owner's own entries, labelled by what they say ("BSc Computer Science, University of Aveiro"), and checked again on save that the entry has the same owner. A diploma and its supplement point at the same entry; an entry may have several documents; a document proves at most one entry.
  3. Cleared, not cascaded. Deleting the entry clears proves on its documents (a receiver, as forget_the_source_of_a_render clears a render's source, documents/signals.py:72-89); a GenericRelation on the entry would delete the diploma with it, which is the opposite of what is wanted.
  4. Seen from both ends. The upload form shows "It proves" when the kind is certificate, diploma or diploma supplement. The Files list shows the entry beside the kind, and the Files list's kind filter (#667) offers the three. An education or certification row on the career page says how many documents prove it and links to them. None of this prints on a CV, and the candidate file does not carry the link.
  5. Wiki. Files and what you sent names the three kinds and the link; Your career record says an entry can have its proofs.

Decisions

  • Which kinds. The note names three. Recommended: exactly those, plus whatever a plugin registers. A transcript of records and a micro-credential are natural next kinds and need nothing but a registration.
  • Naming. The note says "supplement diploma"; the official name is Diploma Supplement. Recommended: "Diploma supplement".
  • The link: generic or two foreign keys. Recommended: generic, on the document side, as CVItem does. Two nullable keys would be enough today and would have to be widened for each accolade section; a link on the entry's side (a many-to-many) would put a private document into the career block, which #684 to #686 and the candidate file both read.
  • What deleting a document does to the entry. Nothing: the entry is the person's claim and does not depend on having a proof.
  • Stores. The two new kinds default to on, as every kind does. A diploma is what somebody most wants archived. (Contrast the reference letter, #666, which is about another person.)
  • Required or optional. The link is optional: a scan of a diploma uploaded before the entry exists is common.
  • EDC. A European Digital Credential is a file format of a certificate or a diploma, with a seal to check, and is handled in #670. It will be uploaded as one of these kinds.
  • Conflict with an earlier decision. None found. #131 left the career's institution and issuer free text; the link does not change that.

Builds on

#28 and #133 (letter and document kinds, closed), #131 (career entries, closed), #663 (the integrity of an uploaded document matters most for these).

Needed by

#670 (an EDC is a certificate or a diploma with a seal), #684 (an entry with an EQF level may show its proof).

Done when

  • tests/test_document_kinds.py: the two kinds are described and storable (the existing tests at :33-47 cover it once they are registered), a store connection has a switch for each, and a plugin can still register another.
  • tests/test_documents.py (new tests): a diploma and a supplement can be uploaded and both point at one Education entry; the form offers only the owner's own entries; another person's entry is refused by the form and by the save; deleting the entry clears the link and keeps the files; deleting the document leaves the entry.
  • tests/test_career_sections.py: the career page shows the number of documents on an entry and links to them.
  • tests/test_export.py: the archive carries proves as a section and a local id and restores it; an older archive loads with no links.
  • New fields on a record, so also: the archive format goes up by one (core/export.py FORMAT_VERSION, 29 on f09b6010b; one more than whatever it is when the commit lands); the candidate format (4) is unchanged, and a test pins that the candidate file carries no document link; the API (api/routers/documents.py) returns proves on an upload; search is unchanged (titles and notes); CV print choice (#308) does not apply; processing record (core/gdpr.py) unchanged, the same kind of data as any upload; tests/test_wiki_surface.py does not apply (no plugin-surface name); tests/test_page_coverage.py and tests/security/test_isolation_sweep.py need no entry unless a page is added; strings in the working catalogues (en-GB, fr-FR, pt-PT as drafts) and Weblate (#349).

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

From the list of 2026-10-02, under Documents: "Add diploma, supplement diploma, and certificates, including EDC." A certificate can be uploaded today. A diploma and a diploma supplement cannot be told apart from "Other", and none of them is tied to the entry of the career record it is the proof of. The EDC (a European Digital Credential, assumed here to be the Europass one) is #670. ## What exists today - **One of the three exists.** `DocumentKind.CERTIFICATE` ("Certificate") is a kind Postulo *holds* rather than writes: "somebody else issues these, and Postulo keeps the file" (`src/postulo/documents/kinds.py:61`, `:179`). There is no diploma and no diploma supplement. The registry takes a new kind without a migration, because the pickers read it through a callable (`kinds.py:91-105`, `:131-133`); `tests/test_document_kinds.py:33-47` fails if a stored kind is not described and the other way round. - **Each kind is another switch on every store connection.** `kind_specs` builds one per registered kind, on unless unset (`documents/stores.py:112-125`). - **An upload is not tied to anything in the career.** An `UploadedDocument` has a title, a kind, a file, notes, a language, a checksum and a version chain (`documents/models.py:564-640`). `Education` (qualification, institution, place, dates, grade; `resume/models.py:101-117`) and `Certification` (name, issuer, issued on, expires on, credential link; `:211-222`) have no document. The career record travels without documents (the candidate file), and the wiki says what it leaves out. - **There is already a way to point at a career entry without a column per kind.** `CVItem` uses a generic relation "because … six nullable foreign keys … would need widening every time a new kind of item is added" (`documents/models.py:378-413`), and the importer resolves it by section and local id (`core/importer.py:966-977`). The accolades in #693 to #697 will add more sections. - **What the Files list offers.** No filter by kind (D2, #667), and an extension list with no `json` or `xml` (`documents/models.py:585-589`). - **What a diploma supplement is.** A document a higher-education institution issues with a diploma in the Bologna countries: eight sections (the holder, the institution, the level, the content, the function, the certification, the national system and further information), in a model the European Commission, the Council of Europe and UNESCO agreed. "It does, however, not represent a Curriculum Vitae or a substitute for the original qualification" (<https://education.ec.europa.eu/education-levels/higher-education/inclusion-and-connectivity/diploma-supplement>). So it is a second document beside the diploma, not a version of it. Checked against `main` at `f09b6010b`. ## Proposal 1. **Two kinds.** `DocumentKind.DIPLOMA` ("Diploma") and `DocumentKind.DIPLOMA_SUPPLEMENT` ("Diploma supplement"), registered with the others as held, not authored, with labels in the catalogues. *Certificate* stays as it is. The "Reference" kind of #666 is unrelated. 2. **A link from a document to the entry it proves.** `UploadedDocument.proves`: a generic link (`content_type`, `object_id`, optional) to one of the owner's career entries, as `CVItem` does, limited to the models that can be proven (`Education` and `Certification` now, a section later by adding one line). Written only through a form field that offers the owner's own entries, labelled by what they say ("BSc Computer Science, University of Aveiro"), and checked again on save that the entry has the same owner. A diploma and its supplement point at the same entry; an entry may have several documents; a document proves at most one entry. 3. **Cleared, not cascaded.** Deleting the entry clears `proves` on its documents (a receiver, as `forget_the_source_of_a_render` clears a render's source, `documents/signals.py:72-89`); a `GenericRelation` on the entry would delete the diploma with it, which is the opposite of what is wanted. 4. **Seen from both ends.** The upload form shows "It proves" when the kind is certificate, diploma or diploma supplement. The Files list shows the entry beside the kind, and the Files list's kind filter (#667) offers the three. An education or certification row on the career page says how many documents prove it and links to them. None of this prints on a CV, and the candidate file does not carry the link. 5. **Wiki.** *Files and what you sent* names the three kinds and the link; *Your career record* says an entry can have its proofs. ## Decisions - **Which kinds.** The note names three. **Recommended:** exactly those, plus whatever a plugin registers. A transcript of records and a micro-credential are natural next kinds and need nothing but a registration. - **Naming.** The note says "supplement diploma"; the official name is *Diploma Supplement*. **Recommended:** "Diploma supplement". - **The link: generic or two foreign keys.** **Recommended:** generic, on the document side, as `CVItem` does. Two nullable keys would be enough today and would have to be widened for each accolade section; a link on the entry's side (a many-to-many) would put a private document into the career block, which #684 to #686 and the candidate file both read. - **What deleting a document does to the entry.** Nothing: the entry is the person's claim and does not depend on having a proof. - **Stores.** The two new kinds default to on, as every kind does. A diploma is what somebody most wants archived. (Contrast the reference letter, #666, which is about another person.) - **Required or optional.** The link is optional: a scan of a diploma uploaded before the entry exists is common. - **EDC.** A European Digital Credential is a *file format* of a certificate or a diploma, with a seal to check, and is handled in #670. It will be uploaded as one of these kinds. - **Conflict with an earlier decision.** None found. #131 left the career's `institution` and `issuer` free text; the link does not change that. ## Builds on #28 and #133 (letter and document kinds, closed), #131 (career entries, closed), #663 (the integrity of an uploaded document matters most for these). ## Needed by #670 (an EDC is a certificate or a diploma with a seal), #684 (an entry with an EQF level may show its proof). ## Done when - `tests/test_document_kinds.py`: the two kinds are described and storable (the existing tests at `:33-47` cover it once they are registered), a store connection has a switch for each, and a plugin can still register another. - `tests/test_documents.py` (new tests): a diploma and a supplement can be uploaded and both point at one `Education` entry; the form offers only the owner's own entries; another person's entry is refused by the form and by the save; deleting the entry clears the link and keeps the files; deleting the document leaves the entry. - `tests/test_career_sections.py`: the career page shows the number of documents on an entry and links to them. - `tests/test_export.py`: the archive carries `proves` as a section and a local id and restores it; an older archive loads with no links. - New fields on a record, so also: the archive format goes up by one (`core/export.py` `FORMAT_VERSION`, 29 on `f09b6010b`; one more than whatever it is when the commit lands); the candidate format (4) is unchanged, and a test pins that the candidate file carries no document link; the API (`api/routers/documents.py`) returns `proves` on an upload; search is unchanged (titles and notes); CV print choice (#308) does not apply; processing record (`core/gdpr.py`) unchanged, the same kind of data as any upload; `tests/test_wiki_surface.py` does not apply (no plugin-surface name); `tests/test_page_coverage.py` and `tests/security/test_isolation_sweep.py` need no entry unless a page is added; strings 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:58:49 +00:00
Author
Owner

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

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