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.
73 lines
2.1 KiB
Go
73 lines
2.1 KiB
Go
// Package skiprules provides the "skip-rules" command and its subcommands,
|
|
// which check a skip rules file and apply it to the opportunities already
|
|
// stored (see internal/skiprules).
|
|
package skiprules
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"git.avsdev.uk/Go/job-search/internal/cli"
|
|
"git.avsdev.uk/Go/job-search/internal/config"
|
|
"git.avsdev.uk/Go/job-search/internal/skiprules"
|
|
)
|
|
|
|
// NewCommand returns the "skip-rules" command, with "check" and "apply".
|
|
func NewCommand(opts *cli.Options) *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "skip-rules",
|
|
Short: "Check skip rules, or apply them to stored opportunities",
|
|
Long: `Skip rules mark new opportunities Skipped as the sources fetch
|
|
them. They are read from sources.skip_rules_file, and read again when it
|
|
changes.
|
|
|
|
"check" reports whether a rules file is valid, and "apply" marks the stored
|
|
opportunities that match Skipped. Both read sources.skip_rules_file unless
|
|
given another file.`,
|
|
Args: cobra.NoArgs,
|
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
|
slog.Debug("command persistent pre-run", "command", "skip-rules")
|
|
},
|
|
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
|
slog.Debug("command persistent post-run", "command", "skip-rules")
|
|
},
|
|
}
|
|
cmd.AddCommand(commandCheck(opts))
|
|
cmd.AddCommand(commandApply(opts))
|
|
|
|
return cmd
|
|
}
|
|
|
|
// loadRules returns the rules in the file named by args, if given, or else
|
|
// in sources.skip_rules_file, and the file's path.
|
|
func loadRules(
|
|
conf *config.JobSearchConfig, args []string,
|
|
) (*skiprules.Rules, string, error) {
|
|
path := conf.Sources.SkipRulesFile
|
|
if len(args) > 0 {
|
|
path = args[0]
|
|
}
|
|
if path == "" {
|
|
return nil, "", errors.New(
|
|
"no rules file given, and sources.skip_rules_file is not set",
|
|
)
|
|
}
|
|
|
|
rules, err := skiprules.Load(path)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
return rules, path, nil
|
|
}
|
|
|
|
// describeRules returns a summary of rules for output, e.g. "3 filters (2
|
|
// active)".
|
|
func describeRules(rules *skiprules.Rules) string {
|
|
filters, active := rules.Filters()
|
|
return fmt.Sprintf("%d filters (%d active)", filters, active)
|
|
}
|