commit 8ddfe680e0616922897f400d413e656e8e8b211c Author: eulaly Date: Mon May 18 14:14:33 2026 -0400 initial commit diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..0dc3946 --- /dev/null +++ b/readme.md @@ -0,0 +1,93 @@ +build a small python cli tool called `usajobs.py` for exploring usajobs results. + +goal: +- query the official usajobs api +- apply strict local filters +- show results in a readable terminal table +- let me select rows to export into an org-mode file + +stack: +- python 3.11+ +- click for cli args +- requests for api +- rich for table + row selection prompt +- pathlib/json/csv stdlib only otherwise + +env vars: +- USAJOBS_EMAIL +- USAJOBS_KEY + +basic command: +`python jobs.py search --location "Washington, DC" --radius 25 --salary-min 150 --grade-min 15 --grade-max 15 --series 2210 --series 0340 --clearance 3 --clearance 4` + +option behavior: +- --radius 25 means 25 miles +- --salary-min 150 means $150,000 +- --grade-min/--grade-max filter locally against low/high grade +- --series may repeat; pass to api as semicolon list +- --clearance may repeat; pass to api as semicolon list +- --pay-plan may repeat, default gs and gg +- --limit defaults 100 +- --out defaults jobs.org +- --cache-dir defaults .cache/usajobs + +search behavior: +1. call https://data.usajobs.gov/api/search using official headers: + host: data.usajobs.gov + user-agent: $USAJOBS_EMAIL + authorization-key: $USAJOBS_KEY +2. request fields=full, resultsperpage=500, sortfield=opendate, sortdirection=desc +3. cache raw json response per query/page under .cache/usajobs +4. apply local filters after fetching: + - pay plan in allowed pay plans + - low_grade >= grade_min + - high_grade <= grade_max + - salary max >= salary_min, or salary min >= salary_min if max absent + - location string contains/near requested location as available +5. output table with columns: + idx, title, agency, grade, salary, location, close date, clearance match, url +6. selection/export: +- after displaying table, allow user to arrow/highlight and mark for export + - pick sensible defaults, eg x or m for mark, u for unmark, a for all, e for export +- export selected jobs to new file with short slug name + datetime stamp +- org output format: +``` +** [[url][link]] +:properties: +:agency: +:grade: - +:close_date: +:end: + +salary: +location: +travel: +clearance: + +*** posting + +``` +7. (stretch) Cache each query, allow arrow/scroll through them like a cli, recall, or save filters. could be too much. + +implementation notes: +- write clean functional code: + - build_params() + - fetch_page() + - fetch_all() + - normalize_job() + - passes_filters() + - render_table() + - parse_selection() + - export_org() +- normalize both official api shape and frontend-ish shape if present: + - api jobs may use MatchedObjectDescriptor + - details may be under UserArea.Details +- raw posting text should combine title, summary, duties, requirements, qualifications, evaluations, other info, key requirements. +- shortened job title should be max 80 chars, strip all-caps screaming where reasonable, preserve meaning. +- include helpful errors if env vars missing. +- include a --offline flag that only reads cached json and does not call api. +- include a --debug flag that prints api params and counts before/after filtering. + +acceptance test: +- running the command with `--salary-min 150 --grade-min 15 --grade-max 15 --radius 25` should not show gs/gg-13 jobs after local filtering. +- selecting `none` exits without writing.