diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 566bd9c0a..5e77d207f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: # Lint / autoformat: Python code - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: "v0.8.6" + rev: "v0.9.1" hooks: # Run the linter - id: ruff @@ -21,7 +21,7 @@ repos: # Deps: ensure Python uv lockfile is up to date - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.5.14 + rev: 0.5.18 hooks: - id: uv-lock files: src/backend/pyproject.toml diff --git a/src/backend/app/config.py b/src/backend/app/config.py index f9f3f66fd..5b2e6500b 100644 --- a/src/backend/app/config.py +++ b/src/backend/app/config.py @@ -97,7 +97,7 @@ def otel_python_excluded_urls(self) -> Optional[str]: # NOTE we add ODK Central session auth endpoint here if self.ODK_CENTRAL_URL: os.environ["OTEL_PYTHON_REQUESTS_EXCLUDED_URLS"] = ( - f"{endpoints}" f"{self.ODK_CENTRAL_URL}/v1/sessions" + f"{endpoints}{self.ODK_CENTRAL_URL}/v1/sessions" ) return endpoints @@ -315,7 +315,7 @@ def get_settings(): if _settings.DEBUG: # Enable detailed Python async debugger os.environ["PYTHONASYNCIODEBUG"] = "1" - print("Loaded settings: " f"{_settings.model_dump()}") + print(f"Loaded settings: {_settings.model_dump()}") return _settings diff --git a/src/backend/app/db/models.py b/src/backend/app/db/models.py index e748ea9b1..1d7ee7176 100644 --- a/src/backend/app/db/models.py +++ b/src/backend/app/db/models.py @@ -295,7 +295,7 @@ async def create( ({columns}) VALUES ({value_placeholders}) - {conflict_statement if ignore_conflict else ''} + {conflict_statement if ignore_conflict else ""} RETURNING *; """ @@ -416,7 +416,7 @@ async def create( ({columns}) VALUES ({value_placeholders}) - {'ON CONFLICT ("name") DO NOTHING' if ignore_conflict else ''} + {'ON CONFLICT ("name") DO NOTHING' if ignore_conflict else ""} RETURNING *; """ @@ -495,7 +495,7 @@ async def update( placeholders = [f"{key} = %({key})s" for key in model_dump.keys()] sql = f""" UPDATE organisations - SET {', '.join(placeholders)} + SET {", ".join(placeholders)} WHERE id = %(org_id)s RETURNING *; """ @@ -939,9 +939,7 @@ async def create( for index, feature in enumerate(features): feature_index = f"geom_{index}" values.append( - "(%(project_id)s," - f"{index + 1}," - f"ST_GeomFromGeoJSON(%({feature_index})s))" + f"(%(project_id)s,{index + 1},ST_GeomFromGeoJSON(%({feature_index})s))" ) # Must be string json for db input data[feature_index] = json.dumps(feature["geometry"]) @@ -1234,7 +1232,7 @@ async def all( tasks t ON p.id = t.project_id LEFT JOIN task_events ON p.id = task_events.project_id - {'WHERE ' + ' AND '.join(filters) if filters else ''} + {"WHERE " + " AND ".join(filters) if filters else ""} GROUP BY p.id, project_org.id ORDER BY @@ -1335,7 +1333,7 @@ async def update( sql = f""" UPDATE projects - SET {', '.join(placeholders)} + SET {", ".join(placeholders)} WHERE id = %(project_id)s RETURNING *, @@ -1567,7 +1565,7 @@ async def update( placeholders = [f"{key} = %({key})s" for key in model_dump.keys()] sql = f""" UPDATE background_tasks - SET {', '.join(placeholders)} + SET {", ".join(placeholders)} WHERE id = %(task_id)s RETURNING *; """ @@ -1728,7 +1726,7 @@ async def update( placeholders = [f"{key} = %({key})s" for key in model_dump.keys()] sql = f""" UPDATE basemaps - SET {', '.join(placeholders)} + SET {", ".join(placeholders)} WHERE id = %(basemap_id)s RETURNING *; """ diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index 6586c4b42..c9d3d7234 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -575,7 +575,7 @@ async def validate_form( return StreamingResponse( updated_form, media_type=( - "application/vnd.openxmlformats-" "officedocument.spreadsheetml.sheet" + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ), headers={"Content-Disposition": f"attachment; filename={xform_id}.xlsx"}, ) diff --git a/src/backend/app/s3.py b/src/backend/app/s3.py index 80028fb03..a520577c4 100644 --- a/src/backend/app/s3.py +++ b/src/backend/app/s3.py @@ -304,8 +304,7 @@ def is_connection_secure(minio_url: str): else: err = ( - "The S3_ENDPOINT is set incorrectly. " - "It must start with http:// or https://" + "The S3_ENDPOINT is set incorrectly. It must start with http:// or https://" ) log.error(err) raise ValueError(err) diff --git a/src/backend/app/submissions/submission_schemas.py b/src/backend/app/submissions/submission_schemas.py index 5ab8e068d..91a9f2fec 100644 --- a/src/backend/app/submissions/submission_schemas.py +++ b/src/backend/app/submissions/submission_schemas.py @@ -94,6 +94,6 @@ def get_last_active(self, last_active: Optional[AwareDatetime]) -> str: elif days_difference == 1: return "yesterday" elif days_difference < 7: - return f'{days_difference} day{"s" if days_difference > 1 else ""} ago' + return f"{days_difference} day{'s' if days_difference > 1 else ''} ago" else: return last_active.strftime("%d %b %Y") diff --git a/src/backend/tests/test_projects_routes.py b/src/backend/tests/test_projects_routes.py index 2fc5e4ffa..97e9425bb 100644 --- a/src/backend/tests/test_projects_routes.py +++ b/src/backend/tests/test_projects_routes.py @@ -415,7 +415,7 @@ async def test_update_project(client, admin_user, project): assert sorted(response_data["hashtags"]) == sorted( [ "#FMTM", - f"#{settings.FMTM_DOMAIN}-{response_data["id"]}", + f"#{settings.FMTM_DOMAIN}-{response_data['id']}", "#anothertag", ] ) diff --git a/src/backend/tests/test_submission_routes.py b/src/backend/tests/test_submission_routes.py index 4fd1c2e2c..4e19663d9 100644 --- a/src/backend/tests/test_submission_routes.py +++ b/src/backend/tests/test_submission_routes.py @@ -36,9 +36,9 @@ async def test_read_submissions(client, submission): first_submission = submission_list[0] test_instance_id = submission_data.instanceId assert first_submission["__id"] == test_instance_id, "Instance ID mismatch" - assert ( - first_submission["meta"]["instanceID"] == test_instance_id - ), "Meta instanceID mismatch" + assert first_submission["meta"]["instanceID"] == test_instance_id, ( + "Meta instanceID mismatch" + ) assert first_submission["__system"]["submitterId"] == str( submission_data.submitterId ), "Submitter ID mismatch" @@ -56,17 +56,17 @@ async def test_download_submission_json(client, submission): ) assert response.status_code == 200, ( - f"Failed to download JSON submissions. " f"Response: {response.text}" + f"Failed to download JSON submissions. Response: {response.text}" + ) + assert "Content-Disposition" in response.headers, ( + "Missing Content-Disposition header" ) - assert ( - "Content-Disposition" in response.headers - ), "Missing Content-Disposition header" expected_filename = f"{odk_project.slug}_submissions.json" - assert response.headers["Content-Disposition"].endswith( - expected_filename - ), f"Expected file name to end with {expected_filename}" + assert response.headers["Content-Disposition"].endswith(expected_filename), ( + f"Expected file name to end with {expected_filename}" + ) submissions = response.json() assert isinstance(submissions, dict), "Expected JSON response to be a dictionary" @@ -87,17 +87,17 @@ async def test_download_submission_file(client, submission): ) assert response.status_code == 200, ( - f"Failed to download submissions as file. " f"Response: {response.text}" + f"Failed to download submissions as file. Response: {response.text}" + ) + assert "Content-Disposition" in response.headers, ( + "Missing Content-Disposition header" ) - assert ( - "Content-Disposition" in response.headers - ), "Missing Content-Disposition header" expected_filename = f"{odk_project.slug}.zip" - assert response.headers["Content-Disposition"].endswith( - expected_filename - ), f"Expected file name to end with {expected_filename}" + assert response.headers["Content-Disposition"].endswith(expected_filename), ( + f"Expected file name to end with {expected_filename}" + ) assert len(response.content) > 0, "Expected non-empty ZIP file content" @@ -108,14 +108,14 @@ async def test_get_submission_count(client, submission): response = await client.get( f"/submission/get-submission-count?project_id={odk_project.id}" ) - assert ( - response.status_code == 200 - ), f"Failed to fetch submission count. Response: {response.text}" + assert response.status_code == 200, ( + f"Failed to fetch submission count. Response: {response.text}" + ) submission_count = response.json() - assert isinstance( - submission_count, int - ), "Expected submission count to be an integer" + assert isinstance(submission_count, int), ( + "Expected submission count to be an integer" + ) assert submission_count > 0, "Submission count should be greater than zero" @@ -127,33 +127,33 @@ async def test_download_submission_geojson(client, submission): f"/submission/download-submission-geojson?project_id={odk_project.id}" ) - assert ( - response.status_code == 200 - ), f"Failed to download GeoJSON submissions. Response: {response.text}" + assert response.status_code == 200, ( + f"Failed to download GeoJSON submissions. Response: {response.text}" + ) - assert ( - "Content-Disposition" in response.headers - ), "Missing Content-Disposition header" + assert "Content-Disposition" in response.headers, ( + "Missing Content-Disposition header" + ) expected_filename = f"{odk_project.slug}.geojson" - assert response.headers["Content-Disposition"].endswith( - expected_filename - ), f"Expected file name to end with {expected_filename}" + assert response.headers["Content-Disposition"].endswith(expected_filename), ( + f"Expected file name to end with {expected_filename}" + ) submission_geojson = json.loads(response.content) - assert isinstance( - submission_geojson, dict - ), "Expected GeoJSON content to be a dictionary" + assert isinstance(submission_geojson, dict), ( + "Expected GeoJSON content to be a dictionary" + ) assert "type" in submission_geojson, "Missing 'type' key in GeoJSON" - assert ( - submission_geojson["type"] == "FeatureCollection" - ), "GeoJSON type must be 'FeatureCollection'" + assert submission_geojson["type"] == "FeatureCollection", ( + "GeoJSON type must be 'FeatureCollection'" + ) assert "features" in submission_geojson, "Missing 'features' key in GeoJSON" - assert isinstance( - submission_geojson["features"], list - ), "Expected 'features' to be a list" - assert ( - len(submission_geojson["features"]) > 0 - ), "Expected at least one feature in 'features'" + assert isinstance(submission_geojson["features"], list), ( + "Expected 'features' to be a list" + ) + assert len(submission_geojson["features"]) > 0, ( + "Expected at least one feature in 'features'" + ) if __name__ == "__main__":