diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 82b4d092..5c18a271 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: 3.11 + python-version: 3.13 architecture: x64 - name: Ensure Nexus mirror configuration is not committed diff --git a/app/main.py b/app/main.py index 94550d43..2b4e6505 100644 --- a/app/main.py +++ b/app/main.py @@ -186,6 +186,6 @@ async def http_exception(request: Request, exc: StarletteHTTPException): @app.exception_handler(InvalidPayloadError) async def invalid_payload_exception(request: Request, exc: InvalidPayloadError): return JSONResponse( - status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, content=jsonable_encoder(exc.response()), ) diff --git a/app/model/schema.py b/app/model/schema.py index 2f546db7..ce7137c2 100644 --- a/app/model/schema.py +++ b/app/model/schema.py @@ -92,9 +92,9 @@ class ResponseType(StrEnum): class LineError(BaseModel): - line: Union[int, str] = Field(..., example="5") - group: str = Field(..., example="TRAN") - desc: str = Field(..., example="Blah blah") + line: Union[int, str] = Field(..., json_schema_extra={'example': "5"}) + group: str = Field(..., json_schema_extra={'example': "TRAN"}) + desc: str = Field(..., json_schema_extra={'example': "Blah blah"}) @field_validator("line") @classmethod @@ -105,18 +105,18 @@ def line_if_string_must_be_hyphen(cls, line): class Validation(BaseModel): - filename: str = Field(..., example="example.ags") - filesize: int = Field(None, example="1024") - checkers: List[str] = Field(None, example=["python_ags4 v1.1.0"]) - dictionary: str = Field(None, example="Standard_dictionary_v4_1_1.ags") - time: datetime = Field(None, example="2021-08-18 09:23:29") - message: str = Field(None, example="7 error(s) found in file!") - errors: Dict[str, List[LineError]] = Field(..., example="Rule 1a") - valid: bool = Field(..., example="false") + filename: str = Field(..., json_schema_extra={'example': "example.ags"}) + filesize: int = Field(None, json_schema_extra={'example': "1024"}) + checkers: List[str] = Field(None, json_schema_extra={'example': ["python_ags4 v1.1.0"]}) + dictionary: str = Field(None, json_schema_extra={'example': "Standard_dictionary_v4_1_1.ags"}) + time: datetime = Field(None, json_schema_extra={'example': "2021-08-18 09:23:29"}) + message: str = Field(None, json_schema_extra={'example': "7 error(s}) found in file!"}) + errors: Dict[str, List[LineError]] = Field(..., json_schema_extra={'example': "Rule 1a"}) + valid: bool = Field(..., json_schema_extra={'example': "false"}) additional_metadata: dict = Field(...) - error_count: int = Field(None, example="50") - warnings_count: int = Field(None, example="0") - fyi_count: int = Field(None, example="5") + error_count: int = Field(None, json_schema_extra={'example': "50"}) + warnings_count: int = Field(None, json_schema_extra={'example': "0"}) + fyi_count: int = Field(None, json_schema_extra={'example': "5"}) geojson: dict = dict() geojson_error: str | None = None @@ -129,15 +129,15 @@ def errors_keys_must_be_known_rules(cls, errors): class Error(BaseModel): - error: str = Field(..., example="error") - propName: str = Field(None, example="error") - desc: str = Field(..., example="Error message") + error: str = Field(..., json_schema_extra={'example': "error"}) + propName: str = Field(None, json_schema_extra={'example': "error"}) + desc: str = Field(..., json_schema_extra={'example': "Error message"}) class MinimalResponse(BaseModel): - msg: str = Field(..., example="Example response") - type: str = Field(..., example="success") - self: str = Field(..., example="http://example.com/apis/query") + msg: str = Field(..., json_schema_extra={'example': "Example response"}) + type: str = Field(..., json_schema_extra={'example': "success"}) + self: str = Field(..., json_schema_extra={'example': "http://example.com/apis/query"}) class ErrorResponse(MinimalResponse): @@ -149,4 +149,4 @@ class ValidationResponse(MinimalResponse): class BoreholeCountResponse(MinimalResponse): - count: int = Field(..., example=4) + count: int = Field(..., json_schema_extra={'example': 4}) diff --git a/app/routes/errors.py b/app/routes/errors.py index 4f417074..7fd786f5 100644 --- a/app/routes/errors.py +++ b/app/routes/errors.py @@ -8,7 +8,7 @@ error_responses = { status.HTTP_400_BAD_REQUEST: {"model": ErrorResponse}, status.HTTP_404_NOT_FOUND: {"model": ErrorResponse}, - status.HTTP_422_UNPROCESSABLE_ENTITY: {"model": ErrorResponse}, + status.HTTP_422_UNPROCESSABLE_CONTENT: {"model": ErrorResponse}, status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ErrorResponse}, } diff --git a/requirements.txt b/requirements.txt index 864b1227..d6fff9d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,21 +2,27 @@ # This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --output-file=requirements.txt requirements.in +# pip-compile requirements.in # -aiofiles==24.1.0 +aiofiles==25.1.0 # via -r requirements.in +annotated-doc==0.0.4 + # via + # fastapi + # typer annotated-types==0.7.0 # via pydantic -anyio==4.9.0 +anyio==4.14.0 # via # httpx # starlette # watchfiles -branca==0.8.1 +beautifulsoup4==4.15.0 + # via libpysal +branca==0.8.2 # via folium -certifi==2025.7.14 +certifi==2026.6.17 # via # httpcore # httpx @@ -24,15 +30,14 @@ certifi==2025.7.14 # pyproj # requests # sentry-sdk -charset-normalizer==3.4.2 +charset-normalizer==3.4.7 # via requests -click==8.2.1 +click==8.4.1 # via # python-ags4 # rich-toolkit - # typer # uvicorn -colorlog==6.9.0 +colorlog==6.10.1 # via -r requirements.in contourpy==1.3.3 # via matplotlib @@ -40,35 +45,44 @@ cycler==0.12.1 # via matplotlib defusedxml==0.7.1 # via python-ags4 -dnspython==2.7.0 +detect-installer==0.1.0 + # via fastapi-cloud-cli +dnspython==2.8.0 # via email-validator -email-validator==2.2.0 +email-validator==2.3.0 # via # fastapi # pydantic et-xmlfile==2.0.0 # via openpyxl -fastapi[standard]==0.116.1 +fastapi[standard]==0.138.0 # via -r requirements.in -fastapi-cli[standard]==0.0.8 +fastapi-cli[standard]==0.0.27 # via fastapi -fastapi-cloud-cli==0.1.4 +fastapi-cloud-cli==0.20.0 # via fastapi-cli +fastar==0.11.0 + # via + # fastapi + # fastapi-cloud-cli folium==0.20.0 # via geopandas -fonttools==4.59.0 +fonttools==4.63.0 # via matplotlib -geoalchemy2==0.18.0 +geoalchemy2==0.20.0 # via geopandas -geographiclib==2.0 +geographiclib==2.1 # via geopy -geojson-pydantic==2.0.0 - # via -r requirements.in -geopandas[all]==1.1.1 +geojson-pydantic==2.1.1 # via -r requirements.in +geopandas[all]==1.1.3 + # via + # -r requirements.in + # libpysal + # pointpats geopy==2.4.1 # via geopandas -greenlet==3.2.3 +greenlet==3.5.2 # via sqlalchemy h11==0.16.0 # via @@ -76,13 +90,13 @@ h11==0.16.0 # uvicorn httpcore==1.0.9 # via httpx -httptools==0.6.4 +httptools==0.8.0 # via uvicorn httpx==0.28.1 # via # fastapi # fastapi-cloud-cli -idna==3.10 +idna==3.18 # via # anyio # email-validator @@ -94,70 +108,92 @@ jinja2==3.1.6 # branca # fastapi # folium -joblib==1.5.1 + # libpysal +joblib==1.5.3 # via scikit-learn -kiwisolver==1.4.8 +kiwisolver==1.5.0 # via matplotlib +libpysal==4.14.1 + # via pointpats mapclassify==2.10.0 # via geopandas -markdown-it-py==3.0.0 +markdown-it-py==4.2.0 # via rich -markupsafe==3.0.2 +markupsafe==3.0.3 # via jinja2 -matplotlib==3.10.3 - # via geopandas +matplotlib==3.11.0 + # via + # geopandas + # pointpats mdurl==0.1.2 # via markdown-it-py -networkx==3.5 +narwhals==2.22.1 + # via scikit-learn +networkx==3.6.1 # via mapclassify -numpy==2.3.2 +numpy==2.5.0 # via # -r requirements.in # contourpy # folium # geopandas + # libpysal # mapclassify # matplotlib # pandas + # pointpats # pyogrio # scikit-learn # scipy # shapely openpyxl==3.1.5 # via python-ags4 -packaging==25.0 +packaging==26.2 # via # geoalchemy2 # geopandas + # libpysal # matplotlib # pyogrio -pandas==2.3.1 +pandas==2.3.3 # via # geopandas + # libpysal # mapclassify + # pointpats # python-ags4 -pillow==11.3.0 +pillow==12.2.0 # via matplotlib -psycopg[binary]==3.2.9 +platformdirs==4.10.0 + # via libpysal +pointpats==2.6.0 # via geopandas -psycopg-binary==3.2.9 +psycopg[binary]==3.3.4 + # via geopandas +psycopg-binary==3.3.4 # via psycopg -pyarrow==21.0.0 +pyarrow==24.0.0 # via geopandas -pydantic[email]==2.11.7 +pydantic[email]==2.13.4 # via # fastapi # fastapi-cloud-cli # geojson-pydantic -pydantic-core==2.33.2 + # pydantic-extra-types + # pydantic-settings +pydantic-core==2.46.4 # via pydantic -pygments==2.19.2 +pydantic-extra-types==2.11.1 + # via fastapi +pydantic-settings==2.14.2 + # via fastapi +pygments==2.20.0 # via rich -pyogrio==0.11.0 +pyogrio==0.12.1 # via geopandas -pyparsing==3.2.3 +pyparsing==3.3.2 # via matplotlib -pyproj==3.7.1 +pyproj==3.7.2 # via geopandas python-ags4==1.1.0 # via -r requirements.in @@ -165,91 +201,104 @@ python-dateutil==2.9.0.post0 # via # matplotlib # pandas -python-dotenv==1.1.1 - # via uvicorn -python-multipart==0.0.20 +python-dotenv==1.2.2 + # via + # pydantic-settings + # uvicorn +python-multipart==0.0.32 # via fastapi -pytz==2025.2 +pytz==2026.2 # via pandas -pyyaml==6.0.2 +pyyaml==6.0.3 # via uvicorn -requests==2.32.4 +requests==2.34.2 # via # -r requirements.in # folium + # libpysal rich==13.9.4 # via # python-ags4 # rich-toolkit # typer -rich-toolkit==0.14.8 +rich-toolkit==0.20.1 # via # fastapi-cli # fastapi-cloud-cli -rignore==0.6.4 +rignore==0.7.6 # via fastapi-cloud-cli -scikit-learn==1.7.1 - # via mapclassify -scipy==1.16.1 +scikit-learn==1.9.0 + # via + # libpysal + # mapclassify +scipy==1.18.0 # via # geopandas + # libpysal # mapclassify + # pointpats # scikit-learn -sentry-sdk==2.33.2 +sentry-sdk==2.63.0 # via fastapi-cloud-cli -shapely==2.1.1 +shapely==2.1.2 # via # -r requirements.in # geopandas + # libpysal + # pointpats shellingham==1.5.4 # via typer shortuuid==1.0.13 # via -r requirements.in six==1.17.0 # via python-dateutil -sniffio==1.3.1 - # via anyio -sqlalchemy==2.0.41 +soupsieve==2.8.4 + # via beautifulsoup4 +sqlalchemy==2.0.51 # via # geoalchemy2 # geopandas -starlette==0.47.2 +starlette==1.3.1 # via fastapi threadpoolctl==3.6.0 # via scikit-learn -typer==0.16.0 +typer==0.26.7 # via # fastapi-cli # fastapi-cloud-cli -typing-extensions==4.14.1 +typing-extensions==4.15.0 # via + # beautifulsoup4 # fastapi # pydantic # pydantic-core + # pydantic-extra-types # rich-toolkit # sqlalchemy - # typer # typing-inspection -typing-inspection==0.4.1 - # via pydantic -tzdata==2025.2 +typing-inspection==0.4.2 + # via + # fastapi + # pydantic + # pydantic-settings +tzdata==2026.2 # via pandas -urllib3==2.5.0 +urllib3==2.7.0 # via # requests # sentry-sdk -uvicorn[standard]==0.35.0 +uvicorn[standard]==0.49.0 # via # fastapi # fastapi-cli # fastapi-cloud-cli -uvloop==0.21.0 +uvloop==0.22.1 # via uvicorn -watchfiles==1.1.0 +watchfiles==1.2.0 # via uvicorn -websockets==15.0.1 +websockets==16.0 # via uvicorn -xyzservices==2025.4.0 +xyzservices==2026.3.0 # via # folium # geopandas diff --git a/requirements_dev.txt b/requirements_dev.txt index 8f86aaa8..14241538 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -2,29 +2,33 @@ # This file is autogenerated by pip-compile with Python 3.13 # by the following command: # -# pip-compile --output-file=requirements_dev.txt requirements_dev.in +# pip-compile requirements_dev.in # -anyio==4.9.0 +anyio==4.14.0 # via httpx -asttokens==3.0.0 +asttokens==3.0.1 # via stack-data -certifi==2025.7.14 +build==1.5.0 + # via pip-tools +certifi==2026.6.17 # via # httpcore # httpx # requests -charset-normalizer==3.4.2 +charset-normalizer==3.4.7 # via requests -decorator==5.2.1 +click==8.4.1 + # via pip-tools +decorator==5.3.1 # via # ipdb # ipython -executing==2.2.0 +executing==2.2.1 # via stack-data flake8==7.3.0 # via -r requirements_dev.in -freezegun==1.5.3 +freezegun==1.5.5 # via -r requirements_dev.in h11==0.16.0 # via httpcore @@ -32,36 +36,43 @@ httpcore==1.0.9 # via httpx httpx==0.28.1 # via -r requirements_dev.in -idna==3.10 +idna==3.18 # via # anyio # httpx # requests -iniconfig==2.1.0 +iniconfig==2.3.0 # via pytest ipdb==0.13.13 # via -r requirements_dev.in -ipython==9.4.0 +ipython==9.14.1 # via # -r requirements_dev.in # ipdb ipython-pygments-lexers==1.1.1 # via ipython -jedi==0.19.2 +jedi==0.20.0 # via ipython -matplotlib-inline==0.1.7 +matplotlib-inline==0.2.2 # via ipython mccabe==0.7.0 # via flake8 -packaging==25.0 - # via pytest -parso==0.8.4 +packaging==26.2 + # via + # build + # pytest + # wheel +parso==0.8.7 # via jedi pexpect==4.9.0 # via ipython +pip-tools==7.5.3 + # via -r requirements_dev.in pluggy==1.6.0 # via pytest -prompt-toolkit==3.0.51 +prompt-toolkit==3.0.52 + # via ipython +psutil==7.2.2 # via ipython ptyprocess==0.7.0 # via pexpect @@ -71,10 +82,14 @@ pycodestyle==2.14.0 # via flake8 pyflakes==3.4.0 # via flake8 -pygments==2.19.2 +pygments==2.20.0 # via # ipython # ipython-pygments-lexers +pyproject-hooks==1.2.0 + # via + # build + # pip-tools pytest==8.0.2 # via # -r requirements_dev.in @@ -83,7 +98,7 @@ pytest-asyncio==0.23.8 # via -r requirements_dev.in python-dateutil==2.9.0.post0 # via freezegun -requests==2.32.4 +requests==2.34.2 # via # -r requirements_dev.in # requests-toolbelt @@ -91,15 +106,19 @@ requests-toolbelt==1.0.0 # via -r requirements_dev.in six==1.17.0 # via python-dateutil -sniffio==1.3.1 - # via anyio stack-data==0.6.3 # via ipython -traitlets==5.14.3 +traitlets==5.15.1 # via # ipython # matplotlib-inline -urllib3==2.5.0 +urllib3==2.7.0 # via requests -wcwidth==0.2.13 +wcwidth==0.8.1 # via prompt-toolkit +wheel==0.47.0 + # via pip-tools + +# The following packages are considered to be unsafe in a requirements file: +# pip +# setuptools