mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
fix(jobdanmark-search): filter search by the administrative regions locations returns (#650)
* fix(jobdanmark-search): filter search by the administrative regions locations returns `locations` lists two kinds of region: landsdele such as Østjylland (category `region`) and the administrative regions such as Region Midtjylland (category `administrativeRegion`), and the search API filters each by its own type. `--region` always sent type `region`, so an administrative region - and SKILL.md's own `--region Midtjylland` example - returned a successful search with zero jobs. Add `--administrative-region`, sent as the `administrativeRegion` filter, map each locations category to its flag in SKILL.md and the README, and give `--region` examples that exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changelog: place the #650 entry under [Unreleased] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Mads Lorentzen <madslorentzen_17@hotmail.com>
This commit is contained in:
5 files changed
+101
-5
No files matched your search
@@ -52,7 +52,8 @@ Key flags:
|
|||||||
- `--jobtitle-id <id>` — job title ID from `autocomplete` results
|
- `--jobtitle-id <id>` — job title ID from `autocomplete` results
|
||||||
- `--municipality <name>` — e.g. `Odense`, `København`, `Aarhus`
|
- `--municipality <name>` — e.g. `Odense`, `København`, `Aarhus`
|
||||||
- `--zip <code>` — zip code, e.g. `5000`, `8000`, `2100`
|
- `--zip <code>` — zip code, e.g. `5000`, `8000`, `2100`
|
||||||
- `--region <name>` — region name, e.g. `Midtjylland`, `Sjælland`
|
- `--region <name>` — landsdel (`locations` category `region`), e.g. `Sjælland`, `Østjylland`, `Fyn`
|
||||||
|
- `--administrative-region <name>` — one of the five regions (`locations` category `administrativeRegion`), e.g. `Region Midtjylland`
|
||||||
- `--job-type <types>` — comma-separated: `fuldtid`, `deltid`, `fleksjob`, `elev`, `studiejob`, `praktik`
|
- `--job-type <types>` — comma-separated: `fuldtid`, `deltid`, `fleksjob`, `elev`, `studiejob`, `praktik`
|
||||||
- `--page <n>` — page number (30 items per page, server-enforced)
|
- `--page <n>` — page number (30 items per page, server-enforced)
|
||||||
- `--limit <n>` — cap total results returned by CLI
|
- `--limit <n>` — cap total results returned by CLI
|
||||||
@@ -91,7 +92,7 @@ Use this to resolve search terms into precise job title IDs (`--jobtitle-id`) or
|
|||||||
bun run .agents/skills/jobdanmark-search/cli/src/cli.ts locations --query "<text>" [--limit <n>]
|
bun run .agents/skills/jobdanmark-search/cli/src/cli.ts locations --query "<text>" [--limit <n>]
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns matching municipalities, zip codes, and regions. Use `value` from results as `--municipality` or `--zip` in `search`.
|
Returns matching municipalities, zip codes, and regions. Pass an item's `value` to the `search` flag named by its `category`: `municipality` → `--municipality`, `zip` → `--zip`, `region` → `--region`, `administrativeRegion` → `--administrative-region`. A value under the wrong flag matches no jobs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ bun run src/cli.ts search [flags]
|
|||||||
| `--jobtitle-id` | number | — | Job title ID from `autocomplete` results |
|
| `--jobtitle-id` | number | — | Job title ID from `autocomplete` results |
|
||||||
| `--municipality` | string | — | Municipality name, e.g. `Odense`, `København` |
|
| `--municipality` | string | — | Municipality name, e.g. `Odense`, `København` |
|
||||||
| `--zip` | string | — | Zip code, e.g. `5000` |
|
| `--zip` | string | — | Zip code, e.g. `5000` |
|
||||||
| `--region` | string | — | Region name |
|
| `--region` | string | — | Landsdel: a `locations` item with category `region`, e.g. `Sjælland`, `Østjylland` |
|
||||||
|
| `--administrative-region` | string | — | Administrative region: a `locations` item with category `administrativeRegion`, e.g. `Region Midtjylland` |
|
||||||
| `--job-type` | string | — | Comma-separated job types: `fuldtid,deltid,fleksjob,elev,studiejob,praktik` |
|
| `--job-type` | string | — | Comma-separated job types: `fuldtid,deltid,fleksjob,elev,studiejob,praktik` |
|
||||||
| `--page` | number | `1` | Page number (30 items per page, server-enforced) |
|
| `--page` | number | `1` | Page number (30 items per page, server-enforced) |
|
||||||
| `--limit` | number | — | Cap total results returned by CLI (client-side) |
|
| `--limit` | number | — | Cap total results returned by CLI (client-side) |
|
||||||
@@ -420,7 +421,7 @@ bun run src/cli.ts locations --query "Sjælland"
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note**: Returns `[]` when no locations match. The `value` field for municipalities is the display name (string); for zip codes it is the numeric string. Use `value` directly as `--municipality` or `--zip` in `search`.
|
> **Note**: Returns `[]` when no locations match. The `value` field for municipalities is the display name (string); for zip codes it is the numeric string. Pass `value` to the `search` flag matching its `category`: `--municipality`, `--zip`, `--region` (category `region`, a landsdel such as `Sjælland`) or `--administrative-region` (category `administrativeRegion`, such as `Region Midtjylland`). Under any other flag the value matches no jobs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,10 @@ export const search = defineCommand({
|
|||||||
description: "Zip code, e.g. 5000",
|
description: "Zip code, e.g. 5000",
|
||||||
}),
|
}),
|
||||||
region: option(z.string().optional(), {
|
region: option(z.string().optional(), {
|
||||||
description: "Region name",
|
description: "Landsdel from locations (category region), e.g. Sjælland, Østjylland",
|
||||||
|
}),
|
||||||
|
"administrative-region": option(z.string().optional(), {
|
||||||
|
description: "Administrative region from locations (category administrativeRegion), e.g. Region Midtjylland",
|
||||||
}),
|
}),
|
||||||
"job-type": option(z.string().optional(), {
|
"job-type": option(z.string().optional(), {
|
||||||
description: "Comma-separated job types: fuldtid,deltid,fleksjob,elev,studiejob,praktik",
|
description: "Comma-separated job types: fuldtid,deltid,fleksjob,elev,studiejob,praktik",
|
||||||
@@ -143,6 +146,12 @@ export const search = defineCommand({
|
|||||||
if (flags.region) {
|
if (flags.region) {
|
||||||
filters.push({ type: "region", value: flags.region, displayText: flags.region })
|
filters.push({ type: "region", value: flags.region, displayText: flags.region })
|
||||||
}
|
}
|
||||||
|
// `locations` returns two kinds of region, and the API filters each by its
|
||||||
|
// own type: a "region" filter holding "Region Midtjylland" matches no job.
|
||||||
|
if (flags["administrative-region"]) {
|
||||||
|
const value = flags["administrative-region"]
|
||||||
|
filters.push({ type: "administrativeRegion", value, displayText: value })
|
||||||
|
}
|
||||||
|
|
||||||
const jobTypes = flags["job-type"]
|
const jobTypes = flags["job-type"]
|
||||||
? flags["job-type"].split(",").map((t) => t.trim()).filter(Boolean)
|
? flags["job-type"].split(",").map((t) => t.trim()).filter(Boolean)
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { expect, test } from "bun:test";
|
||||||
|
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
// Both region kinds exactly as GET /api/search/locations?q=jylland returns them.
|
||||||
|
const LOCATIONS = [
|
||||||
|
{
|
||||||
|
title: "Landsdel",
|
||||||
|
items: [{ id: "region__218546", text: "Østjylland", value: "Østjylland", category: "region", slug: "oestjylland" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Region",
|
||||||
|
items: [{
|
||||||
|
id: "administrativeRegion__389101",
|
||||||
|
text: "Region Midtjylland",
|
||||||
|
value: "Region Midtjylland",
|
||||||
|
category: "administrativeRegion",
|
||||||
|
slug: "region-midtjylland",
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function searchFilters(args: string[]) {
|
||||||
|
const bodies: unknown[] = [];
|
||||||
|
const server = Bun.serve({
|
||||||
|
hostname: "127.0.0.1",
|
||||||
|
port: 0,
|
||||||
|
async fetch(request) {
|
||||||
|
bodies.push(await request.json());
|
||||||
|
return Response.json({ items: [], currentPage: 1, totalItems: 0, itemsPrPage: 30, totalPages: 0 });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const dir = mkdtempSync(join(tmpdir(), "jobdanmark-region-"));
|
||||||
|
const preload = join(dir, "fetch.ts");
|
||||||
|
writeFileSync(preload, `
|
||||||
|
const originalFetch = globalThis.fetch;
|
||||||
|
globalThis.fetch = (input, init) => {
|
||||||
|
const url = new URL(input instanceof Request ? input.url : String(input));
|
||||||
|
if (url.protocol === "data:") return originalFetch(input, init);
|
||||||
|
if (url.origin !== "https://jobdanmark.dk" || url.pathname !== "/api/jobsearch/search/1") {
|
||||||
|
throw new Error("Unexpected search fixture request");
|
||||||
|
}
|
||||||
|
return originalFetch(${JSON.stringify(server.url.origin)} + url.pathname, init);
|
||||||
|
};
|
||||||
|
`);
|
||||||
|
try {
|
||||||
|
const proc = Bun.spawn([
|
||||||
|
process.execPath, "run", "--preload", preload,
|
||||||
|
join(import.meta.dir, "../src/cli.ts"), "search", ...args,
|
||||||
|
], { stdout: "pipe", stderr: "pipe" });
|
||||||
|
const [stderr, exitCode] = await Promise.all([new Response(proc.stderr).text(), proc.exited]);
|
||||||
|
expect(stderr).toBe("");
|
||||||
|
expect(exitCode).toBe(0);
|
||||||
|
expect(bodies).toHaveLength(1);
|
||||||
|
return (bodies[0] as { filters: Array<{ type: string; value: string; displayText: string }> }).filters;
|
||||||
|
} finally {
|
||||||
|
server.stop(true);
|
||||||
|
rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = (category: string) => LOCATIONS.flatMap((g) => g.items).find((i) => i.category === category)!;
|
||||||
|
|
||||||
|
test("an administrativeRegion location is sent with the API's administrativeRegion filter type", async () => {
|
||||||
|
const { value } = item("administrativeRegion");
|
||||||
|
const filters = await searchFilters(["--administrative-region", value]);
|
||||||
|
expect(filters).toEqual([{ type: "administrativeRegion", value, displayText: value }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("a region (landsdel) location keeps the region filter type", async () => {
|
||||||
|
const { value } = item("region");
|
||||||
|
const filters = await searchFilters(["--region", value]);
|
||||||
|
expect(filters).toEqual([{ type: "region", value, displayText: value }]);
|
||||||
|
});
|
||||||
@@ -146,6 +146,16 @@ per-file diff commands.
|
|||||||
API's `occupations` parameter returns the 830 nurse ads for the same UUID. The new flag sends that
|
API's `occupations` parameter returns the 830 nurse ads for the same UUID. The new flag sends that
|
||||||
parameter and accepts the full URI or the bare UUID; the docs stop pointing at the numeric flags.
|
parameter and accepts the full URI or the bare UUID; the docs stop pointing at the numeric flags.
|
||||||
|
|
||||||
|
- **`jobdanmark-search search` can filter by the administrative regions that `locations` returns**
|
||||||
|
(#650, `.agents/skills/jobdanmark-search/cli/src/commands/search.ts`, `tests/region-filter-cli.test.ts`,
|
||||||
|
`SKILL.md`, `cli/README.md`) - `locations` lists two kinds of region: landsdele such as `Østjylland`
|
||||||
|
(category `region`) and the five administrative regions such as `Region Midtjylland` (category
|
||||||
|
`administrativeRegion`), and the search API filters each by its own type. `--region` always sent
|
||||||
|
type `region`, so an administrative region - and SKILL.md's own example `--region Midtjylland` - came
|
||||||
|
back as a successful search with zero jobs. A new `--administrative-region` flag sends the
|
||||||
|
`administrativeRegion` filter (2,486 jobs for `Region Midtjylland`), and the docs map each `locations`
|
||||||
|
category to its flag and give `--region` examples that exist.
|
||||||
|
|
||||||
## [1.7.3] - 2026-10-19
|
## [1.7.3] - 2026-10-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
Reference in new issue
Block a user