Europass import: a website or address already on the account under another kind or holder fails the whole import #616

Closed
opened 2026-10-01 10:48:24 +00:00 by tiagoagueda · 0 comments
Owner

Found on 2026-09-29 in a read-only review of postulo at 95348908d, comparing what the Europass writer checks before inserting with the uniqueness constraints on the rows it writes, and settled by calling importing.apply against such an account.

Checked again on 2026-10-01 against main at 0a5cad587: still present.

importing.apply writes the file's website only after asking whether the profile has a website-kind link, and its address only after asking whether the profile has an address. The constraints it writes against are wider: a WebLink is unique on its holder and URL whatever its kind, and a PostalAddress is unique on its owner and comparable form whatever it is attached to. A file that repeats either one raises IntegrityError inside the view's transaction.atomic(), the person gets a server error, and nothing of the import lands.

  • Where: resume/importing.py:141-145 (web_links.primary_for(profile, web_links.Kind.WEBSITE) is None, then save_only_link), core/web_links.py:227-241 (save_only_link looks for a hidden row with kind=kind only), against core/models.py:677-680 (web_link_unique_per_holder on content_type, object_id, url); resume/importing.py:95 (_write_address: if postal.for_holder(profile).exists()), against core/models.py:564-568 (address_unique_per_owner on owner, comparable); the transaction is resume/views.py:479-481.
  • What happens:
    1. A person keeps https://www.linkedin.com/in/lee as their social profile and has no website. Their Europass CV gives the same address as its website, as many do. Add all of this fails with UNIQUE constraint failed: core_weblink.content_type_id, core_weblink.object_id, core_weblink.url.
    2. A person has no address on their profile but has recorded the same address on one of their contacts. The import fails with UNIQUE constraint failed: core_postaladdress.owner_id, core_postaladdress.comparable.
      The parsed record stays in the session, so every retry fails the same way until the person finds and removes the row the import collides with, which nothing tells them.
  • Why it matters: a common Europass file cannot be imported at all, and the error page says nothing about why. The candidate-file importer already compares across kinds (resume/candidate.py:930-935, _same_address) and across the whole account (resume/candidate.py:870-899, comparable), so the two importers have drifted.
  • Fix: in importing.apply, skip the website when any link on the profile has the same address (compared as candidate._same_address compares), and in _write_address skip an address whose comparable_form() the owner already holds anywhere. Record each skip in Report.skipped in words, so the rest of the import lands and the person knows what was left out. Sharing the comparison helpers between the two importers would stop them drifting again.

Done when

  • tests/test_europass.py imports, through the view, a file whose website is the account's existing social link and a file whose address is already on one of the owner's contacts; both answer with a redirect, the rest of the record is added, and the skip is reported.
Found on 2026-09-29 in a read-only review of postulo at 95348908d, comparing what the Europass writer checks before inserting with the uniqueness constraints on the rows it writes, and settled by calling `importing.apply` against such an account. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. `importing.apply` writes the file's website only after asking whether the profile has a *website-kind* link, and its address only after asking whether the *profile* has an address. The constraints it writes against are wider: a `WebLink` is unique on its holder and URL whatever its kind, and a `PostalAddress` is unique on its owner and comparable form whatever it is attached to. A file that repeats either one raises `IntegrityError` inside the view's `transaction.atomic()`, the person gets a server error, and nothing of the import lands. - **Where:** `resume/importing.py:141-145` (`web_links.primary_for(profile, web_links.Kind.WEBSITE) is None`, then `save_only_link`), `core/web_links.py:227-241` (`save_only_link` looks for a hidden row with `kind=kind` only), against `core/models.py:677-680` (`web_link_unique_per_holder` on `content_type`, `object_id`, `url`); `resume/importing.py:95` (`_write_address`: `if postal.for_holder(profile).exists()`), against `core/models.py:564-568` (`address_unique_per_owner` on `owner`, `comparable`); the transaction is `resume/views.py:479-481`. - **What happens:** 1. A person keeps `https://www.linkedin.com/in/lee` as their social profile and has no website. Their Europass CV gives the same address as its website, as many do. *Add all of this* fails with `UNIQUE constraint failed: core_weblink.content_type_id, core_weblink.object_id, core_weblink.url`. 2. A person has no address on their profile but has recorded the same address on one of their contacts. The import fails with `UNIQUE constraint failed: core_postaladdress.owner_id, core_postaladdress.comparable`. The parsed record stays in the session, so every retry fails the same way until the person finds and removes the row the import collides with, which nothing tells them. - **Why it matters:** a common Europass file cannot be imported at all, and the error page says nothing about why. The candidate-file importer already compares across kinds (`resume/candidate.py:930-935`, `_same_address`) and across the whole account (`resume/candidate.py:870-899`, `comparable`), so the two importers have drifted. - **Fix:** in `importing.apply`, skip the website when any link on the profile has the same address (compared as `candidate._same_address` compares), and in `_write_address` skip an address whose `comparable_form()` the owner already holds anywhere. Record each skip in `Report.skipped` in words, so the rest of the import lands and the person knows what was left out. Sharing the comparison helpers between the two importers would stop them drifting again. ## Done when - `tests/test_europass.py` imports, through the view, a file whose website is the account's existing social link and a file whose address is already on one of the owner's contacts; both answer with a redirect, the rest of the record is added, and the skip is reported.
tiagoagueda added this to the 0.5.0 milestone 2026-10-01 10:48:24 +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#616
No description provided.