mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
fix(jobbank-search)!: emit deadline as YYYY-MM-DD in search output
The feed's DD.MM.YYYY parenthetical passed through raw - documented, but contradicting the /scrape contract, every other portal, and this CLI's own detail command for the same job, and ambiguous to a date parser (01.09.2026: 1 Sep or 9 Jan). The known shape converts to ISO; løbende still maps to null; unrecognized shapes pass through for /rank's defensive handling. Breaking for anything parsing the old format - the README's own search example already showed ISO. Review finding F5 (2026-08-19), decision approved by Mads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b067928da7
commit
48965960d3
@@ -267,7 +267,7 @@ bun run src/cli.ts search --education 24 --suitable-for 2 --since 2026-03-01
|
|||||||
| `url` | string | Full URL to job posting |
|
| `url` | string | Full URL to job posting |
|
||||||
| `posted` | string | Publication date in ISO 8601 |
|
| `posted` | string | Publication date in ISO 8601 |
|
||||||
| `date` | string \| null | Publication date as `YYYY-MM-DD` (derived from `posted`), or `null` if absent |
|
| `date` | string \| null | Publication date as `YYYY-MM-DD` (derived from `posted`), or `null` if absent |
|
||||||
| `deadline` | string \| null | Application deadline as `DD.MM.YYYY` string, or `null` if "løbende" / not present |
|
| `deadline` | string \| null | Application deadline as `YYYY-MM-DD` (converted from the feed's `DD.MM.YYYY`), or `null` if "løbende" / not present |
|
||||||
|
|
||||||
> `meta.total` is fetched from the HTML page `<title>` in a secondary request (pattern: `"{N} relevante job og karriereopslag"`). If the secondary request fails, `meta.total` is `null`.
|
> `meta.total` is fetched from the HTML page `<title>` in a secondary request (pattern: `"{N} relevante job og karriereopslag"`). If the secondary request fails, `meta.total` is `null`.
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,11 @@ export function parseRssDescription(desc: string): ParsedDescription {
|
|||||||
if (deadlineStr.toLowerCase() === "løbende" || deadlineStr.toLowerCase() === "lobende") {
|
if (deadlineStr.toLowerCase() === "løbende" || deadlineStr.toLowerCase() === "lobende") {
|
||||||
deadline = null
|
deadline = null
|
||||||
} else {
|
} else {
|
||||||
deadline = deadlineStr
|
// The feed writes DD.MM.YYYY; the /scrape contract (and this CLI's own
|
||||||
|
// detail command) use YYYY-MM-DD. Convert the known shape; anything else
|
||||||
|
// passes through so an unexpected value stays visible downstream.
|
||||||
|
const dmy = deadlineStr.match(/^(\d{2})\.(\d{2})\.(\d{4})$/)
|
||||||
|
deadline = dmy ? `${dmy[3]}-${dmy[2]}-${dmy[1]}` : deadlineStr
|
||||||
}
|
}
|
||||||
// Remove the deadline portion from rest
|
// Remove the deadline portion from rest
|
||||||
rest = rest.substring(0, deadlineMatch.index).trim()
|
rest = rest.substring(0, deadlineMatch.index).trim()
|
||||||
|
|||||||
@@ -56,10 +56,17 @@ describe("parseRssDescription", () => {
|
|||||||
jobType: "Fuldtidsjob, Graduate/trainee",
|
jobType: "Fuldtidsjob, Graduate/trainee",
|
||||||
company: "Acme A/S",
|
company: "Acme A/S",
|
||||||
location: "København",
|
location: "København",
|
||||||
deadline: "31.07.2026",
|
deadline: "2026-07-31",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("passes an unrecognized deadline shape through for downstream defensive parsing", () => {
|
||||||
|
const parsed = parseRssDescription(
|
||||||
|
"Fuldtidsjob hos Acme A/S, Odense (Ansøgningsfrist: snarest muligt)",
|
||||||
|
);
|
||||||
|
expect(parsed.deadline).toBe("snarest muligt");
|
||||||
|
});
|
||||||
|
|
||||||
test("normalizes a rolling deadline to null", () => {
|
test("normalizes a rolling deadline to null", () => {
|
||||||
expect(
|
expect(
|
||||||
parseRssDescription("Deltidsjob hos Example ApS, Aarhus (Ansøgningsfrist: løbende)"),
|
parseRssDescription("Deltidsjob hos Example ApS, Aarhus (Ansøgningsfrist: løbende)"),
|
||||||
|
|||||||
@@ -33,6 +33,6 @@ describe("Jobbank search normalization", () => {
|
|||||||
expect(result.company).toBe("Acme A/S");
|
expect(result.company).toBe("Acme A/S");
|
||||||
expect(result.location).toBe("København");
|
expect(result.location).toBe("København");
|
||||||
expect(result.url).toBe("https://jobbank.dk/job/12345/acme/data-scientist");
|
expect(result.url).toBe("https://jobbank.dk/job/12345/acme/data-scientist");
|
||||||
expect(result.deadline).toBe("31.07.2026");
|
expect(result.deadline).toBe("2026-07-31");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -65,6 +65,15 @@ per-file diff commands.
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- **BREAKING (jobbank forks): `jobbank-search` search output emits `deadline` as
|
||||||
|
`YYYY-MM-DD`** - the feed's `DD.MM.YYYY` parenthetical was passed through raw,
|
||||||
|
contradicting the `/scrape` contract, the other portals, and the same CLI's own
|
||||||
|
`detail` command (which already emits ISO for the same job). `01.09.2026` is also
|
||||||
|
ambiguous to a date parser (1 Sep vs 9 Jan). The known shape is now converted;
|
||||||
|
"løbende" still maps to `null`, and an unrecognized shape passes through for `/rank`'s
|
||||||
|
defensive handling. Anything parsing the old `DD.MM.YYYY` output must update - though
|
||||||
|
the README's own search example already showed the ISO form. Pinned in
|
||||||
|
`tests/rss-parsing.test.ts` and `tests/search-normalization.test.ts`.
|
||||||
- **Job matching reframed around function, not title** (`framework_version` 1.2.2 -> 1.2.3 in
|
- **Job matching reframed around function, not title** (`framework_version` 1.2.2 -> 1.2.3 in
|
||||||
`04-job-evaluation.md`) - title-lookalike matching throws away career capital that doesn't
|
`04-job-evaluation.md`) - title-lookalike matching throws away career capital that doesn't
|
||||||
fit one job-title box (e.g. a background spanning research leadership, platform ownership,
|
fit one job-title box (e.g. a background spanning research leadership, platform ownership,
|
||||||
|
|||||||
Reference in New Issue
Block a user