Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get reference metadata reports for a structure #12

Merged
merged 2 commits into from
Jan 18, 2024
Merged
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
57 changes: 55 additions & 2 deletions src/pysdmx/fmr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class Context(Enum):
),
"provider": "structure/dataproviderscheme/{0}?references={1}",
"report": "metadata/metadataset/{0}/{1}/{2}",
"reports": "metadata/structure/{0}/{1}/{2}/{3}",
"schema": "schema/{0}/{1}/{2}/{3}",
"vl": "structure/valuelist/{0}/{1}/{2}",
}
Expand Down Expand Up @@ -425,7 +426,33 @@ def get_report(
The requested metadata report.
"""
out = self.__fetch(super()._url("report", provider, id, version), True)
return super()._out(out, self.deser.report)
return super()._out(out, self.deser.report)[0]

def get_reports(
self,
artefact_type: str,
agency: str,
id: str,
version: str = "+",
) -> Sequence[MetadataReport]:
"""Get the reference metadata reports for the supplied structure.

Args:
artefact_type: The type of the structure for which reports must
be returned.
agency: The agency maintaining the hierarchy for which reports
must be returned.
id: The ID of the structure for which reports must be returned.
version: The version of the structure for which reports must be
returned.

Returns:
The metadata reports about the supplied structure.
"""
out = self.__fetch(
super()._url("reports", artefact_type, agency, id, version), True
)
return super()._out(out, self.deser.report, True)

def get_mapping(
self,
Expand Down Expand Up @@ -734,7 +761,33 @@ async def get_report(
out = await self.__fetch(
super()._url("report", provider, id, version), True
)
return super()._out(out, self.deser.report)
return super()._out(out, self.deser.report)[0]

async def get_reports(
self,
artefact_type: str,
agency: str,
id: str,
version: str = "+",
) -> Sequence[MetadataReport]:
"""Get the reference metadata reports for the supplied structure.

Args:
artefact_type: The type of the structure for which reports must
be returned.
agency: The agency maintaining the hierarchy for which reports
must be returned.
id: The ID of the structure for which reports must be returned.
version: The version of the structure for which reports must be
returned.

Returns:
The metadata reports about the supplied structure.
"""
out = await self.__fetch(
super()._url("reports", artefact_type, agency, id, version), True
)
return super()._out(out, self.deser.report, True)

async def get_mapping(
self,
Expand Down
11 changes: 8 additions & 3 deletions src/pysdmx/fmr/fusion/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,15 @@ class FusionMetadataMessage(Struct, frozen=True):

data: FusionMetadataSets

def to_model(self) -> MetadataReport:
"""Returns the requested metadata report."""
r = self.data.metadatasets[0]
def __create_report(self, r: FusionMetadataReport) -> MetadataReport:
attrs = _merge_attributes(r.attributes)
return MetadataReport(
r.id, r.names[0].value, r.metadataflow, r.targets, attrs
)

def to_model(self, fetch_all: bool = False) -> Sequence[MetadataReport]:
"""Returns the requested metadata report(s)."""
if fetch_all:
return [self.__create_report(r) for r in self.data.metadatasets]
else:
return [self.__create_report(self.data.metadatasets[0])]
11 changes: 8 additions & 3 deletions src/pysdmx/fmr/sdmx/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ class JsonMetadataMessage(Struct, frozen=True):

data: JsonMetadataSets

def to_model(self) -> MetadataReport:
"""Returns the requested metadata report."""
r = self.data.metadataSets[0]
def __create_report(self, r: MetadataReport) -> MetadataReport:
attrs = _merge_attributes(r.attributes)
return MetadataReport(r.id, r.name, r.metadataflow, r.targets, attrs)

def to_model(self, fetch_all: bool = False) -> Sequence[MetadataReport]:
"""Returns the requested metadata report(s)."""
if fetch_all:
return [self.__create_report(r) for r in self.data.metadataSets]
else:
return [self.__create_report(self.data.metadataSets[0])]
47 changes: 47 additions & 0 deletions tests/fmr/fusion/test_reports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import pytest
import tests.fmr.mult_reports_checks as checks

from pysdmx.fmr import AsyncRegistryClient, Format, RegistryClient


@pytest.fixture()
def fmr():
return RegistryClient(
"https://registry.sdmx.org/sdmx/v2/",
Format.FUSION_JSON,
)


@pytest.fixture()
def async_fmr() -> AsyncRegistryClient:
return AsyncRegistryClient(
"https://registry.sdmx.org/sdmx/v2/",
Format.FUSION_JSON,
)


@pytest.fixture()
def query(fmr):
res = "metadata/structure/"
typ = "dataflow"
agency = "BIS.MACRO"
id = "BIS_MACRO"
version = "1.0"
return f"{fmr.api_endpoint}{res}{typ}/{agency}/{id}/{version}"


@pytest.fixture()
def body():
with open("tests/fmr/samples/refmeta/mult_reports.fusion.json", "rb") as f:
return f.read()


def test_returns_mult_report(respx_mock, fmr, query, body):
"""get_reports() should return multiple reports."""
checks.check_reports(respx_mock, fmr, query, body)


@pytest.mark.asyncio()
async def test_returns_mult_report_async(respx_mock, async_fmr, query, body):
"""get_reports() should return multiple reports (async)."""
await checks.check_reports_async(respx_mock, async_fmr, query, body)
86 changes: 86 additions & 0 deletions tests/fmr/mult_reports_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from typing import Sequence

import httpx

from pysdmx.fmr import AsyncRegistryClient, RegistryClient
from pysdmx.model import MetadataAttribute


def check_reports(mock, fmr: RegistryClient, query, body):
"""get_reports() should return multiple metadata reports."""
mock.get(query).mock(
return_value=httpx.Response(
200,
content=body,
)
)

reports = fmr.get_reports("dataflow", "BIS.MACRO", "BIS_MACRO", "1.0")

assert isinstance(reports, Sequence)
assert len(reports) == 2
for r in reports:
assert "Dataflow=BIS.MACRO:BIS_MACRO(1.0)" in r.targets[0]
if r.id == "DTI_BIS_MACRO":
assert r.name == "Technical metadata for BIS.MACRO:BIS_MACRO"
assert r.metadataflow == (
"urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow="
"BIS.MEDIT:DTI(1.0)"
)
assert len(r.targets) == 1

assert len(r) == 5
for attr in r:
assert isinstance(attr, MetadataAttribute)
else:
assert r.name == "Reference metadata for BIS.MACRO:BIS_MACRO"
assert r.metadataflow == (
"urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow="
"BIS:DF_META(1.0)"
)
assert len(r.targets) == 1

assert len(r) == 1
for attr in r:
assert isinstance(attr, MetadataAttribute)


async def check_reports_async(mock, fmr: AsyncRegistryClient, query, body):
"""get_reports() should return multiple metadata reports."""
mock.get(query).mock(
return_value=httpx.Response(
200,
content=body,
)
)

reports = await fmr.get_reports(
"dataflow", "BIS.MACRO", "BIS_MACRO", "1.0"
)

assert isinstance(reports, Sequence)
assert len(reports) == 2
for r in reports:
assert "Dataflow=BIS.MACRO:BIS_MACRO(1.0)" in r.targets[0]
if r.id == "DTI_BIS_MACRO":
assert r.name == "Technical metadata for BIS.MACRO:BIS_MACRO"
assert r.metadataflow == (
"urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow="
"BIS.MEDIT:DTI(1.0)"
)
assert len(r.targets) == 1

assert len(r) == 5
for attr in r:
assert isinstance(attr, MetadataAttribute)
else:
assert r.name == "Reference metadata for BIS.MACRO:BIS_MACRO"
assert r.metadataflow == (
"urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow="
"BIS:DF_META(1.0)"
)
assert len(r.targets) == 1

assert len(r) == 1
for attr in r:
assert isinstance(attr, MetadataAttribute)
87 changes: 87 additions & 0 deletions tests/fmr/samples/refmeta/mult_reports.fusion.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"meta": {
"id": "IREF389470",
"test": false,
"prepared": "2023-09-18T12:09:04Z",
"contentLanguages": [
"en"
],
"sender": {
"id": "FusionRegistry"
},
"format": "fusionjson"
},
"data": {
"metadatasets": [
{
"id": "DTI_BIS_MACRO",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=BIS.MEDIT:DTI_BIS_MACRO(1.0)",
"names": [
{
"locale": "en",
"value": "Technical metadata for BIS.MACRO:BIS_MACRO"
}
],
"agencyId": "BIS.MEDIT",
"version": "1.0",
"isFinal": false,
"metadataflow": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=BIS.MEDIT:DTI(1.0)",
"targets": [
"urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=BIS.MACRO:BIS_MACRO(1.0)"
],
"attributes": [
{
"id": "idea_partition_details",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttribute=BIS.MEDIT:DTI_BIS_MACRO(1.0).idea_partition_details",
"attributes": [
{
"id": "idea_partition_name",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttribute=BIS.MEDIT:DTI_BIS_MACRO(1.0).idea_partition_name",
"value": "dt"
},
{
"id": "idea_partition_pattern",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttribute=BIS.MEDIT:DTI_BIS_MACRO(1.0).idea_partition_pattern",
"value": "\\d{4}-(0\\d|1[012])"
},
{
"id": "idea_partition_type",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttribute=BIS.MEDIT:DTI_BIS_MACRO(1.0).idea_partition_type",
"value": "string"
}
]
},
{
"id": "record_format",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttribute=BIS.MEDIT:DTI_BIS_MACRO(1.0).record_format",
"value": "A"
}
]
},
{
"id": "BIS_MACRO",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataSet=BIS.MACRO:BIS_MACRO(1.0)",
"names": [
{
"locale": "en",
"value": "Reference metadata for BIS.MACRO:BIS_MACRO"
}
],
"agencyId": "BIS.MACRO",
"version": "1.0",
"isFinal": false,
"metadataflow": "urn:sdmx:org.sdmx.infomodel.metadatastructure.Metadataflow=BIS:DF_META(1.0)",
"targets": [
"urn:sdmx:org.sdmx.infomodel.datastructure.Dataflow=BIS.MACRO:BIS_MACRO(1.0)"
],
"attributes": [
{
"id": "methodology",
"urn": "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataAttribute=BIS.MACRO:BIS_MACRO(1.0).methodology",
"value": "A methodological note"
}
]
}
]
}
}
Loading