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
76 changes: 44 additions & 32 deletions spp_area_hdx/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ OpenSPP HDX COD Integration
!! source digest: sha256:cf138e08394d940c78e288afd34fd606ac55ac16203d679b29c392b02aa4bdae
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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 All @@ -35,17 +35,17 @@ area lookup using PostGIS spatial queries.
Key Capabilities
~~~~~~~~~~~~~~~~

- Sync COD dataset metadata from HDX API by country
- Auto-detect field mappings from GeoJSON (P-code, name, parent P-code)
using HXL tags
- Import admin boundaries with polygons from HDX or manually uploaded
GeoJSON files
- Match imported features to existing areas by P-code or create new
areas
- GPS-based area lookup using PostGIS ``ST_Contains`` for
point-in-polygon queries
- Standardize area identification with HDX P-codes for inter-agency
coordination
- Sync COD dataset metadata from HDX API by country
- Auto-detect field mappings from GeoJSON (P-code, name, parent P-code)
using HXL tags
- Import admin boundaries with polygons from HDX or manually uploaded
GeoJSON files
- Match imported features to existing areas by P-code or create new
areas
- GPS-based area lookup using PostGIS ``ST_Contains`` for
point-in-polygon queries
- Standardize area identification with HDX P-codes for inter-agency
coordination

Key Models
~~~~~~~~~~
Expand Down Expand Up @@ -83,10 +83,10 @@ After installing:
UI Location
~~~~~~~~~~~

- **Menu**: Area > Areas > HDX Integration > COD Sources
- **Import**: Area > Areas > HDX Integration > Import COD
- **Area Records**: Extended with HDX P-code field visible in area form
view
- **Menu**: Area > Areas > HDX Integration > COD Sources
- **Import**: Area > Areas > HDX Integration > Import COD
- **Area Records**: Extended with HDX P-code field visible in area form
view

Security
~~~~~~~~
Expand All @@ -103,27 +103,22 @@ Security
Extension Points
~~~~~~~~~~~~~~~~

- ``spp.area.find_by_coordinates(latitude, longitude, level=None)`` -
Find area containing GPS point
- ``spp.area.find_all_containing(latitude, longitude)`` - Find all
areas in hierarchy containing point
- ``spp.area.find_by_pcode(pcode)`` - Find area by HDX P-code or
fallback to code field
- Inherit ``spp.hdx.cod.source`` to add country-specific dataset
discovery logic
- Inherit ``spp.hdx.cod.import.wizard._process_features()`` to
customize import behavior
- ``spp.area.find_by_coordinates(latitude, longitude, level=None)`` -
Find area containing GPS point
- ``spp.area.find_all_containing(latitude, longitude)`` - Find all areas
in hierarchy containing point
- ``spp.area.find_by_pcode(pcode)`` - Find area by HDX P-code or
fallback to code field
- Inherit ``spp.hdx.cod.source`` to add country-specific dataset
discovery logic
- Inherit ``spp.hdx.cod.import.wizard._process_features()`` to customize
import behavior

Dependencies
~~~~~~~~~~~~

``spp_area``, ``spp_gis``

.. 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.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
Expand All @@ -150,6 +145,23 @@ Authors
Maintainers
-----------

.. |maintainer-jeremi| image:: https://github.com/jeremi.png?size=40px
:target: https://github.com/jeremi
:alt: jeremi
.. |maintainer-gonzalesedwin1123| image:: https://github.com/gonzalesedwin1123.png?size=40px
:target: https://github.com/gonzalesedwin1123
:alt: gonzalesedwin1123
.. |maintainer-reichie020212| image:: https://github.com/reichie020212.png?size=40px
:target: https://github.com/reichie020212
:alt: reichie020212
.. |maintainer-emjay0921| image:: https://github.com/emjay0921.png?size=40px
:target: https://github.com/emjay0921
:alt: emjay0921

Current maintainers:

|maintainer-jeremi| |maintainer-gonzalesedwin1123| |maintainer-reichie020212| |maintainer-emjay0921|

This module is part of the `OpenSPP/OpenSPP2 <https://github.com/OpenSPP/OpenSPP2/tree/19.0/spp_area_hdx>`_ project on GitHub.

