Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions spp_api_v2_gis/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ OpenSPP GIS API
!! source digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
Expand Down Expand Up @@ -144,10 +144,6 @@ Dependencies
- ``spp_gis_report`` - Report configuration
- ``spp_area`` - Administrative area data

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.

**Table of contents**

.. contents::
Expand All @@ -156,6 +152,16 @@ Dependencies
Changelog
=========

19.0.2.0.1
~~~~~~~~~~

- Promoted to Beta
- fix(schemas): Add GeoJSON geometry validation for spatial queries and
geofences
- fix(routers): Use proper exception chaining (from e) for better
debugging
- feat: Add SPP module icon

19.0.2.0.0
~~~~~~~~~~

Expand Down
4 changes: 2 additions & 2 deletions spp_api_v2_gis/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
{
"name": "OpenSPP GIS API",
"category": "OpenSPP/Integration",
"version": "19.0.2.0.0",
"version": "19.0.2.0.1",
"sequence": 1,
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
"license": "LGPL-3",
"development_status": "Alpha",
"development_status": "Beta",
"maintainers": ["jeremi", "gonzalesedwin1123", "reichie020212"],
"depends": [
"spp_api_v2",
Expand Down
7 changes: 7 additions & 0 deletions spp_api_v2_gis/readme/HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
### 19.0.2.0.1

- Promoted to Beta
- fix(schemas): Add GeoJSON geometry validation for spatial queries and geofences
- fix(routers): Use proper exception chaining (from e) for better debugging
- feat: Add SPP module icon

### 19.0.2.0.0

- Initial migration to OpenSPP2
6 changes: 3 additions & 3 deletions spp_api_v2_gis/routers/proximity.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ async def query_proximity(
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
) from None
except Exception:
) from e
except Exception as e:
_logger.exception("Proximity query failed")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Proximity query failed",
) from None
) from e
6 changes: 3 additions & 3 deletions spp_api_v2_gis/routers/spatial_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ async def query_statistics_batch(
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
) from None
except Exception:
) from e
except Exception as e:
_logger.exception("Batch spatial query failed")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Batch spatial query failed",
) from None
) from e
4 changes: 2 additions & 2 deletions spp_api_v2_gis/routers/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ async def list_statistics(
total_count=total_count,
)

except Exception:
except Exception as e:
_logger.exception("Failed to list statistics")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to list statistics",
) from None
) from e
16 changes: 14 additions & 2 deletions spp_api_v2_gis/schemas/geofence.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
"""Pydantic schemas for Geofence API."""

from pydantic import BaseModel, Field
from typing import Literal

from pydantic import BaseModel, Field, field_validator

from .query import _validate_geojson_geometry


class GeofenceCreateRequest(BaseModel):
Expand All @@ -10,7 +14,15 @@ class GeofenceCreateRequest(BaseModel):
name: str = Field(..., description="Name of the geofence")
description: str | None = Field(default=None, description="Description of the geofence")
geometry: dict = Field(..., description="Geometry as GeoJSON (Polygon or MultiPolygon)")
geofence_type: str = Field(default="custom", description="Type of geofence")
geofence_type: Literal["hazard_zone", "service_area", "targeting_area", "custom"] = Field(
default="custom", description="Type of geofence"
)

@field_validator("geometry")
@classmethod
def check_geometry(cls, v):
return _validate_geojson_geometry(v)

incident_code: str | None = Field(default=None, description="Related incident code")


Expand Down
30 changes: 29 additions & 1 deletion spp_api_v2_gis/schemas/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,36 @@

from typing import Literal

from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, field_validator

_VALID_GEOMETRY_TYPES = {"Polygon", "MultiPolygon"}


def _validate_geojson_geometry(v):
"""Validate that a dict is a valid GeoJSON geometry (Polygon or MultiPolygon)."""
if not isinstance(v, dict):
raise ValueError("geometry must be a JSON object")
geo_type = v.get("type")
if not geo_type:
raise ValueError("geometry must have a 'type' field")
if geo_type not in _VALID_GEOMETRY_TYPES:
raise ValueError(f"geometry type must be one of {_VALID_GEOMETRY_TYPES}, got '{geo_type}'")
Comment on lines +18 to +19
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message for invalid geometry types uses the default string representation of a Python set, which can look a bit unpolished (e.g., {'Polygon', 'MultiPolygon'}). It's better to format it as a sorted, comma-separated string for better readability.

Suggested change
if geo_type not in _VALID_GEOMETRY_TYPES:
raise ValueError(f"geometry type must be one of {_VALID_GEOMETRY_TYPES}, got '{geo_type}'")
if geo_type not in _VALID_GEOMETRY_TYPES:
valid_types = ", ".join(sorted(_VALID_GEOMETRY_TYPES))
raise ValueError(f"geometry type must be one of {valid_types}, got '{geo_type}'")

