diff --git a/pyproject.toml b/pyproject.toml index 68ebdd8..f2dbd09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pysdmx" -version = "1.0.0-beta-7" +version = "1.0.0-beta-8" description = "Your opinionated Python SDMX library" authors = [ "Xavier Sosnovsky ", diff --git a/src/pysdmx/__init__.py b/src/pysdmx/__init__.py index 774c2bf..5773f50 100644 --- a/src/pysdmx/__init__.py +++ b/src/pysdmx/__init__.py @@ -1,3 +1,3 @@ """Your opinionated Python SDMX library.""" -__version__ = "1.0.0-beta-7" +__version__ = "1.0.0-beta-8" diff --git a/src/pysdmx/model/code.py b/src/pysdmx/model/code.py index e13ed5b..4cf85ea 100644 --- a/src/pysdmx/model/code.py +++ b/src/pysdmx/model/code.py @@ -257,6 +257,34 @@ def by_id(self, id: str) -> Sequence[HierarchicalCode]: """ return self.__by_id(id, self.codes) + def __get_codes( + self, codes: Sequence[HierarchicalCode] + ) -> Sequence[HierarchicalCode]: + out = [] + for code in codes: + out.append(code) + if code.codes: + out.extend(self.__get_codes(code.codes)) + return out + + def all_codes(self) -> Sequence[HierarchicalCode]: + """Get all the codes in the hierarchy as a flat list. + + This is useful for validation purposes. The sequence behaves + as a set, i.e. even if a code is attached to multiple nodes, + it will be available only once in the returned sequence. + + Returns: + A flat list of the codes present in the hierarchy. + """ + out = [] + # We need to do this below because a hierarchical code is not + # (yet?) hashable. + for c in self.__get_codes(self.codes): + if c not in out: + out.append(c) + return out + class HierarchyAssociation(Struct, frozen=True, omit_defaults=True): """Links a hierarchy to a component withing the context of a dataflow.""" diff --git a/src/pysdmx/util/__init__.py b/src/pysdmx/util/__init__.py index 2c3397c..732645d 100644 --- a/src/pysdmx/util/__init__.py +++ b/src/pysdmx/util/__init__.py @@ -7,6 +7,8 @@ from pysdmx.errors import NotFound +NF = "Not found" + class Reference(Struct, frozen=True): """The coordinates of an SDMX maintainable artefact. @@ -58,9 +60,7 @@ def parse_urn(urn: str) -> Reference: ) else: raise NotFound( - 404, - "Not found", - f"{urn} does not match {maintainable_urn_pattern}", + 404, NF, f"{urn} does not match {maintainable_urn_pattern}" ) @@ -76,9 +76,7 @@ def parse_item_urn(urn: str) -> ItemReference: item_id=m.group(5), ) else: - raise NotFound( - 404, "Not found", f"{urn} does not match {item_urn_pattern}." - ) + raise NotFound(404, NF, f"{urn} does not match {item_urn_pattern}.") def find_by_urn(artefacts: Sequence[Any], urn: str) -> Any: @@ -95,7 +93,7 @@ def find_by_urn(artefacts: Sequence[Any], urn: str) -> Any: urns = [f"{a.agency}:{a.id}({a.version})" for a in artefacts] raise NotFound( 404, - "Not found", + NF, ( f"Could not find an artefact matching the following URN: " f"{urn}. The artefacts received were: {urns}." diff --git a/tests/model/test_hierarchy.py b/tests/model/test_hierarchy.py index f941857..8aad1e8 100644 --- a/tests/model/test_hierarchy.py +++ b/tests/model/test_hierarchy.py @@ -169,3 +169,26 @@ def test_codes_by_id_diff_names(id, name, agency): m = list(m) assert grandchild1 in m assert grandchild3 in m + + +def test_all_codes(id, name, agency): + grandchild1 = HierarchicalCode("grandchild1", "grandchild 1") + grandchild2 = HierarchicalCode("grandchild2", "grandchild 2") + grandchild3 = HierarchicalCode("grandchild3", "grandchild 3") + child1 = HierarchicalCode( + "child1", "child 1", codes=[grandchild1, grandchild2] + ) + child2 = HierarchicalCode( + "child2", "child 2", codes=[grandchild1, grandchild2, grandchild3] + ) + parent1 = HierarchicalCode("parent1", "parent 1") + parent2 = HierarchicalCode( + "parent2", + "parent 2", + codes=[child1, child2], + ) + + h = Hierarchy(id, name, agency, codes=[parent1, parent2]) + + m = h.all_codes() + assert len(m) == 7