From 2e89a5afb586c27d3d25048951408406936d3b8f Mon Sep 17 00:00:00 2001 From: Patrick Young Date: Mon, 6 Oct 2025 16:35:22 -0400 Subject: [PATCH 1/7] docs(fix): reflect current project in readme --- README.md | 83 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 7bd69ff..b883286 100644 --- a/README.md +++ b/README.md @@ -7,21 +7,30 @@ [![Code Style][codestyle-img]][codestyle-lnk] [![Coverage Status][codecov-img]][codecov-lnk] -A Python wrapper for the [USAJOBS REST API](https://developer.usajobs.gov/). The library aims to provide a simple interface for discovering and querying job postings from USAJOBS using Python. +`python-usajobsapi` is a typed Python wrapper for the [USAJOBS REST API](https://developer.usajobs.gov/). The project provides a clean interface for discovering and querying job postings using Python. -## Features +## Status -- Lightweight client for the USAJOBS REST API endpoints -- Leverage type hinting and validation for endpoint parameters -- Map endpoint results to Python objects +This project is under active development and its API may change. Changes to the [USAJOBS REST API documentation](https://developer.usajobs.gov/) are monitored and incorporated on a best-effort basis. Feedback and ideas are appreciated. + +## Overview + +The USAJOBS REST API exposes a large catalog of job opportunity announcements (JOAs) with a complex query surface. This project focuses on providing: + +- Declarative endpoint definitions. +- Strongly typed request/query parameters and response models. +- Streaming helpers for paginating through large result sets. +- Normalization of data formats (e.g., date handling, booleans, payload serialization). ### Supported Endpoints -This package primarily aims to support searching and retrieval of active and past job listings. However, updates are planned to add support for all other documented endpoints. +This package primarily aims to support searching and retrieval of active and past job listings. Coverage of all [documented endpoints](https://developer.usajobs.gov/api-reference/) will continue to be expanded. Currently, the following endpoints are supported: - [Job Search API](https://developer.usajobs.gov/api-reference/get-api-search) (`/api/Search`) +- [Historic JOA API](https://developer.usajobs.gov/api-reference/get-api-historicjoa) (`/api/HistoricJoa`) +- Planned in [#6](https://github.com/paddy74/python-usajobsapi/issues/6) - [Announcement Text API](https://developer.usajobs.gov/api-reference/get-api-joa) (`/api/HistoricJoa/AnnouncementText`) ## Installation @@ -45,37 +54,75 @@ cd python-usajobsapi pip install . ``` -## Usage +## Quickstart -Register for a USAJOBS API key and set a valid User-Agent before making requests. +1. [Request USAJOBS API credentials](https://developer.usajobs.gov/APIRequest/Forms/DeveloperSignup) (Job Search API only). +2. Instatiate the client (`USAJobsClient`) with your `User-Agent` (email) and API key. +3. Perform a search: ```python from usajobsapi import USAJobsClient client = USAJobsClient(auth_user="name@example.com", auth_key="YOUR_API_KEY") -results = client.search_jobs(keyword="data scientist", location_names=["Atlanta", "Georgia"]).search_result.jobs() -for job in results: +response = client.search_jobs(keyword="data scientist", location_names=["Atlanta", "Georgia"]) + +for job in response.jobs(): print(job.position_title) ``` +### Pagination + +### Handling pagination + +Use streaming helpers to to iterate through multiple pages or individual result items without needing to worry about pagination: + +```python +for job in client.search_jobs_items(keyword="cybersecurity", results_per_page=100): + if "Remote" in (job.position_location_display or ""): + print(job.position_title, job.organization_name) +``` + +## Developer Guide + +Set up a development environment with [`uv`](https://docs.astral.sh/uv/): + +```bash +uv sync --all-extras --dev +uv run pytest tests +uv run ruff check +uv run ruff format +``` + +### Key Development Principles + +- Keep Pydantic models exhaustive and prefer descriptive field metadata so that auto-generated docs remain informative. +- Maintain 100% passing tests, at least 80% test coverage, formatting, and linting before opening a pull request. +- Update docstrings alongside code changes to keep the generated reference accurate. + ## Contributing Contributions are welcome! To get started: 1. Fork the repository and create a new branch. -2. Create a virtual environment and install development dependencies. -3. Run the test suite with `pytest` and ensure all tests pass. -4. Submit a pull request describing your changes. +2. Install development dependencies (see the [developer guide](#developer-guide)). +3. Add or update tests together with your change. +4. Run the full test, linting, and formatting suite locally. +5. Submit a pull request describing your changes and referencing any relevant issues. -Please open an issue first for major changes to discuss your proposal. +For major changes, open an issue first to discuss your proposal. -## License +## Design -Distributed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html). See [LICENSE](LICENSE) for details. +The software design architecture prioritizes composability and strong typing, ensuring that it is straightforward to add/update endpoints and generate documentation from docstrings. -## Project Status +- **Client session management**: `USAJobsClient` wraps a configurable `requests.Session` to reuse connections and centralize authentication headers. +- **Declarative endpoints**: Each USAJOBS endpoint is expressed as a Pydantic model with nested `Params` and `Response` classes, providing validation, serialization helpers, and rich metadata for documentation. +- **Pagination helpers**: Iterators (`search_jobs_pages` and `search_jobs_items`) encapsulate pagination logic and expose idiomatic Python iterators so users focus on data consumption, not page math. +- **Shared utilities**: Shared utilities handle API-specific normalization (e.g., date parsing, alias mapping) so endpoint models stay declarative and thin. -This project is under active development and its API may change. Changes to the [USAJOBS REST API documentation](https://developer.usajobs.gov/) shall be monitored and incorporated into this project in a reasonable amount of time. Feedback and ideas are appreciated. +## License + +Distributed under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html). See [LICENSE](LICENSE) for details. ## Contact From ff48d54fb1ceb67e93b9376e9c767481e7e8f059 Mon Sep 17 00:00:00 2001 From: Patrick Young Date: Tue, 7 Oct 2025 18:04:55 -0400 Subject: [PATCH 2/7] docs: add top-level docstrings --- README.md | 13 ++++++++++++- usajobsapi/client.py | 8 +++++++- usajobsapi/endpoints/__init__.py | 6 +++++- usajobsapi/endpoints/announcementtext.py | 6 +++++- usajobsapi/endpoints/historicjoa.py | 10 +++++++++- usajobsapi/endpoints/search.py | 10 +++++++++- usajobsapi/utils.py | 6 +++++- 7 files changed, 52 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b883286..0633899 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ for job in client.search_jobs_items(keyword="cybersecurity", results_per_page=10 Set up a development environment with [`uv`](https://docs.astral.sh/uv/): ```bash -uv sync --all-extras --dev +uv sync --all-extras --all-groups uv run pytest tests uv run ruff check uv run ruff format @@ -99,6 +99,17 @@ uv run ruff format - Maintain 100% passing tests, at least 80% test coverage, formatting, and linting before opening a pull request. - Update docstrings alongside code changes to keep the generated reference accurate. +### Document Generation + +Documentation is generated using [MkDocs](https://www.mkdocs.org/). The technical reference surfaces the reStructuredText style docstrings from the package's source code. + +```bash +# Run the development server +uv run mkdocs serve -f mkdocs/mkdocs.yaml +# Build the static site +uv run mkdocs build -f mkdocs/mkdocs.yaml +``` + ## Contributing Contributions are welcome! To get started: diff --git a/usajobsapi/client.py b/usajobsapi/client.py index 7f87907..927f732 100644 --- a/usajobsapi/client.py +++ b/usajobsapi/client.py @@ -1,4 +1,10 @@ -"""Wrapper for the USAJOBS REST API.""" +""" +Wrapper for the USAJOBS REST API. + +Use the high-level client to manage authentication headers and make strongly typed requests to individual endpoints. The models documented below are auto-generated from the runtime code so every parameter, default, and helper stays in sync with the library. + +To execute a query, pair the [`USAJobsClient`](#usajobsapi.client.USAJobsClient) with any of the endpoint payloads (query parameters) under [usajobsapi.endpoints](#usajobsapi.endpoints) such as [`SearchEndpoint.Params`](#usajobsapi.endpoints.search.SearchEndpoint). +""" from collections.abc import Iterator from typing import Dict, Optional diff --git a/usajobsapi/endpoints/__init__.py b/usajobsapi/endpoints/__init__.py index eb20b25..f9ef57f 100644 --- a/usajobsapi/endpoints/__init__.py +++ b/usajobsapi/endpoints/__init__.py @@ -1,4 +1,8 @@ -"""Wrapper for USAJOBS REST API endpoints.""" +""" +Wrapper for USAJOBS REST API endpoints. + +Each endpoint exposes declarative `Params` and `Response` models so you can validate queries and parse responses without hand-written schemas. +""" from .announcementtext import AnnouncementTextEndpoint from .historicjoa import HistoricJoaEndpoint diff --git a/usajobsapi/endpoints/announcementtext.py b/usajobsapi/endpoints/announcementtext.py index 88d982b..a981dab 100644 --- a/usajobsapi/endpoints/announcementtext.py +++ b/usajobsapi/endpoints/announcementtext.py @@ -1,4 +1,8 @@ -"""Wrapper for the Announcement Text API.""" +""" +Wrapper for the Announcement Text API. + +Fetch the rendered job announcement text for a single job opportunity announcement. Pair this endpoint with a control number discovered from a [`SearchEndpoint.JOAItem`](#usajobsapi.endpoints.search.SearchEndpoint.JOAItem) to pull the full HTML description. +""" from typing import Dict diff --git a/usajobsapi/endpoints/historicjoa.py b/usajobsapi/endpoints/historicjoa.py index 60ac8e3..058cd5b 100644 --- a/usajobsapi/endpoints/historicjoa.py +++ b/usajobsapi/endpoints/historicjoa.py @@ -1,4 +1,12 @@ -"""Wrapper for the Historic JOAs API.""" +""" +Wrapper for the Historic JOAs API. + +Access archived job opportunity announcements with date filters, control numbers, and hiring organization metadata. + +- When you capture a control number from [`SearchEndpoint.JOAItem.id`](#usajobsapi.endpoints.search.SearchEndpoint.JOAItem.id), feed it into [`HistoricJoaEndpoint.Params.usajobs_control_numbers`](#usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Params.usajobs_control_numbers) to retrieve historical records for the same posting. +- Date filters such as [`HistoricJoaEndpoint.Params.start_position_open_date`](#usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Params.start_position_open_date) normalize strings via [`_normalize_date`](#usajobsapi.utils._normalize_date) and are reflected back in [`Item.position_open_date`](#usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Item.position_open_date). +- Boolean indicators use the shared [`_normalize_yn_bool`](#usajobsapi.utils._normalize_yn_bool). +""" import datetime as dt from typing import Dict, List, Optional diff --git a/usajobsapi/endpoints/search.py b/usajobsapi/endpoints/search.py index 966cabb..8aa01c2 100644 --- a/usajobsapi/endpoints/search.py +++ b/usajobsapi/endpoints/search.py @@ -1,4 +1,12 @@ -"""Wrapper for the Job Search API.""" +""" +Wrapper for the Job Search API. + +The search endpoint wraps the core USAJOBS Job Search API. Enumerations describe allowed query values, the nested `Params` model validates input, and the response graph mirrors the payload returned from the API. + +- Filter inputs such as [`Params.hiring_path`](usajobsapi.endpoints.search.SearchEndpoint.Params.hiring_path) and [`Params.pay_grade_high`](usajobsapi.endpoints.search.SearchEndpoint.Params.pay_grade_high) are surfaced in [`Response.params`](usajobsapi.endpoints.search.SearchEndpoint.Response.params) so you can inspect what was sent to USAJOBS. +- Each [`JOAItem`](usajobsapi.endpoints.search.SearchEndpoint.JOAItem) contains a [`JOADescriptor`](usajobsapi.endpoints.search.SearchEndpoint.JOADescriptor) with rich metadata like [`PositionRemuneration`](usajobsapi.endpoints.search.SearchEndpoint.PositionRemuneration) that aligns with the `SearchEndpoint.Params` salary filters. +- `Response.jobs()` returns the flattened list of [`JOAItem`](usajobsapi.endpoints.search.SearchEndpoint.JOAItem) instances to iterate over the same objects delivered in [`SearchResult.items`](usajobsapi.endpoints.search.SearchEndpoint.SearchResult.items). +""" from __future__ import annotations diff --git a/usajobsapi/utils.py b/usajobsapi/utils.py index 908f3c8..5bdecd4 100644 --- a/usajobsapi/utils.py +++ b/usajobsapi/utils.py @@ -1,4 +1,8 @@ -"""Helper utility functions.""" +""" +Helper utility functions. + +Shared helpers keep endpoint payloads consistent and ergonomic. These utilities handle normalization, serialization, and data validation used throughout the client and endpoint models. +""" import datetime as dt from enum import Enum From 7b1e9748a33f9b800ea348712600c99fa38d0d6e Mon Sep 17 00:00:00 2001 From: Patrick Young Date: Tue, 7 Oct 2025 18:06:55 -0400 Subject: [PATCH 3/7] chore(docs): move mkdocs pkgs to 'docs' group --- .github/actions/python-init/action.yaml | 2 +- README.md | 2 ++ pyproject.toml | 10 +++--- uv.lock | 44 ++++++++++++++----------- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/.github/actions/python-init/action.yaml b/.github/actions/python-init/action.yaml index e3d4ce7..1f4ae53 100644 --- a/.github/actions/python-init/action.yaml +++ b/.github/actions/python-init/action.yaml @@ -14,5 +14,5 @@ runs: enable-cache: true - name: Install the project - run: uv sync --locked --all-extras --dev + run: uv sync --locked --all-extras --all-groups shell: bash diff --git a/README.md b/README.md index 0633899..0e8ad09 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,8 @@ uv run ruff format Documentation is generated using [MkDocs](https://www.mkdocs.org/). The technical reference surfaces the reStructuredText style docstrings from the package's source code. ```bash +uv sync --group docs + # Run the development server uv run mkdocs serve -f mkdocs/mkdocs.yaml # Build the static site diff --git a/pyproject.toml b/pyproject.toml index 5f80a82..cde4e60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,16 +27,18 @@ packages = ["usajobsapi"] [dependency-groups] dev = [ + "pre-commit>=4.3.0", + "pytest>=8.4.1", + "pytest-cov>=7.0.0", + "ruff>=0.12.11", +] +docs = [ "mkdocs-external-files", "mkdocs-gen-files>=0.5.0", "mkdocs-literate-nav>=0.6.2", "mkdocs-section-index>=0.3.10", "mkdocs>=1.6.1", "mkdocstrings[python]>=0.30.1", - "pre-commit>=4.3.0", - "pytest>=8.4.1", - "pytest-cov>=7.0.0", - "ruff>=0.12.11", ] [tool.semantic_release] diff --git a/uv.lock b/uv.lock index 68a0f06..a6e9420 100644 --- a/uv.lock +++ b/uv.lock @@ -644,18 +644,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, ] -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - [[package]] name = "pytest-cov" version = "7.0.0" @@ -670,6 +658,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "python-usajobsapi" source = { editable = "." } @@ -685,16 +685,18 @@ build = [ [package.dev-dependencies] dev = [ + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] +docs = [ { name = "mkdocs" }, { name = "mkdocs-external-files" }, { name = "mkdocs-gen-files" }, { name = "mkdocs-literate-nav" }, { name = "mkdocs-section-index" }, { name = "mkdocstrings", extra = ["python"] }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-cov" }, - { name = "ruff" }, ] [package.metadata] @@ -707,16 +709,18 @@ provides-extras = ["build"] [package.metadata.requires-dev] dev = [ + { name = "pre-commit", specifier = ">=4.3.0" }, + { name = "pytest", specifier = ">=8.4.1" }, + { name = "pytest-cov", specifier = ">=7.0.0" }, + { name = "ruff", specifier = ">=0.12.11" }, +] +docs = [ { name = "mkdocs", specifier = ">=1.6.1" }, { name = "mkdocs-external-files", directory = "mkdocs/mkdocs_external_files" }, { name = "mkdocs-gen-files", specifier = ">=0.5.0" }, { name = "mkdocs-literate-nav", specifier = ">=0.6.2" }, { name = "mkdocs-section-index", specifier = ">=0.3.10" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=0.30.1" }, - { name = "pre-commit", specifier = ">=4.3.0" }, - { name = "pytest", specifier = ">=8.4.1" }, - { name = "pytest-cov", specifier = ">=7.0.0" }, - { name = "ruff", specifier = ">=0.12.11" }, ] [[package]] From 963b39b323a22ea70dac3452a1a0590d4397d4c8 Mon Sep 17 00:00:00 2001 From: Patrick Young Date: Tue, 7 Oct 2025 19:18:32 -0400 Subject: [PATCH 4/7] docs(chore): format docstrings to sphinx complinat --- mkdocs/gen_ref_pages.py | 8 +++----- usajobsapi/client.py | 8 ++++---- usajobsapi/endpoints/announcementtext.py | 4 +++- usajobsapi/endpoints/historicjoa.py | 8 ++++---- usajobsapi/endpoints/search.py | 10 ++++++---- usajobsapi/utils.py | 16 ++++++++-------- 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/mkdocs/gen_ref_pages.py b/mkdocs/gen_ref_pages.py index ae993cf..8326cb9 100644 --- a/mkdocs/gen_ref_pages.py +++ b/mkdocs/gen_ref_pages.py @@ -11,15 +11,13 @@ def gen_ref_pages(root_dir: Path, source_dir: Path, output_dir: str | Path) -> None: """Emit mkdocstrings-compatible reference pages and navigation entries. - :param root_dir: _description_ + :param root_dir: Project root directory used to resolve edit links :type root_dir: Path - :param source_dir: _description_ + :param source_dir: Directory containing the Python packages to document :type source_dir: Path :param output_dir: Output directory for the generated files; must be relative (non-escaping) to the docs directory. :type output_dir: str | Path - :raises ValueError: _description_ - :raises ValueError: _description_ - :raises ValueError: _description_ + :raises ValueError: If `output_dir` is absolute, escapes the docs directory, or no Python modules are found """ # output_dir must be a relative, non-escaping path diff --git a/usajobsapi/client.py b/usajobsapi/client.py index 927f732..f1a01e7 100644 --- a/usajobsapi/client.py +++ b/usajobsapi/client.py @@ -3,7 +3,7 @@ Use the high-level client to manage authentication headers and make strongly typed requests to individual endpoints. The models documented below are auto-generated from the runtime code so every parameter, default, and helper stays in sync with the library. -To execute a query, pair the [`USAJobsClient`](#usajobsapi.client.USAJobsClient) with any of the endpoint payloads (query parameters) under [usajobsapi.endpoints](#usajobsapi.endpoints) such as [`SearchEndpoint.Params`](#usajobsapi.endpoints.search.SearchEndpoint). +To execute a query, pair the `USAJobsClient` with any of the endpoint payload models provided under `usajobsapi.endpoints`, such as `SearchEndpoint.Params`. """ from collections.abc import Iterator @@ -42,7 +42,7 @@ def __init__( :type auth_user: str | None, optional :param auth_key: API key used for the Job Search API, defaults to None :type auth_key: str | None, optional - :param session: _description_, defaults to None + :param session: Session to reuse for HTTP connections, defaults to None :type session: requests.Session | None, optional """ self._url = url @@ -105,7 +105,7 @@ def _request( def announcement_text(self, **kwargs) -> AnnouncementTextEndpoint.Response: """Query the Announcement Text API. - :return: _description_ + :return: Deserialized announcement text response :rtype: AnnouncementTextEndpoint.Response """ params = AnnouncementTextEndpoint.Params(**kwargs) @@ -203,7 +203,7 @@ def search_jobs_items(self, **kwargs) -> Iterator[SearchEndpoint.JOAItem]: def historic_joa(self, **kwargs) -> HistoricJoaEndpoint.Response: """Query the Historic JOAs API. - :return: _description_ + :return: Deserialized historic job announcement response :rtype: HistoricJoaEndpoint.Response """ params = HistoricJoaEndpoint.Params(**kwargs) diff --git a/usajobsapi/endpoints/announcementtext.py b/usajobsapi/endpoints/announcementtext.py index a981dab..3587f12 100644 --- a/usajobsapi/endpoints/announcementtext.py +++ b/usajobsapi/endpoints/announcementtext.py @@ -1,7 +1,9 @@ """ Wrapper for the Announcement Text API. -Fetch the rendered job announcement text for a single job opportunity announcement. Pair this endpoint with a control number discovered from a [`SearchEndpoint.JOAItem`](#usajobsapi.endpoints.search.SearchEndpoint.JOAItem) to pull the full HTML description. +Fetch the rendered job announcement text for a single job opportunity announcement. + +Pair this endpoint with a control number discovered from `SearchEndpoint.JOAItem` to pull the full HTML description. """ from typing import Dict diff --git a/usajobsapi/endpoints/historicjoa.py b/usajobsapi/endpoints/historicjoa.py index 058cd5b..7057239 100644 --- a/usajobsapi/endpoints/historicjoa.py +++ b/usajobsapi/endpoints/historicjoa.py @@ -1,11 +1,11 @@ """ Wrapper for the Historic JOAs API. -Access archived job opportunity announcements with date filters, control numbers, and hiring organization metadata. +Access archived job opportunity announcements with date filters, control numbers, and hiring-organization metadata. -- When you capture a control number from [`SearchEndpoint.JOAItem.id`](#usajobsapi.endpoints.search.SearchEndpoint.JOAItem.id), feed it into [`HistoricJoaEndpoint.Params.usajobs_control_numbers`](#usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Params.usajobs_control_numbers) to retrieve historical records for the same posting. -- Date filters such as [`HistoricJoaEndpoint.Params.start_position_open_date`](#usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Params.start_position_open_date) normalize strings via [`_normalize_date`](#usajobsapi.utils._normalize_date) and are reflected back in [`Item.position_open_date`](#usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Item.position_open_date). -- Boolean indicators use the shared [`_normalize_yn_bool`](#usajobsapi.utils._normalize_yn_bool). +- Feed a control number captured from `SearchEndpoint.JOAItem.id` into `HistoricJoaEndpoint.Params.usajobs_control_numbers` to retrieve historical records for the same posting. +- Date filters such as `HistoricJoaEndpoint.Params.start_position_open_date` normalize strings via :func:`usajobsapi.utils._normalize_date` and are reflected back in `Item.position_open_date`. +- Boolean indicators rely on `usajobsapi.utils._normalize_yn_bool`. """ import datetime as dt diff --git a/usajobsapi/endpoints/search.py b/usajobsapi/endpoints/search.py index 8aa01c2..47afe8e 100644 --- a/usajobsapi/endpoints/search.py +++ b/usajobsapi/endpoints/search.py @@ -1,11 +1,13 @@ """ Wrapper for the Job Search API. -The search endpoint wraps the core USAJOBS Job Search API. Enumerations describe allowed query values, the nested `Params` model validates input, and the response graph mirrors the payload returned from the API. +The search endpoint wraps the core USAJOBS Job Search API. Enumerations describe allowed +query values, the nested `Params` model validates input, and the response graph mirrors +the payload returned from the API. -- Filter inputs such as [`Params.hiring_path`](usajobsapi.endpoints.search.SearchEndpoint.Params.hiring_path) and [`Params.pay_grade_high`](usajobsapi.endpoints.search.SearchEndpoint.Params.pay_grade_high) are surfaced in [`Response.params`](usajobsapi.endpoints.search.SearchEndpoint.Response.params) so you can inspect what was sent to USAJOBS. -- Each [`JOAItem`](usajobsapi.endpoints.search.SearchEndpoint.JOAItem) contains a [`JOADescriptor`](usajobsapi.endpoints.search.SearchEndpoint.JOADescriptor) with rich metadata like [`PositionRemuneration`](usajobsapi.endpoints.search.SearchEndpoint.PositionRemuneration) that aligns with the `SearchEndpoint.Params` salary filters. -- `Response.jobs()` returns the flattened list of [`JOAItem`](usajobsapi.endpoints.search.SearchEndpoint.JOAItem) instances to iterate over the same objects delivered in [`SearchResult.items`](usajobsapi.endpoints.search.SearchEndpoint.SearchResult.items). +- Filter inputs such as `Params.hiring_path` and `Params.pay_grade_high` are surfaced in `Response.params` so you can inspect what was sent to USAJOBS. +- Each `JOAItem` contains a `JOADescriptor` with rich metadata (for example, `PositionRemuneration`) that aligns with the `Params` salary filters. +- :meth:`SearchEndpoint.Response.jobs` returns the flattened list of `JOAItem` instances that correspond to `SearchResult.items` in the raw payload. """ from __future__ import annotations diff --git a/usajobsapi/utils.py b/usajobsapi/utils.py index 5bdecd4..874c13d 100644 --- a/usajobsapi/utils.py +++ b/usajobsapi/utils.py @@ -31,7 +31,7 @@ def _normalize_date(value: None | dt.datetime | dt.date | str) -> Optional[dt.da def _normalize_yn_bool(value: None | bool | str) -> Optional[bool]: - """Normalize "Y"/"N" to `bool`.""" + """Normalize `"Y"`/`"N"` to `bool`.""" if value is None: return None @@ -84,9 +84,9 @@ def _normalize_param(value: Any) -> Optional[str]: def _dump_by_alias(model: BaseModel) -> Dict[str, str]: """Dump a Pydantic model to a query-param dict using the model's field aliases and USAJOBS formatting rules (lists + bools). - :param model: _description_ + :param model: Pydantic model instance to export using field aliases :type model: BaseModel - :return: _description_ + :return: Mapping of alias names to normalized parameter values :rtype: Dict[str, str] """ # Use the API's wire names and drop `None`s @@ -106,13 +106,13 @@ def _is_inrange(n: int | float, lower: int | float, upper: int | float): A closed interval [a, b] represents the set of all real numbers greater or equal to a and less or equal to b. - :param n: _description_ + :param n: Value to check :type n: int - :param lower: _description_ + :param lower: Lower bound of the interval :type lower: int - :param upper: _description_ + :param upper: Upper bound of the interval :type upper: int - :return: _description_ - :rtype: _type_ + :return: `True` if the value falls inside the closed interval `[lower, upper]` + :rtype: bool """ return n >= lower and n <= upper From 5746767d66165f5d40ece6ace9f0ca593e19d402 Mon Sep 17 00:00:00 2001 From: Patrick Young Date: Tue, 7 Oct 2025 19:19:03 -0400 Subject: [PATCH 5/7] docs(fix): mkdocstrings expects sphinx style docstrings --- mkdocs/mkdocs.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mkdocs/mkdocs.yaml b/mkdocs/mkdocs.yaml index 71925a0..5d68cb8 100644 --- a/mkdocs/mkdocs.yaml +++ b/mkdocs/mkdocs.yaml @@ -27,7 +27,11 @@ plugins: - literate-nav: nav_file: NAV_REF.md - section-index - - mkdocstrings + - mkdocstrings: + handlers: + python: + selection: + docstring_style: sphinx - external-files: files: - src: ../README.md From 970016c9f5ac026a9632fdb426a53f807edc6362 Mon Sep 17 00:00:00 2001 From: Patrick Young Date: Thu, 9 Oct 2025 14:42:44 -0400 Subject: [PATCH 6/7] docs(fix): correct mkdocstrings object links --- usajobsapi/client.py | 2 +- usajobsapi/endpoints/announcementtext.py | 2 +- usajobsapi/endpoints/historicjoa.py | 6 +++--- usajobsapi/endpoints/search.py | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/usajobsapi/client.py b/usajobsapi/client.py index f1a01e7..9711673 100644 --- a/usajobsapi/client.py +++ b/usajobsapi/client.py @@ -3,7 +3,7 @@ Use the high-level client to manage authentication headers and make strongly typed requests to individual endpoints. The models documented below are auto-generated from the runtime code so every parameter, default, and helper stays in sync with the library. -To execute a query, pair the `USAJobsClient` with any of the endpoint payload models provided under `usajobsapi.endpoints`, such as `SearchEndpoint.Params`. +To execute a query, pair the [`USAJobsClient`][usajobsapi.client.USAJobsClient] with any of the endpoint payload models provided in the [`endpoints` module][usajobsapi.endpoints], such as [`SearchEndpoint.Params`][usajobsapi.endpoints.search.SearchEndpoint.Params]. """ from collections.abc import Iterator diff --git a/usajobsapi/endpoints/announcementtext.py b/usajobsapi/endpoints/announcementtext.py index 3587f12..5f6efb1 100644 --- a/usajobsapi/endpoints/announcementtext.py +++ b/usajobsapi/endpoints/announcementtext.py @@ -3,7 +3,7 @@ Fetch the rendered job announcement text for a single job opportunity announcement. -Pair this endpoint with a control number discovered from `SearchEndpoint.JOAItem` to pull the full HTML description. +Pair this endpoint with a control number discovered from [`SearchEndpoint.JOAItem`][usajobsapi.endpoints.search.SearchEndpoint.JOAItem] to pull the full HTML description. """ from typing import Dict diff --git a/usajobsapi/endpoints/historicjoa.py b/usajobsapi/endpoints/historicjoa.py index 7057239..5c4519e 100644 --- a/usajobsapi/endpoints/historicjoa.py +++ b/usajobsapi/endpoints/historicjoa.py @@ -3,9 +3,9 @@ Access archived job opportunity announcements with date filters, control numbers, and hiring-organization metadata. -- Feed a control number captured from `SearchEndpoint.JOAItem.id` into `HistoricJoaEndpoint.Params.usajobs_control_numbers` to retrieve historical records for the same posting. -- Date filters such as `HistoricJoaEndpoint.Params.start_position_open_date` normalize strings via :func:`usajobsapi.utils._normalize_date` and are reflected back in `Item.position_open_date`. -- Boolean indicators rely on `usajobsapi.utils._normalize_yn_bool`. +- Feed a control number captured from a [search result item's `id`][usajobsapi.endpoints.search.SearchEndpoint.JOAItem] into [`usajobs_control_numbers`][usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Params] to retrieve historical records for the same posting. +- Date filters such as [`start_position_open_date`][usajobsapi.endpoints.historicjoa.HistoricJoaEndpoint.Params] normalize strings as `datetime.date` objects and are reflected back in a response's `position_open_date`. +- Boolean indicators rely on normalization validators to handle the API's inconsistent input/output formats for booleans. """ import datetime as dt diff --git a/usajobsapi/endpoints/search.py b/usajobsapi/endpoints/search.py index 47afe8e..27352a4 100644 --- a/usajobsapi/endpoints/search.py +++ b/usajobsapi/endpoints/search.py @@ -2,12 +2,12 @@ Wrapper for the Job Search API. The search endpoint wraps the core USAJOBS Job Search API. Enumerations describe allowed -query values, the nested `Params` model validates input, and the response graph mirrors +query values, the nested [`Params`][usajobsapi.endpoints.search.SearchEndpoint.Params] model validates input, and the response graph mirrors the payload returned from the API. -- Filter inputs such as `Params.hiring_path` and `Params.pay_grade_high` are surfaced in `Response.params` so you can inspect what was sent to USAJOBS. -- Each `JOAItem` contains a `JOADescriptor` with rich metadata (for example, `PositionRemuneration`) that aligns with the `Params` salary filters. -- :meth:`SearchEndpoint.Response.jobs` returns the flattened list of `JOAItem` instances that correspond to `SearchResult.items` in the raw payload. +- Filter inputs such as [`hiring_path`][usajobsapi.endpoints.search.SearchEndpoint.Params] and [`pay_grade_high`][usajobsapi.endpoints.search.SearchEndpoint.Params] are surfaced in a [search result's `params` field][usajobsapi.endpoints.search.SearchEndpoint.Response] so you can inspect what was sent to USAJOBS. +- Each [`JOAItem`][usajobsapi.endpoints.search.SearchEndpoint.JOAItem] contains a [`JOADescriptor`][usajobsapi.endpoints.search.SearchEndpoint.JOADescriptor] with rich metadata (for example, [`PositionRemuneration`][usajobsapi.endpoints.search.SearchEndpoint.PositionRemuneration]) that aligns with the salary filter parameters. +- [`SearchEndpoint.Response.jobs`][usajobsapi.endpoints.search.SearchEndpoint.Response.jobs] returns the flattened list of [`JOAItem`][usajobsapi.endpoints.search.SearchEndpoint.JOAItem] instances that correspond to the [`items`][usajobsapi.endpoints.search.SearchEndpoint.SearchResult] in the response payload. """ from __future__ import annotations @@ -387,7 +387,7 @@ def _radius_requires_location(self) -> "SearchEndpoint.Params": def _check_min_le_max( cls, v: Optional[int], info: ValidationInfo ) -> Optional[int]: - """Validate that renumeration max is >= renumeration min.""" + """Validate that remuneration max is >= remuneration min.""" mn = info.data.get("remuneration_min") if v is not None and mn is not None and v < mn: raise ValueError( From 0d715cd361509c3427b35299ff6f928a388fe10d Mon Sep 17 00:00:00 2001 From: Patrick Young Date: Thu, 9 Oct 2025 14:54:02 -0400 Subject: [PATCH 7/7] docs(fix): fix missing mkdocs about/ pages --- mkdocs/docs/.gitkeep | 0 mkdocs/docs/index.md | 17 ----------------- mkdocs/mkdocs.yaml | 4 +++- 3 files changed, 3 insertions(+), 18 deletions(-) create mode 100644 mkdocs/docs/.gitkeep delete mode 100644 mkdocs/docs/index.md diff --git a/mkdocs/docs/.gitkeep b/mkdocs/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/mkdocs/docs/index.md b/mkdocs/docs/index.md deleted file mode 100644 index 327a4ed..0000000 --- a/mkdocs/docs/index.md +++ /dev/null @@ -1,17 +0,0 @@ -# Welcome to MkDocs - -For full documentation visit [mkdocs.org](https://www.mkdocs.org). - -## Commands - -- `mkdocs new [dir-name]` - Create a new project. -- `mkdocs serve` - Start the live-reloading docs server. -- `mkdocs build` - Build the documentation site. -- `mkdocs -h` - Print help message and exit. - -## Project layout - - mkdocs.yml # The configuration file. - docs/ - index.md # The documentation homepage. - ... # Other markdown pages, images and other files. diff --git a/mkdocs/mkdocs.yaml b/mkdocs/mkdocs.yaml index 5d68cb8..ea8cba9 100644 --- a/mkdocs/mkdocs.yaml +++ b/mkdocs/mkdocs.yaml @@ -18,6 +18,8 @@ nav: - About: - Changelog: about/changelog.md - License: about/license.md + - Code of Conduct: about/conduct.md + - Security Policy: about/security.md plugins: - search @@ -35,7 +37,7 @@ plugins: - external-files: files: - src: ../README.md - dest: README.md + dest: index.md - src: ../CHANGELOG.md dest: about/changelog.md - src: ../LICENSE