Files
bwilliams 0543762330 Added skip rules, which mark new opportunities Skipped when they're fetched
sources.skip_rules_file names a JSON file of filters, each with a
comment, an active flag, a logic mode ("or" by default, or "and") and
rules by buyer, title words or CPV codes. The fetcher applies them
before saving, so only new opportunities are marked; the marks of
stored ones are never touched. CPV rules match when the opportunity's
codes are all in the list, or any of them with exclusive false, and
by prefix with exact false.

The file is read again when its time or size changes: an invalid edit
is logged and the last good rules kept, and a deleted file means no
rules until it's back. fetcher service and serve refuse to start if it
is define but missing or invalid.

job-search skip-rules check validates a rules file, and skip-rules
apply marks the stored opportunities that match Skipped, or only lists
them with --dry-run.
2026-10-20 08:55:19 +01:00

94 lines
3.8 KiB
Markdown

# Skip rules
Skip rules mark new opportunities **Skipped** as the sources fetch them, so
opportunities that are never of interest don't need skipping by hand. The
rules file is named by `sources.skip_rules_file`, and used by
`fetcher service` and `job-search serve`.
- Only **new** opportunities are skipped: the store keeps the marks of an
opportunity it already has, so a new release never changes them. To apply
rules to the opportunities already stored, run
`job-search skip-rules apply` (`--dry-run` lists the matches only).
- The file is checked before each save and read again when it has changed.
An invalid edit is logged as an error and the last good rules are kept; a
deleted file is logged as a warning and nothing is skipped until it is
back. At startup, a missing or invalid file stops the command.
- `job-search skip-rules check [file]` reports whether a file is valid.
## Format
The file is a JSON list of filters. An opportunity is skipped when **any**
active filter matches it.
```json
[
{
"comment": "Buyer X's IT work",
"active": true,
"logic_mode": "and",
"rules": [
{ "rule_type": "buyer", "values": ["GB-FTS-12345"] },
{ "rule_type": "cpv", "values": ["72000000", "48000000"], "exact": false }
]
},
{
"comment": "Road repairs",
"active": true,
"rules": [
{ "rule_type": "title", "values": ["road", "repairs"] }
]
},
{
"comment": "Agriculture only (turned off)",
"active": false,
"rules": [
{ "rule_type": "cpv", "values": ["03000000"] },
{ "rule_type": "cpv", "values": ["03100000", "03200000"] }
]
},
{
"comment": "Anything with a medical equipment code",
"active": true,
"rules": [
{ "rule_type": "cpv", "values": ["33100000"], "exact": false, "exclusive": false }
]
}
]
```
### Filters
| Field | Required | Meaning |
|---|---|---|
| `comment` | yes | What the filter is for; logged with each opportunity it skips. May be `""` |
| `active` | yes | `false` turns the filter off |
| `rules` | yes | One or more rules |
| `logic_mode` | no | `"or"` (default): any rule matching is enough. `"and"`: every rule must match |
### Rules
| Field | Required | Meaning |
|---|---|---|
| `rule_type` | yes | `"buyer"`, `"title"` or `"cpv"` |
| `values` | yes | One or more strings; see below |
| `exact` | no | CPV rules only; default `true`. See below |
| `exclusive` | no | CPV rules only; default `true`. See below |
| `rule_type` | Matches when |
|---|---|
| `buyer` | The buyer's ID is any one of `values` |
| `title` | Every word in `values` is a word of the title. Matching ignores case and is by whole words (`road` doesn't match "Railroad"); anything but letters and digits separates words, and a value with a space in it is several words |
| `cpv` | The opportunity has CPV codes, and **every** one of them is in `values`: they are a subset. `["C", "D", "E"]` matches an opportunity with only C and D, but not one with C and F. For "A or B or C+D+E", use three rules (or three filters). With `"exclusive": false`, **any** one of them being in `values` is enough: `["C"]` matches an opportunity with C and F |
CPV codes are 8 digits; check digits (`72000000-5`) are ignored on both
sides. With `"exact": false`, a value's trailing zeros are dropped and the
rest matched as a prefix, keeping at least the first two digits (the
division): `72000000` covers every `72…` code, `72200000` every `722…`, and
`70000000` every `70…` but not `71…`.
Anything else is refused when the file is read, with the filter and rule
named by their place in the file: an unknown field, a missing required
field, an empty `rules` or `values`, an unknown `rule_type` or `logic_mode`,
`exact` or `exclusive` on a rule that isn't a CPV rule, a title value with
no words, or a CPV value that isn't 8 digits.