Every document should record the language it is in, uploads and sent snapshots included #283
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
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#283
Loading…
Add table
Add a link
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?
#280 asks for a flag on the cards, and scoped uploads and sent documents out on the grounds that neither carries a language. This issue settles that the other way: every document Postulo holds should record the language it is in, and every one should have somewhere to see it and — where it can honestly be changed — to set it. #280 then has something to draw for all four kinds instead of two.
The field decides the
langof a PDF that goes to an employer, which is what a screen reader reads it out with and what WeasyPrint hyphenates by (#223); it decides where a store files it (#130); and for a sent document it is part of the record of what was actually sent. Two of the four kinds have it. The other two guess.What each kind has today
CV(models.py:120)CoverLetter(models.py:333)UploadedDocument(models.py:383)RenderedDocument(models.py:509)Three things that are wrong today, not merely missing
1. An upload's language is guessed, and the guess is written into other people's systems.
documents/stores.py:50,_language_of, says so in its own docstring:A scanned German certificate uploaded by somebody who reads Postulo in English is handed to Paperless as English. The comment three lines below the call site (
stores.py:105) states the purpose that defeats: "A French CV filed in Paperless under the language its owner happens to read Postulo in is filed wrongly, and the whole point of sending it there is to find it again." The reasoning was right and the upload case was left as the best guess available. Now there can be a better one: ask.2. A sent document's language is read from its source, live, and the source can change.
For a render,
_language_offalls through todocument_language(source)— evaluated whenever a store is told about it, not when the PDF was made. Change the CV's language after sending it and the frozen PDF is suddenly described differently. This contradicts what the model is for:RenderedDocument's own docstring is "A PDF exactly as it was sent, kept unchanged", andsent_tois stored as text rather than followed as a link precisely so a later edit cannot rewrite history (models.py:544).3. When the source is deleted, the language of what the employer received is lost outright.
sourceis legitimately empty — an upload never had one, a report has none, andsignals.pyclears the link when a CV is deleted so that the frozen PDF survives it (models.py:552-560)._language_ofthen falls back to the owner's profile. So the one document that can never be regenerated is the one whose language is least recoverable.The value is already computed at the right moment and thrown away:
rendering.py:435doeswith translation.override(document_language(cv))to build the title, two lines aboveRenderedDocument(...). Storing it is a one-line change to something already in hand.What to build
UploadedDocument.language— a real field,blank=True, same shape as the other two, and a picker on the form via the existingLanguageChoiceMixin(documents/forms.py:49). Blank must mean "not said", not "follow your profile": for a CV that fallback is a sensible default about text Postulo renders, but for a file somebody else made it is a claim about contents Postulo has never read._language_ofshould return""for an upload nobody has told, and a store should be sent no language rather than a wrong one. The upload form already allows editing everything except the bytes (#217), so an existing upload can be corrected.RenderedDocument.language— captured at snapshot time insnapshot_cv,snapshot_letterandsnapshot_report, alongsidesent_toandchecksum, andeditable=Falselike them._language_ofthen reads the stored value and stops consulting the source at all._language_ofbecomes a reader, not a resolver: stored value, else nothing. Its present cleverness is the bug.The UI half
templates/documents/upload_list.html:34) — the row already reads kind · version · date; the language joins it, and "not said" is worth showing as such rather than as a blank, because it is the prompt to fill it in.templates/documents/rendered_list.html:34) — beside the kind, read-only.Points to settle
sourcestill points at something,document_language(source)today is a better answer than nothing — it is the same value the store is already being given. Freezing that at migration time is an improvement even though it is not certain, but it should be a deliberate decision, not a side effect.documents/urls.pygives it list, download and archive only). If the captured value is ever wrong, there is no way to fix it. Leaving it that way is consistent; allowing a correction to metadata about the artefact while the artefact stays frozen is also defensible. Decide it here rather than discovering it later.Notes for whoever takes it
core/export.py:45;UPLOAD_FIELDS(:188) andSENT_FIELDS(:189) gain the field, and the importer must keep reading 16, where it is absent.CV_FIELDSandLETTER_FIELDSalready carry it.CVOuthaslanguage(api/schemas.py:370) andLetterOutdoes not, althoughCoverLetterhas had the field all along.DocumentOut(:406), which serves both uploads and renders, has none. All three want it.documents/kinds.pyis the vocabulary of kinds; a language is not a kind and nothing there needs touching.documents/models.pyshifts the#:source references of every translatable string below them and breaksscripts/messages.py extract --checkwith no new string — #193 was this exact file. Runextract, then scope the revert tosrc/postulo/plugins/*/locale.draft(fr-fr,pt-pt,pt-br); the other 36 at the release sweep.Landed on
mainas3b173cf82.