Skip to content

Commit

Permalink
Add NUTS regions (#393)
Browse files Browse the repository at this point in the history
* Import pysquirrel

* Update definitions with nuts

* Add NUTS regions according to config

* Test pysquirrel integration

* Add pysquirrel as dependency

* Fix nuts config typing

* Add None to nuts typing

* Make nuts validation error explicit

* Change nuts import and variable names

* Change test region to str

* Create nomenclature.nuts module

* Fix nuts import
  • Loading branch information
dc-almeida authored Sep 20, 2024
1 parent 73715e4 commit 4ae6fa3
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 2 deletions.
1 change: 1 addition & 0 deletions nomenclature/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from nomenclature.codelist import CodeList # noqa
from nomenclature.core import process # noqa
from nomenclature.countries import countries # noqa
from nomenclature.nuts import nuts # noqa
from nomenclature.definition import SPECIAL_CODELIST, DataStructureDefinition # noqa
from nomenclature.processor import RegionAggregationMapping # noqa
from nomenclature.processor import RegionProcessor, RequiredDataValidator # noqa
Expand Down
12 changes: 12 additions & 0 deletions nomenclature/codelist.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from nomenclature.code import Code, MetaCode, RegionCode, VariableCode
from nomenclature.config import CodeListConfig, NomenclatureConfig
from nomenclature.error import ErrorCollector, custom_pydantic_errors, log_error
from nomenclature.nuts import nuts


here = Path(__file__).parent.absolute()

Expand Down Expand Up @@ -726,6 +728,16 @@ def from_directory(
except AttributeError:
code_list.append(RegionCode(name=c.name, hierarchy="Country"))

# adding nuts regions
if config.definitions.region.nuts:
for level, countries in config.definitions.region.nuts.items():
for nuts_region in nuts.get(
level=int(level[-1]), country_code=countries
):
code_list.append(
RegionCode(name=nuts_region.code, hierarchy="NUTS 2021-2024")
)

# importing from an external repository
for repo in config.definitions.region.repositories:
repo_path = config.repositories[repo].local_path / "definitions" / "region"
Expand Down
13 changes: 13 additions & 0 deletions nomenclature/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ def repository_dimension_path(self) -> str:

class RegionCodeListConfig(CodeListConfig):
country: bool = False
nuts: dict[str, str | list[str]] | None = None

@field_validator("nuts")
@classmethod
def check_nuts(
cls, v: dict[str, str | list[str]] | None
) -> dict[str, str | list[str]] | None:
if v and not all(k in ["nuts-1", "nuts-2", "nuts-3"] for k in v.keys()):
raise ValueError(
"Invalid fields for `nuts` in configuration. "
"Allowed values are: 'nuts-1', 'nuts-2' and 'nuts-3'."
)
return v


class Repository(BaseModel):
Expand Down
4 changes: 3 additions & 1 deletion nomenclature/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ def __init__(self, path, dimensions=None):
self.repo = None

if not path.is_dir() and not (
self.config.repositories or self.config.definitions.region.country
self.config.repositories
or self.config.definitions.region.country
or self.config.definitions.region.nuts
):
raise NotADirectoryError(f"Definitions directory not found: {path}")

Expand Down
7 changes: 7 additions & 0 deletions nomenclature/nuts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import logging

import pysquirrel

logger = logging.getLogger(__name__)

nuts = pysquirrel.nuts
55 changes: 54 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pydantic = "^2"
PyYAML = "^6.0.1"
pandas = ">=1.5.2"
pycountry = "23.12.11" # pin to guard against "silent" changes in country names
pysquirrel = "1.0"
gitpython = "^3.1.40"
numpy = "^1.23.0"

Expand Down
8 changes: 8 additions & 0 deletions tests/data/general-config-only-nuts/nomenclature.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dimensions:
- region
definitions:
region:
nuts:
nuts-1: [ AT ]
nuts-2: BE
nuts-3: [ CZ ]
11 changes: 11 additions & 0 deletions tests/test_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ def test_definition_general_config_country_only():
assert all(region in obs.region for region in ("Austria", "Bolivia", "Kosovo"))


def test_definition_general_config_nuts_only():
"""Check that DataStructureDefinition is properly initialised with NUTS region config only"""
obs = DataStructureDefinition(
TEST_DATA_DIR / "general-config-only-nuts" / "definitions"
)
assert all(region[:2] in ("AT", "BE", "CZ") for region in obs.region)
assert len([region for region in obs.region if region.startswith("AT")]) == 4
assert len([region for region in obs.region if region.startswith("BE")]) == 12
assert len([region for region in obs.region if region.startswith("CZ")]) == 15


def test_to_excel(simple_definition, tmpdir):
"""Check writing a DataStructureDefinition to file"""
file = tmpdir / "testing_export.xlsx"
Expand Down

0 comments on commit 4ae6fa3

Please sign in to comment.