coords = v.get("coordinates")
if not coords or not isinstance(coords, list):
raise ValueError("geometry must have a non-empty 'coordinates' array")
Comment on lines +21 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The validation for the coordinates field is quite basic. While it correctly checks for a non-empty list, it doesn't verify that the structure matches what's expected for a Polygon (list of lists of lists) or MultiPolygon (list of lists of lists of lists). While a full GeoJSON validator might be overkill, consider adding a check for minimum nesting depth to catch obviously malformed inputs like [0, 0] for a Polygon.

return v


class SpatialQueryRequest(BaseModel):
"""Request for spatial query."""

geometry: dict = Field(..., description="Query geometry as GeoJSON (Polygon or MultiPolygon)")

@field_validator("geometry")
@classmethod
def check_geometry(cls, v):
return _validate_geojson_geometry(v)

filters: dict | None = Field(default=None, description="Additional filters for registrants")
variables: list[str] | None = Field(
default=None,
Expand Down Expand Up @@ -50,6 +73,11 @@ class GeometryItem(BaseModel):
id: str = Field(..., description="Unique identifier for this geometry (e.g., feature ID)")
geometry: dict = Field(..., description="GeoJSON geometry (Polygon or MultiPolygon)")

@field_validator("geometry")
@classmethod
def check_geometry(cls, v):
return _validate_geojson_geometry(v)


class BatchSpatialQueryRequest(BaseModel):
"""Request for batch spatial query across multiple geometries."""
Expand Down
Binary file added spp_api_v2_gis/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 19 additions & 12 deletions spp_api_v2_gis/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ <h1 class="title">OpenSPP GIS API</h1>
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0000000000000000000000000000000000000000000000000000000000000000
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_api_v2_gis"><img alt="OpenSPP/OpenSPP2" src="https://img.shields.io/badge/github-OpenSPP%2FOpenSPP2-lightgray.png?logo=github" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_api_v2_gis"><img alt="OpenSPP/OpenSPP2" src="https://img.shields.io/badge/github-OpenSPP%2FOpenSPP2-lightgray.png?logo=github" /></a></p>
<p>REST API for QGIS plugin integration, providing OGC API - Features
endpoints, spatial queries, and geofence management.</p>
<div class="section" id="key-features">
Expand Down Expand Up @@ -548,41 +548,48 @@ <h1>Dependencies</h1>
<li><tt class="docutils literal">spp_gis_report</tt> - Report configuration</li>
<li><tt class="docutils literal">spp_area</tt> - Administrative area data</li>
</ul>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.</p>
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#changelog" id="toc-entry-1">Changelog</a><ul>
<li><a class="reference internal" href="#section-1" id="toc-entry-2">19.0.2.0.0</a></li>
<li><a class="reference internal" href="#section-1" id="toc-entry-2">19.0.2.0.1</a></li>
<li><a class="reference internal" href="#section-2" id="toc-entry-3">19.0.2.0.0</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-4">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-5">Credits</a></li>
</ul>
</div>
<div class="section" id="changelog">
<h2><a class="toc-backref" href="#toc-entry-1">Changelog</a></h2>
<div class="section" id="section-1">
<h3><a class="toc-backref" href="#toc-entry-2">19.0.2.0.0</a></h3>
<h3><a class="toc-backref" href="#toc-entry-2">19.0.2.0.1</a></h3>
<ul class="simple">
<li>Promoted to Beta</li>
<li>fix(schemas): Add GeoJSON geometry validation for spatial queries and
geofences</li>
<li>fix(routers): Use proper exception chaining (from e) for better
debugging</li>
<li>feat: Add SPP module icon</li>
</ul>
</div>
<div class="section" id="section-2">
<h3><a class="toc-backref" href="#toc-entry-3">19.0.2.0.0</a></h3>
<ul class="simple">
<li>Initial migration to OpenSPP2</li>
</ul>
</div>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h2>
<h2><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OpenSPP/OpenSPP2/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OpenSPP/OpenSPP2/issues/new?body=module:%20spp_api_v2_gis%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-4">Credits</a></h2>
<h2><a class="toc-backref" href="#toc-entry-5">Credits</a></h2>
</div>
</div>
<div class="section" id="authors">
Expand Down
2 changes: 2 additions & 0 deletions spp_api_v2_gis/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
from . import test_catalog_service
from . import test_export_service
from . import test_geofence_http
from . import test_geofence_model
from . import test_layers_service
from . import test_ogc_features
Expand All @@ -10,3 +11,4 @@
from . import test_statistics_endpoint
from . import test_batch_query
from . import test_proximity_query
from . import test_router_http
Loading
Loading