Spreadsheet import: statuses are matched as substrings, so \"Rejected after interview\" imports as Interviewing and \"No offer\" as Offer #467

Closed
opened 2026-10-01 10:46:54 +00:00 by tiagoagueda · 0 comments
Owner

Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading core/csv_import.py and calling map_status and map_channel on ordinary phrases.

Checked again on 2026-10-01 against main at 0a5cad587: still present. The code is now at core/csv_import.py:760-770 (map_status), :264-365 (the table, with no at :340) and :773-782 (map_channel).

map_status tries an exact match and then returns the first alias, in the table's order, that appears anywhere inside the cell. The table's order and its short aliases then decide the result: interview comes before rejected, offer before declined, and no (:336) is found inside unknown, nothing and announced. The match is reported as recognised, so the original words are not kept in a note, which is what the module promises for a status it does not know.

  • Where: core/csv_import.py:765-775 (map_status: for alias, status in STATUS_ALIASES.items(): if alias in key: return status, True); the table at :260-361; map_channel at :778-787 does the same with CHANNEL_ALIASES.
  • What happens: on the snapshot:
    • "Rejected after interview" and "Interview - rejected" give Interviewing;
    • "No offer", "Offer declined", "Declined offer" and "Refused offer" give Offer;
    • "Unknown", "Not sure", "Nothing yet" and "announced" give Rejected;
    • "latest" and "Contest" give Assessment (through test);
    • map_channel: "Referral via LinkedIn" gives a job board, "eventually" an event.
      The status is applied through change_status and written on the timeline as if the sheet had said it. The preview shows the mapped status, but nothing marks it as a guess.
  • Why it matters: a rejected application imports as open and is later flagged as gone quiet; an application the person never heard back on is closed as a rejection; offers appear that were refused. Insights and the report count all of these.
  • Fix: match whole words (a \b-bounded pattern per alias), never substrings. When several aliases match, prefer the outcome words (rejected, declined, refused, withdrawn) over the stage words, or treat the cell as unrecognised: applied with the original kept in a note, as the docstring above the table says. Accept the bare no only as the whole cell. Apply the same to map_channel.

Done when

  • tests/test_csv_import.py asserts that "Rejected after interview" and "Offer declined" map to Rejected, that "Unknown" and "Nothing yet" are unrecognised and kept in a note, and that "Referral via LinkedIn" maps to a referral.
Found on 2026-09-29 in a read-only review of postulo at 95348908d, reading `core/csv_import.py` and calling `map_status` and `map_channel` on ordinary phrases. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. The code is now at `core/csv_import.py:760-770` (`map_status`), `:264-365` (the table, with `no` at `:340`) and `:773-782` (`map_channel`). `map_status` tries an exact match and then returns the first alias, in the table's order, that appears anywhere inside the cell. The table's order and its short aliases then decide the result: `interview` comes before `rejected`, `offer` before `declined`, and `no` (`:336`) is found inside `unknown`, `nothing` and `announced`. The match is reported as recognised, so the original words are not kept in a note, which is what the module promises for a status it does not know. - **Where:** `core/csv_import.py:765-775` (`map_status`: `for alias, status in STATUS_ALIASES.items(): if alias in key: return status, True`); the table at `:260-361`; `map_channel` at `:778-787` does the same with `CHANNEL_ALIASES`. - **What happens:** on the snapshot: - "Rejected after interview" and "Interview - rejected" give *Interviewing*; - "No offer", "Offer declined", "Declined offer" and "Refused offer" give *Offer*; - "Unknown", "Not sure", "Nothing yet" and "announced" give *Rejected*; - "latest" and "Contest" give *Assessment* (through `test`); - `map_channel`: "Referral via LinkedIn" gives a job board, "eventually" an event. The status is applied through `change_status` and written on the timeline as if the sheet had said it. The preview shows the mapped status, but nothing marks it as a guess. - **Why it matters:** a rejected application imports as open and is later flagged as gone quiet; an application the person never heard back on is closed as a rejection; offers appear that were refused. Insights and the report count all of these. - **Fix:** match whole words (a `\b`-bounded pattern per alias), never substrings. When several aliases match, prefer the outcome words (rejected, declined, refused, withdrawn) over the stage words, or treat the cell as unrecognised: *applied* with the original kept in a note, as the docstring above the table says. Accept the bare `no` only as the whole cell. Apply the same to `map_channel`. ## Done when - `tests/test_csv_import.py` asserts that "Rejected after interview" and "Offer declined" map to *Rejected*, that "Unknown" and "Nothing yet" are unrecognised and kept in a note, and that "Referral via LinkedIn" maps to a referral.
tiagoagueda added this to the 0.5.0 milestone 2026-10-01 10:46:54 +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#467
No description provided.