You are welcome to contribute.
3 changes: 2 additions & 1 deletion spp_area_hdx/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"author": "OpenSPP.org",
"website": "https://github.com/OpenSPP/OpenSPP2",
"license": "LGPL-3",
"development_status": "Alpha",
"development_status": "Beta",
"maintainers": ["jeremi", "gonzalesedwin1123", "reichie020212", "emjay0921"],
"depends": [
"spp_area",
"spp_gis",
Expand Down
23 changes: 18 additions & 5 deletions spp_area_hdx/models/hdx_cod_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,18 @@ class HdxCodSource(models.Model):

_country_unique = models.Constraint("UNIQUE(country_id)", "Only one COD source per country is allowed")

@api.depends("country_id")
@api.depends("country_id", "hdx_dataset_id")
def _compute_country_iso3(self):
"""Compute ISO3 code from country."""
"""Compute ISO3 code from HDX dataset ID or country.

HDX dataset IDs follow the pattern ``cod-ab-{iso3}`` (e.g., ``cod-ab-phl``),
which contains the correct 3-letter ISO code. Falls back to the 2-letter
``res.country.code`` when the dataset ID is not set.
"""
for record in self:
if record.country_id:
if record.hdx_dataset_id and record.hdx_dataset_id.startswith("cod-ab-"):
record.country_iso3 = record.hdx_dataset_id[len("cod-ab-"):].upper()
elif record.country_id:
record.country_iso3 = record.country_id.code
else:
record.country_iso3 = False
Expand All @@ -63,7 +70,12 @@ def action_sync_from_hdx(self):

try:
client = HdxClient()
dataset = client.search_cod_datasets(self.country_iso3)

# Use existing dataset ID if available, otherwise search by ISO3
if self.hdx_dataset_id:
dataset = client.get_dataset(self.hdx_dataset_id)
else:
dataset = client.search_cod_datasets(self.country_iso3)

if not dataset:
raise UserError(_("No COD dataset found for country %s") % self.country_id.name)
Expand All @@ -88,8 +100,9 @@ def action_sync_from_hdx(self):
admin_level = client.detect_admin_level(resource_name)

# Check if resource already exists
_rid, _lvl = resource_id, admin_level
existing_resource = self.resource_ids.filtered(
lambda r: r.hdx_resource_id == resource_id or r.admin_level == admin_level
lambda r, rid=_rid, lvl=_lvl: r.hdx_resource_id == rid or r.admin_level == lvl
)

resource_vals = {
Expand Down
12 changes: 6 additions & 6 deletions spp_area_hdx/tests/test_hdx_cod_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ def setUp(self):
self.source = self.env.ref("spp_area_hdx.cod_source_lka")

def test_compute_country_iso3(self):
"""Test ISO3 code computation."""
self.assertEqual(self.source.country_iso3, "LK")
"""Test ISO3 code computation from hdx_dataset_id."""
self.assertEqual(self.source.country_iso3, "LKA")

def test_unique_country_constraint(self):
"""Test that only one source per country is allowed."""
Expand All @@ -31,13 +31,13 @@ def test_unique_country_constraint(self):
}
)

@patch("odoo.addons.spp_area_hdx.services.hdx_client.HdxClient.search_cod_datasets")
@patch("odoo.addons.spp_area_hdx.services.hdx_client.HdxClient.get_dataset")
@patch("odoo.addons.spp_area_hdx.services.hdx_client.HdxClient.get_geojson_resources")
@patch("odoo.addons.spp_area_hdx.services.hdx_client.HdxClient.detect_admin_level")
def test_action_sync_from_hdx(self, mock_detect_level, mock_get_resources, mock_search):
def test_action_sync_from_hdx(self, mock_detect_level, mock_get_resources, mock_get_dataset):
"""Test syncing resources from HDX."""
# Mock HDX API responses
mock_search.return_value = {
# Mock HDX API responses — source has hdx_dataset_id set, so get_dataset is called
mock_get_dataset.return_value = {
"name": "cod-ab-lka",
"title": "Sri Lanka Administrative Boundaries",
}
Expand Down
Loading