Documents: untagged PDFs, CV arrows with the #203 bug, a letter preview that 500s, Europass inventing levels, long URLs off the page #235

Closed
opened 2026-09-15 21:23:31 +00:00 by tiagoagueda · 0 comments
Owner

Found in the 2026-09-15 code audit.

1. PDFs are untagged: no PDF/UA structure, no outline, no PDF/A

  • documents/pdf.py:92 calls write_pdf() without pdf_tags or pdf_variant.
  • pdf.py:140 calls Chromium's page.pdf(...) without tagged=True, outline=True.
  • Job titles are <p class="role"> (themes/base_cv.html:84-90), so nothing below <h2> can be navigated by a screen reader or read as structure by an ATS. Letter section titles are plain paragraphs.
  • There is no author metadata.
  • The lang work in #67 reaches a screen reader only through a tag tree, and these PDFs have none.

Fix:

  • WeasyPrint: pdf_variant="pdf/ua-1" (or pdf_tags=True), plus pdf/a-3u for renders copied to stores.
  • Chromium: tagged=True, outline=True.
  • Entry titles as <h3>, and author metadata.
  • tests/test_pdf_render.py asserts /StructTreeRoot, /Lang and /Title.

2. The CV's entry arrows still have the bug #203 fixed on the career page

  • documents/views.py:163: item.order = max(0, item.order ± 1). Up at 0 does nothing, one down can jump past several entries, and entries sharing a number don't visibly move.
  • New entries get order = count + added (:122-131), which collides after removals.
  • Nothing tests cv_item_move beyond page coverage.

Fix: reuse resume/ordering.move and renumber over cv.items.order_by("order", "pk"), and grey out the arrow at either end.

3. Letter preview

  • letter_detail.html:15 links to the preview with no application, and Send freezes the letter without showing the filled text.
  • ?application=abc reaches filter(pk=...) and raises ValueError, a 500 (views.py:273-277).
  • Placeholders with no value fill with empty strings (rendering.py:216-218), so a preview reads "Dear ," and a posting with no location sends a gap.
  • There is no placeholder for the contact person, although the follow-up starter asks for "[name]".

Fix:

  • An application picker on the letter page, and a "preview the filled letter" step in Send.
  • Visible markers for unfilled placeholders, with a warning before freezing.
  • Validate the query parameter.
  • Consider a {{ contact }} placeholder.

4. The Europass import invents language levels and ignores the file's language

  • resume/importing.py:214 uses proficiency or "b1", so a file with no CEFR levels claims B1.
  • The file's locale is never read, so profile.record_language stays blank and the #131 fallback warnings fire wrongly.
  • Skill group names are fixed English words ("Digital", "Job-related"; plugins/europass/reader.py:82-88), even for a Portuguese record.
  • Plausible: today's Europass editor exports HR-Open "Candidate" XML, or a PDF with the XML embedded, not SkillsPassport. read_xml refuses anything without LearnerInfo, and both test fixtures are SkillsPassport.

Fix:

  • Leave the level unset and flag it on the review page.
  • Set record_language from the file's locale when it is blank, and translate the group labels.
  • Check a real current export; add a Candidate reader and PDF-attachment extraction if needed.

5. Long URLs can run off the page

plain/cv.html, classic/cv.html and both letter themes set no overflow-wrap, and .dates is nowrap inside a flex row. The portfolio themes already handle it (plain/portfolio.html:39). Contact details are also separated only by CSS-generated " · ", which some text extractors drop.

Fix: overflow-wrap: anywhere on every base theme, real separator characters in the markup, and a render test with a 120-character URL.

Found in the 2026-09-15 code audit. ## 1. PDFs are untagged: no PDF/UA structure, no outline, no PDF/A - `documents/pdf.py:92` calls `write_pdf()` without `pdf_tags` or `pdf_variant`. - `pdf.py:140` calls Chromium's `page.pdf(...)` without `tagged=True, outline=True`. - Job titles are `<p class="role">` (`themes/base_cv.html:84-90`), so nothing below `<h2>` can be navigated by a screen reader or read as structure by an ATS. Letter section titles are plain paragraphs. - There is no author metadata. - The `lang` work in #67 reaches a screen reader only through a tag tree, and these PDFs have none. **Fix:** - WeasyPrint: `pdf_variant="pdf/ua-1"` (or `pdf_tags=True`), plus `pdf/a-3u` for renders copied to stores. - Chromium: `tagged=True, outline=True`. - Entry titles as `<h3>`, and author metadata. - `tests/test_pdf_render.py` asserts `/StructTreeRoot`, `/Lang` and `/Title`. ## 2. The CV's entry arrows still have the bug #203 fixed on the career page - `documents/views.py:163`: `item.order = max(0, item.order ± 1)`. *Up* at 0 does nothing, one *down* can jump past several entries, and entries sharing a number don't visibly move. - New entries get `order = count + added` (`:122-131`), which collides after removals. - Nothing tests `cv_item_move` beyond page coverage. **Fix:** reuse `resume/ordering.move` and `renumber` over `cv.items.order_by("order", "pk")`, and grey out the arrow at either end. ## 3. Letter preview - `letter_detail.html:15` links to the preview with no application, and *Send* freezes the letter without showing the filled text. - `?application=abc` reaches `filter(pk=...)` and raises `ValueError`, a 500 (`views.py:273-277`). - Placeholders with no value fill with empty strings (`rendering.py:216-218`), so a preview reads "Dear ," and a posting with no location sends a gap. - There is no placeholder for the contact person, although the follow-up starter asks for "[name]". **Fix:** - An application picker on the letter page, and a "preview the filled letter" step in *Send*. - Visible markers for unfilled placeholders, with a warning before freezing. - Validate the query parameter. - Consider a `{{ contact }}` placeholder. ## 4. The Europass import invents language levels and ignores the file's language - `resume/importing.py:214` uses `proficiency or "b1"`, so a file with no CEFR levels claims B1. - The file's `locale` is never read, so `profile.record_language` stays blank and the #131 fallback warnings fire wrongly. - Skill group names are fixed English words ("Digital", "Job-related"; `plugins/europass/reader.py:82-88`), even for a Portuguese record. - Plausible: today's Europass editor exports HR-Open "Candidate" XML, or a PDF with the XML embedded, not SkillsPassport. `read_xml` refuses anything without `LearnerInfo`, and both test fixtures are SkillsPassport. **Fix:** - Leave the level unset and flag it on the review page. - Set `record_language` from the file's locale when it is blank, and translate the group labels. - Check a real current export; add a Candidate reader and PDF-attachment extraction if needed. ## 5. Long URLs can run off the page `plain/cv.html`, `classic/cv.html` and both letter themes set no `overflow-wrap`, and `.dates` is `nowrap` inside a flex row. The portfolio themes already handle it (`plain/portfolio.html:39`). Contact details are also separated only by CSS-generated " · ", which some text extractors drop. **Fix:** `overflow-wrap: anywhere` on every base theme, real separator characters in the markup, and a render test with a 120-character URL.
tiagoagueda added this to the 0.4.0 milestone 2026-09-15 21:33:28 +00:00
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#235
No description provided.