More kinds of documents: motivation letters, portfolios as links, video CVs #28
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#28
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?
Observation
What exists today
CoverLetter: name, subject, body, template flag, theme) — one kind of letter, with no field to say which kind. An uploaded document with aDocumentKindof CV, cover letter, certificate, portfolio, reference or other (documents/models.py, line 190). So "portfolio" exists already — as a file.pdf doc docx odt rtf txt png jpg jpeg, 20 MB per file (MAX_UPLOAD_BYTES,documents/forms.py; the wiki says so on Files and what you sent). No audio or video type, and a video CV is typically 30 to 200 MB.snapshot_cvandsnapshot_letter(documents/rendering.py) as aRenderedDocumentcarrying the sameDocumentKind; uploads are attached throughApplication.sent_uploads.serve_private_file(core/files.py) streams with Django'sFileResponse, which does not answerRangerequests. Fine for a PDF; for a video it means no seeking, and Safari refuses to play media at all from a server without byte ranges. The X-Accel and X-Sendfile options hand this to the web server, where it works.media-src, sodefault-src 'none'blocks a<video>element even from Postulo's own media; there is noframe-srceither, so embedding a YouTube or Vimeo player is out — which is right, since an embed is a third-party request on every page view.write_archive(core/export.py, line 359) reads every file whole (handle.read()) into aBytesIOby default. A few PDFs are nothing; a handful of videos is a memory problem.The observation names three things, and they are three different problems: a motivation letter is a kind of text, a portfolio is mostly a link, a video CV is a large binary.
Shape
1. Letters get a kind.
CoverLetter.kind, with existing rows migrated to cover letter:Each kind has its own starter text and a sensible default theme; rendering is unchanged.
DocumentKindgains motivation letter for renders and uploads. The letters page gets a kind filter, and the navigation says Letters. A translation note that matters: in French and Portuguese lettre de motivation and carta de motivação are the everyday words for a cover letter. The two kinds must be told apart by their shape in the interface — length, sections, addressee — not by their names alone, and the translators need the distinction explained indocs/TRANSLATING.md.2. Portfolios: links, since files already exist. A
Linkdocument: title, URL, kind (portfolio, personal site, GitHub or GitLab profile, Behance or Dribbble, publication, video, other), a line of description. Links attach to applications the way uploads do (sent_links), join the CV as a Links section — the CV is built from items through a generic relation (CVItem), so this is a new item type, not a new mechanism — and travel in the export. No fetching by default: Postulo makes no request on its own. A Check links action, per link and opt-in, sends a HEAD request and reports dead ones, because a portfolio URL that 404s on the day the recruiter clicks is the worst possible outcome. Portfolio files stay uploads: the kind is already there.3. Video CVs, in two honest steps.
Linkof kind video: an unlisted YouTube or Vimeo, a PeerTube instance, a Nextcloud share. Shown as a link with the provider's name; never embedded. This is what most people actually do, and it is a day's work on top of item 2.mp4andwebm(H.264/AAC and VP9/Opus, which every browser plays) under its own cap,POSTULO_MAX_VIDEO_MB, default 200 and an operator's to lower: a Raspberry Pi with a small card must be able to say 50. No transcoding: no ffmpeg dependency, the person uploads what they exported, and the form says which formats play. Playback with<video controls preload="metadata">needs two changes:media-src 'self'in the production CSP, and byte-range support inserve_private_file— or the documented recommendation to use X-Accel/X-Sendfile for instances that host video. Uploads already stream to disk (Django spills anything over 2.5 MB to a temporary file), but the reverse proxy's body limit — nginx defaults to 1 MB — must be documented on Configuration, or the first upload fails with a proxy error nobody can read.ZIP_STOREDfor media (videos do not compress), instead of reading them into memory. Worth doing regardless of video.put()contract returns "not for me" and the person sees it.4. What was sent. Today renders point at the application and uploads sit in
sent_uploads. Links addsent_links. Three tables is tolerable; a single sent items table is the refactor to do if a fourth appears.5. Documentation. Files and what you sent and Cover letters (which becomes Letters) are rewritten; Configuration gains the video cap and the proxy body-size note;
seed_demogets a motivation letter, a portfolio link and a video link so the pages show what they are for.Classification
Enhancement. Not breaking: new kinds and one new model; existing letters migrate to cover letter; the 20 MB cap on documents stays; the CSP gains one directive.
Open questions