Repository navigation
fix(job_key): hash the URL alone so a re-listed non-Latin title keeps one key - #502
Conversation
MadsLorentzen
left a comment
There was a problem hiding this comment.
Reproduced on master with your three commands (three keys for one URL) and on the branch (one key, untitled-e9df22). The pinning test fails against master's job_key.py and passes here; 490 OK on Windows, lint and guards green, CI 18/18. The live API does return Инженер and инженер across rows today, and freehire's slugs carry no six-digit run, so the numeric-id branch really never fires for it. Keying the hash on the URL alone is the right shape: the URL is the identity /scrape stores, and the title was never a stable half.
Two things before merge:
- No CHANGELOG entry. Please add one under
[Unreleased]/### Fixednamingtools/job_key.pyand the test, and say in it that entries written before the fix for a non-Latin title on a portal without a numeric id will re-key once on the next scrape (--auditreports them, never rewrites). - Out of interest, not a blocker: did
--auditreportduplicate_urlson a realseen_jobs.jsonof yours, or is the same-URL re-list inferred from the case variation across rows? The fix stands either way; I would like to log which it was.
Merge on the push.
|
CHANGELOG added at I checked that last clause rather than asserting it. A On your second question — inferred, not observedInferred. I want to be precise about it, since you asked to log which. What I actually observed:
What I did not observe: Both zero. Two reasons, and the second is the interesting one: those 884 keys are the So the honest characterisation is: a latent defect, demonstrated by mechanism plus live data, not one caught in the wild in a file of mine. It needs a re-list with changed casing between two scrapes of the same posting, which is a narrow window to catch by accident even when the conditions are all present. If that changes your read of the priority, say so — the fix stands on the mechanism either way, but you should have the real provenance rather than my summary of it. |
Adds the [Unreleased] / Fixed entry for MadsLorentzen#501, naming tools/job_key.py and tests/test_job_key_url_identity.py. The entry states the migration consequence in bold: an entry written before this fix, for a non-Latin title on a portal without a numeric id, re-keys once on the next scrape. It also names where those entries surface, keys_not_matching_current_rule, and that --audit never rewrites them. Verified that claim rather than asserting it. A seen_jobs.json holding one entry under its pre-fix key for a Cyrillic-titled freehire URL reports keys_not_matching_current_rule: 1 under the fixed audit. The same audit over a real 884-entry seen_jobs.json reports 0, because those keys are the company_title form that is_canonical() excludes from the drift check.
5854215 to
936c846
Compare
|
Rebased onto The conflict was the v1.7.2 cut: it rolled One correction to flag, since you asked for the test to be named. The first version of that entry named
|
|
One heads-up so you are not waiting on a check that cannot start: the run on For what it is worth, no Actions run ever completed on this PR's earlier heads either — |
|
Merged, thank you. Re-verified Provenance logged as you gave it: inferred from mechanism plus live data, with |
…n fragment (#514) The hash fallbacks from #487 and #502 fired only when the slug was completely empty. A mixed-script name kept its Latin or digit fragment and skipped them: "Программист 1С" and "Аналитик 1С" at one company both keyed as company-6d769a_1, "Python-разработчик" and "Python-аналитик" as ..._python, "Сбер AI" and "Яндекс AI" as ai_... /scrape Step 4 then dropped the second posting as already seen, and because the dict key is overwritten, --audit could not report the collision as a duplicate URL. Treat the fold as lossy whenever it drops a letter outside the Latin script, and let the existing fallbacks take over: the title half uses the portal's numeric id or the URL hash with the fragment kept as a readable prefix, the company half uses the NFC-casefold name hash with the fragment as prefix. Latin letters that also lack a decomposition (ø, æ, ß, ł) are deliberately not counted, so "Ørsted" still keys as "rsted" and a live Danish seen_jobs.json does not re-key. Seven new cases; the four collision cases fail on master.
What changed and why
Closes #501.
make_key's hash for a title that slugifies to nothing coveredtitle + url. The URL identifies the posting; the title does not. So when a portal re-listed the same posting with the title altered, the hash moved andseen_jobs.jsongained a second entry for one URL — the failuretools/job_key.pyexists to prevent, and the one.claude/skills/job-scraper/SKILL.mdstates the rule against: "The key must be a pure function of the posting."The numeric-id branch above it never fires for
freehire._JOB_IDwants a run of six digits andfreehire's real slugs are alphanumeric, so for its Cyrillic and Greek titles the hash is the only key half there is.The change hashes the URL alone when one is present, and falls back to the title only when there is no URL. One line of logic; the rest is the comment explaining why.
I took this shape over two alternatives. Widening
_JOB_IDto accept alphanumeric slugs would key on a slug that can itself change on re-list. Transliterating Cyrillic and Greek would give readable keys but needs a transliteration table, which sits against the zero-runtime-dependency norm for portal tooling. Happy to switch if you prefer either.Failing case / reproduction
Real values from the public API (
/api/v1/agent/jobs/search?q=инженер), not constructed.freehire-search's SKILL.md describes the portal as covering "many countries and markets", and that query returns bothИнженерandинженерacross rows today, so the case-variation below is something the portal really emits.On
master, three keys for one URL:On this branch, one:
python3 tools/job_key.py --auditalready reports this shape underduplicate_urls, so the defect was observable with the tooling already in the repo.Verification
Two tests added to
tests/test_job_key.py, both using the real slugs above:test_one_url_keeps_one_key_when_a_non_latin_title_is_re_listed— fails on master, passes here. This is the one that pins the fix.test_distinct_urls_still_get_distinct_keys— passes both ways. It is the guard: collapsing title variants must not collapse two genuinely different postings from one company.Ran what CI runs:
488 before, 490 after. No existing test changed behaviour — the Korean-title, LinkedIn-id and Cyrillic-company cases all still pass untouched.
No CLI was modified, so no
bunlegs were affected.Scope
Only titles that slugify to nothing — Cyrillic, Greek, CJK — on portals whose URLs carry no six-digit id. Latin-titled postings are untouched, and LinkedIn still resolves through the numeric-id branch, which the existing test pins.
Existing state files are unaffected in the sense that no key is rewritten; the audit reports legacy entries and never rewrites, as before. New scrapes of an affected posting will key differently from an entry written before this change, which is the same one-time shift any key-derivation fix causes.