pysdmx 1.7.0__py3-none-any.whl → 1.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. pysdmx/__init__.py +1 -1
  2. pysdmx/api/fmr/__init__.py +266 -0
  3. pysdmx/api/qb/refmeta.py +1 -1
  4. pysdmx/api/qb/schema.py +4 -10
  5. pysdmx/api/qb/service.py +26 -4
  6. pysdmx/api/qb/structure.py +2 -2
  7. pysdmx/io/json/fusion/messages/__init__.py +14 -0
  8. pysdmx/io/json/fusion/messages/dsd.py +52 -1
  9. pysdmx/io/json/fusion/messages/metadataflow.py +44 -0
  10. pysdmx/io/json/fusion/messages/mpa.py +45 -0
  11. pysdmx/io/json/fusion/messages/msd.py +121 -0
  12. pysdmx/io/json/fusion/messages/org.py +90 -0
  13. pysdmx/io/json/fusion/reader/__init__.py +5 -0
  14. pysdmx/io/json/sdmxjson2/messages/__init__.py +15 -1
  15. pysdmx/io/json/sdmxjson2/messages/dsd.py +9 -6
  16. pysdmx/io/json/sdmxjson2/messages/metadataflow.py +88 -0
  17. pysdmx/io/json/sdmxjson2/messages/mpa.py +88 -0
  18. pysdmx/io/json/sdmxjson2/messages/msd.py +245 -0
  19. pysdmx/io/json/sdmxjson2/messages/provider.py +117 -1
  20. pysdmx/io/json/sdmxjson2/messages/structure.py +25 -1
  21. pysdmx/io/json/sdmxjson2/reader/__init__.py +5 -0
  22. pysdmx/io/serde.py +5 -0
  23. pysdmx/io/writer.py +2 -4
  24. pysdmx/io/xml/__structure_aux_writer.py +9 -9
  25. pysdmx/io/xml/__write_data_aux.py +1 -2
  26. pysdmx/model/__init__.py +12 -1
  27. pysdmx/model/dataflow.py +1 -0
  28. pysdmx/model/map.py +3 -1
  29. pysdmx/model/message.py +29 -2
  30. pysdmx/model/metadata.py +255 -4
  31. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/METADATA +1 -1
  32. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/RECORD +34 -28
  33. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/WHEEL +0 -0
  34. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,121 @@
1
+ """Collection of Fusion-JSON schemas for SDMX-REST schema queries."""
2
+
3
+ from typing import Literal, Optional, Sequence, Union
4
+
5
+ from msgspec import Struct
6
+
7
+ from pysdmx.io.json.fusion.messages.code import FusionCodelist
8
+ from pysdmx.io.json.fusion.messages.concept import FusionConceptScheme
9
+ from pysdmx.io.json.fusion.messages.core import (
10
+ FusionRepresentation,
11
+ FusionString,
12
+ )
13
+ from pysdmx.io.json.fusion.messages.dsd import (
14
+ _find_concept,
15
+ _get_representation,
16
+ )
17
+ from pysdmx.model import (
18
+ ArrayBoundaries,
19
+ MetadataComponent,
20
+ )
21
+ from pysdmx.model import (
22
+ MetadataStructure as MSD,
23
+ )
24
+
25
+
26
+ class FusionMetadataAttribute(Struct, frozen=True):
27
+ """Fusion-JSON payload for a metadata attribute."""
28
+
29
+ id: str
30
+ concept: str
31
+ minOccurs: int
32
+ maxOccurs: Union[int, Literal["unbounded"]] = "unbounded"
33
+ presentational: Optional[bool] = False
34
+ representation: Optional[FusionRepresentation] = None
35
+ metadataAttributes: Sequence["FusionMetadataAttribute"] = ()
36
+
37
+ def to_model(
38
+ self,
39
+ cs: Sequence[FusionConceptScheme],
40
+ cls: Sequence[FusionCodelist],
41
+ ) -> MetadataComponent:
42
+ """Returns an attribute."""
43
+ c = _find_concept(cs, self.concept)
44
+ dt, facets, codes, _ = _get_representation(
45
+ self.id, self.representation, cls, {}
46
+ )
47
+
48
+ if self.representation and self.representation.representation:
49
+ local_enum_ref = self.representation.representation
50
+ else:
51
+ local_enum_ref = None
52
+
53
+ if self.maxOccurs == "unbounded":
54
+ ab = ArrayBoundaries(self.minOccurs)
55
+ elif self.maxOccurs > 1:
56
+ ab = ArrayBoundaries(self.minOccurs, self.maxOccurs)
57
+ else:
58
+ ab = None
59
+
60
+ return MetadataComponent(
61
+ self.id,
62
+ is_presentational=self.presentational, # type: ignore[arg-type]
63
+ concept=c.to_model(cls),
64
+ local_dtype=dt,
65
+ local_facets=facets,
66
+ local_codes=codes,
67
+ array_def=ab,
68
+ local_enum_ref=local_enum_ref,
69
+ components=[
70
+ ma.to_model(cs, cls) for ma in self.metadataAttributes
71
+ ],
72
+ )
73
+
74
+
75
+ class FusionMetadataStructure(
76
+ Struct, frozen=True, rename={"agency": "agencyId"}
77
+ ):
78
+ """Fusion-JSON payload for an MSD."""
79
+
80
+ id: str
81
+ names: Sequence[FusionString]
82
+ agency: str
83
+ descriptions: Optional[Sequence[FusionString]] = None
84
+ version: str = "1.0"
85
+ metadataAttributes: Sequence[FusionMetadataAttribute] = ()
86
+
87
+ def to_model(
88
+ self,
89
+ cs: Sequence[FusionConceptScheme],
90
+ cls: Sequence[FusionCodelist],
91
+ ) -> MSD:
92
+ """Returns the schema for this DSD."""
93
+ return MSD(
94
+ id=self.id,
95
+ agency=self.agency,
96
+ name=self.names[0].value if self.names else None,
97
+ description=(
98
+ self.descriptions[0].value if self.descriptions else None
99
+ ),
100
+ version=self.version,
101
+ components=[a.to_model(cs, cls) for a in self.metadataAttributes],
102
+ )
103
+
104
+
105
+ class FusionMetadataStructuresMessage(Struct, frozen=True):
106
+ """Fusion-JSON payload for /metadatastructure queries."""
107
+
108
+ ConceptScheme: Sequence[FusionConceptScheme]
109
+ MetadataStructure: Sequence[FusionMetadataStructure]
110
+ ValueList: Sequence[FusionCodelist] = ()
111
+ Codelist: Sequence[FusionCodelist] = ()
112
+
113
+ def to_model(self) -> Sequence[MSD]:
114
+ """Returns the requested metadata structures."""
115
+ all_mds = []
116
+ for msd in self.MetadataStructure:
117
+ cls: list[FusionCodelist] = []
118
+ cls.extend(self.Codelist)
119
+ cls.extend(self.ValueList)
120
+ all_mds.append(msd.to_model(self.ConceptScheme, cls))
121
+ return all_mds
@@ -6,12 +6,14 @@ from typing import Dict, Optional, Sequence, Set
6
6
  from msgspec import Struct
7
7
 
8
8
  from pysdmx.io.json.fusion.messages.core import FusionString
9
+ from pysdmx.io.json.fusion.messages.mpa import FusionMetadataProvisionAgreement
9
10
  from pysdmx.io.json.fusion.messages.pa import FusionProvisionAgreement
10
11
  from pysdmx.model import (
11
12
  Agency,
12
13
  Contact,
13
14
  DataflowRef,
14
15
  DataProvider,
16
+ MetadataProvider,
15
17
  )
16
18
  from pysdmx.model import (
17
19
  AgencyScheme as AS,
@@ -19,6 +21,9 @@ from pysdmx.model import (
19
21
  from pysdmx.model import (
20
22
  DataProviderScheme as DPS,
21
23
  )
24
+ from pysdmx.model import (
25
+ MetadataProviderScheme as MDPS,
26
+ )
22
27
  from pysdmx.util import parse_item_urn, parse_urn
23
28
 
24
29
 
@@ -188,3 +193,88 @@ class FusionProviderMessage(Struct, frozen=True):
188
193
  p.to_model(self.ProvisionAgreement)
189
194
  for p in self.DataProviderScheme
190
195
  ]
196
+
197
+
198
+ class FusionMetadataProvider(Struct, frozen=True):
199
+ """Fusion-JSON payload for a metadata provider."""
200
+
201
+ id: str
202
+ names: Sequence[FusionString]
203
+ descriptions: Optional[Sequence[FusionString]] = None
204
+ contacts: Sequence[FusionContact] = ()
205
+
206
+ def to_model(self, owner: Optional[str] = None) -> MetadataProvider:
207
+ """Converts a FusionMetadataProvider to a pysdmx one."""
208
+ d = self.descriptions[0].value if self.descriptions else None
209
+ c = tuple([c.to_model() for c in self.contacts])
210
+ oid = f"{owner}.{self.id}" if owner and owner != "SDMX" else self.id
211
+ return MetadataProvider(
212
+ id=oid, name=self.names[0].value, description=d, contacts=c
213
+ )
214
+
215
+
216
+ class FusionMetadataProviderScheme(Struct, frozen=True):
217
+ """Fusion-JSON payload for a metadata provider scheme."""
218
+
219
+ id: str
220
+ agencyId: str
221
+ names: Sequence[FusionString] = ()
222
+ descriptions: Sequence[FusionString] = ()
223
+ items: Sequence[FusionMetadataProvider] = ()
224
+
225
+ def __get_df_ref(self, ref: str) -> DataflowRef:
226
+ a = parse_urn(ref)
227
+ return DataflowRef(id=a.id, agency=a.agency, version=a.version)
228
+
229
+ def to_model(
230
+ self, pas: Sequence[FusionMetadataProvisionAgreement]
231
+ ) -> MDPS:
232
+ """Converts a FusionProviderScheme to a DataProviderScheme."""
233
+ if pas:
234
+ paprs: Dict[str, Set[DataflowRef]] = defaultdict(set)
235
+ for pa in pas:
236
+ df = self.__get_df_ref(pa.metadataflowRef)
237
+ ref = parse_item_urn(pa.metadataproviderRef)
238
+ paprs[f"{ref.agency}:{ref.item_id}"].add(df)
239
+ prvs = [o.to_model() for o in self.items]
240
+ prvs = [
241
+ MetadataProvider(
242
+ id=p.id,
243
+ name=p.name,
244
+ description=p.description,
245
+ contacts=p.contacts,
246
+ dataflows=list(paprs[f"{self.agencyId}:{p.id}"]),
247
+ )
248
+ for p in prvs
249
+ ]
250
+ return MDPS(
251
+ agency=self.agencyId,
252
+ name=self.names[0].value,
253
+ description=(
254
+ self.descriptions[0].value if self.descriptions else None
255
+ ),
256
+ items=prvs,
257
+ )
258
+ else:
259
+ return MDPS(
260
+ agency=self.agencyId,
261
+ name=self.names[0].value,
262
+ description=(
263
+ self.descriptions[0].value if self.descriptions else None
264
+ ),
265
+ items=[o.to_model() for o in self.items],
266
+ )
267
+
268
+
269
+ class FusionMetadataProviderMessage(Struct, frozen=True):
270
+ """Fusion-JSON payload for /metadataproviderscheme queries."""
271
+
272
+ MetadataProviderScheme: Sequence[FusionMetadataProviderScheme]
273
+ MetadataProvisionAgreement: Sequence[FusionMetadataProvisionAgreement] = ()
274
+
275
+ def to_model(self) -> Sequence[MDPS]:
276
+ """Returns the requested list of metadata providers."""
277
+ return [
278
+ p.to_model(self.MetadataProvisionAgreement)
279
+ for p in self.MetadataProviderScheme
280
+ ]
@@ -20,4 +20,9 @@ deserializers = Deserializers(
20
20
  mapping=msg.FusionMappingMessage, # type: ignore[arg-type]
21
21
  code_map=msg.FusionRepresentationMapMessage, # type: ignore[arg-type]
22
22
  transformation_scheme=msg.FusionTransfoMsg, # type: ignore[arg-type]
23
+ metadataflows=msg.FusionMetadataflowsMessage, # type: ignore[arg-type]
24
+ metadata_provision_agreement=msg.FusionMetadataProvisionAgreementMessage, # type: ignore[arg-type]
25
+ metadata_providers=msg.FusionMetadataProviderMessage, # type: ignore[arg-type]
26
+ msds=msg.FusionMetadataStructuresMessage, # type: ignore[arg-type]
27
+ data_structures=msg.FusionDataStructuresMessage, # type: ignore[arg-type]
23
28
  )
@@ -23,10 +23,20 @@ from pysdmx.io.json.sdmxjson2.messages.map import (
23
23
  JsonRepresentationMapsMessage,
24
24
  JsonStructureMapsMessage,
25
25
  )
26
+ from pysdmx.io.json.sdmxjson2.messages.metadataflow import (
27
+ JsonMetadataflowsMessage as JsonMdfsMsg,
28
+ )
29
+ from pysdmx.io.json.sdmxjson2.messages.mpa import (
30
+ JsonMetadataProvisionAgreementsMessage as JsonMPAMsg,
31
+ )
32
+ from pysdmx.io.json.sdmxjson2.messages.msd import JsonMetadataStructuresMessage
26
33
  from pysdmx.io.json.sdmxjson2.messages.pa import (
27
34
  JsonProvisionAgreementsMessage as JsonPAMessage,
28
35
  )
29
- from pysdmx.io.json.sdmxjson2.messages.provider import JsonProviderMessage
36
+ from pysdmx.io.json.sdmxjson2.messages.provider import (
37
+ JsonMetadataProviderMessage,
38
+ JsonProviderMessage,
39
+ )
30
40
  from pysdmx.io.json.sdmxjson2.messages.report import JsonMetadataMessage
31
41
  from pysdmx.io.json.sdmxjson2.messages.schema import JsonSchemaMessage
32
42
  from pysdmx.io.json.sdmxjson2.messages.structure import JsonStructureMessage
@@ -43,14 +53,18 @@ __all__ = [
43
53
  "JsonDataflowMessage",
44
54
  "JsonDataflowsMessage",
45
55
  "JsonDataStructuresMessage",
56
+ "JsonMetadataProviderMessage",
46
57
  "JsonProviderMessage",
47
58
  "JsonPAMessage",
48
59
  "JsonSchemaMessage",
49
60
  "JsonHierarchyAssociationMessage",
50
61
  "JsonHierarchiesMessage",
51
62
  "JsonHierarchyMessage",
63
+ "JsonMdfsMsg",
52
64
  "JsonMetadataMessage",
65
+ "JsonMPAMsg",
53
66
  "JsonMappingMessage",
67
+ "JsonMetadataStructuresMessage",
54
68
  "JsonRepresentationMapMessage",
55
69
  "JsonRepresentationMapsMessage",
56
70
  "JsonStructureMapsMessage",
@@ -1,6 +1,6 @@
1
- """Collection of SDMX-JSON schemas for SDMX-REST schema queries."""
1
+ """Collection of SDMX-JSON schemas for SDMX-REST DSD queries."""
2
2
 
3
- from typing import Dict, List, Literal, Optional, Sequence, Tuple
3
+ from typing import Dict, List, Literal, Optional, Sequence, Tuple, Union
4
4
 
5
5
  from msgspec import Struct
6
6
 
@@ -27,6 +27,7 @@ from pysdmx.model import (
27
27
  DataType,
28
28
  Facets,
29
29
  ItemReference,
30
+ MetadataComponent,
30
31
  Role,
31
32
  )
32
33
  from pysdmx.model.dataflow import Group
@@ -43,7 +44,7 @@ def _find_concept(cs: Sequence[JsonConceptScheme], urn: str) -> JsonConcept:
43
44
  return [c for c in f[0].concepts if c.id == r.item_id][0]
44
45
 
45
46
 
46
- def __get_type(repr_: JsonRepresentation) -> Optional[str]:
47
+ def _get_type(repr_: JsonRepresentation) -> Optional[str]:
47
48
  t: Optional[str] = None
48
49
  if repr_.enumerationFormat:
49
50
  t = repr_.enumerationFormat.dataType
@@ -67,13 +68,15 @@ def _get_representation(
67
68
  ]:
68
69
  valid = cons.get(id_, [])
69
70
  codes = local.to_enumeration(cls, valid) if local else None
70
- dt = DataType(__get_type(local)) if local else None
71
+ dt = DataType(_get_type(local)) if local else None
71
72
  facets = local.to_facets() if local else None
72
73
  ab = local.to_array_def() if local else None
73
74
  return (dt, facets, codes, ab)
74
75
 
75
76
 
76
- def _get_concept_reference(component: Component) -> str:
77
+ def _get_concept_reference(
78
+ component: Union[Component, MetadataComponent],
79
+ ) -> str:
77
80
  if isinstance(component.concept, ItemReference):
78
81
  concept = (
79
82
  "urn:sdmx:org.sdmx.infomodel.conceptscheme."
@@ -95,7 +98,7 @@ def _get_concept_reference(component: Component) -> str:
95
98
 
96
99
 
97
100
  def _get_json_representation(
98
- comp: Component,
101
+ comp: Union[Component, MetadataComponent],
99
102
  ) -> Optional[JsonRepresentation]:
100
103
  enum = comp.local_enum_ref if comp.local_enum_ref else None
101
104
  return JsonRepresentation.from_model(
@@ -0,0 +1,88 @@
1
+ """Collection of SDMX-JSON schemas for dataflow queries."""
2
+
3
+ from typing import Sequence
4
+
5
+ from msgspec import Struct
6
+
7
+ from pysdmx import errors
8
+ from pysdmx.io.json.sdmxjson2.messages.core import (
9
+ JsonAnnotation,
10
+ MaintainableType,
11
+ )
12
+ from pysdmx.model import Agency, Metadataflow, MetadataStructure
13
+
14
+
15
+ class JsonMetadataflow(MaintainableType, frozen=True, omit_defaults=True):
16
+ """SDMX-JSON payload for a metadataflow."""
17
+
18
+ structure: str = ""
19
+ targets: Sequence[str] = ()
20
+
21
+ def to_model(self) -> Metadataflow:
22
+ """Converts a FusionMetadataflow to a standard one."""
23
+ return Metadataflow(
24
+ id=self.id,
25
+ agency=self.agency,
26
+ name=self.name,
27
+ description=self.description,
28
+ version=self.version,
29
+ structure=self.structure,
30
+ targets=self.targets,
31
+ annotations=[a.to_model() for a in self.annotations],
32
+ is_external_reference=self.isExternalReference,
33
+ valid_from=self.validFrom,
34
+ valid_to=self.validTo,
35
+ )
36
+
37
+ @classmethod
38
+ def from_model(self, df: Metadataflow) -> "JsonMetadataflow":
39
+ """Converts a pysdmx metadataflow to an SDMX-JSON one."""
40
+ if not df.name:
41
+ raise errors.Invalid(
42
+ "Invalid input",
43
+ "SDMX-JSON metadataflows must have a name",
44
+ {"metadataflow": df.id},
45
+ )
46
+ if isinstance(df.structure, MetadataStructure):
47
+ dsdref = (
48
+ "urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure="
49
+ f"{df.structure.agency}:{df.structure.id}({df.structure.version})"
50
+ )
51
+ else:
52
+ dsdref = df.structure # type: ignore[assignment]
53
+ return JsonMetadataflow(
54
+ agency=(
55
+ df.agency.id if isinstance(df.agency, Agency) else df.agency
56
+ ),
57
+ id=df.id,
58
+ name=df.name,
59
+ version=df.version,
60
+ isExternalReference=df.is_external_reference,
61
+ validFrom=df.valid_from,
62
+ validTo=df.valid_to,
63
+ description=df.description,
64
+ annotations=tuple(
65
+ [JsonAnnotation.from_model(a) for a in df.annotations]
66
+ ),
67
+ structure=dsdref,
68
+ )
69
+
70
+
71
+ class JsonMetadataflows(Struct, frozen=True, omit_defaults=True):
72
+ """SDMX-JSON payload for the list of metadataflows."""
73
+
74
+ metadataflows: Sequence[JsonMetadataflow]
75
+
76
+ def to_model(self) -> Sequence[Metadataflow]:
77
+ """Returns the requested metadataflows."""
78
+ return [mdf.to_model() for mdf in self.metadataflows]
79
+
80
+
81
+ class JsonMetadataflowsMessage(Struct, frozen=True, omit_defaults=True):
82
+ """SDMX-JSON payload for /metadataflow queries."""
83
+
84
+ data: JsonMetadataflows
85
+
86
+ def to_model(self) -> Sequence[Metadataflow]:
87
+ """Returns the requested dataflows."""
88
+ return self.data.to_model()
@@ -0,0 +1,88 @@
1
+ """Collection of SDMX-JSON schemas for provision agreements."""
2
+
3
+ from typing import Sequence
4
+
5
+ from msgspec import Struct
6
+
7
+ from pysdmx import errors
8
+ from pysdmx.io.json.sdmxjson2.messages.core import (
9
+ JsonAnnotation,
10
+ MaintainableType,
11
+ )
12
+ from pysdmx.model import Agency, MetadataProvisionAgreement
13
+
14
+
15
+ class JsonMetadataProvisionAgreement(
16
+ MaintainableType, frozen=True, omit_defaults=True
17
+ ):
18
+ """SDMX-JSON payload for a metadata provision agreement."""
19
+
20
+ metadataflow: str = ""
21
+ metadataProvider: str = ""
22
+
23
+ def to_model(self) -> MetadataProvisionAgreement:
24
+ """Converts a FusionPA to a standard metadata provision agreement."""
25
+ return MetadataProvisionAgreement(
26
+ id=self.id,
27
+ agency=self.agency,
28
+ name=self.name,
29
+ description=self.description,
30
+ version=self.version,
31
+ valid_from=self.validFrom,
32
+ valid_to=self.validTo,
33
+ metadataflow=self.metadataflow,
34
+ metadata_provider=self.metadataProvider,
35
+ annotations=tuple([a.to_model() for a in self.annotations]),
36
+ is_external_reference=self.isExternalReference,
37
+ )
38
+
39
+ @classmethod
40
+ def from_model(
41
+ self, mpa: MetadataProvisionAgreement
42
+ ) -> "JsonMetadataProvisionAgreement":
43
+ """Converts a pysdmx metadata provision agreement to SDMX-JSON."""
44
+ if not mpa.name:
45
+ raise errors.Invalid(
46
+ "Invalid input",
47
+ "SDMX-JSON metadata provision agreements must have a name",
48
+ {"metadata_provision_agreement": mpa.id},
49
+ )
50
+ return JsonMetadataProvisionAgreement(
51
+ agency=(
52
+ mpa.agency.id if isinstance(mpa.agency, Agency) else mpa.agency
53
+ ),
54
+ id=mpa.id,
55
+ name=mpa.name,
56
+ version=mpa.version,
57
+ isExternalReference=mpa.is_external_reference,
58
+ validFrom=mpa.valid_from,
59
+ validTo=mpa.valid_to,
60
+ description=mpa.description,
61
+ annotations=tuple(
62
+ [JsonAnnotation.from_model(a) for a in mpa.annotations]
63
+ ),
64
+ metadataflow=mpa.metadataflow,
65
+ metadataProvider=mpa.metadata_provider,
66
+ )
67
+
68
+
69
+ class JsonMetadataProvisionAgreements(Struct, frozen=True, omit_defaults=True):
70
+ """SDMX-JSON payload for provision agreements."""
71
+
72
+ metadataProvisionAgreements: Sequence[JsonMetadataProvisionAgreement]
73
+
74
+ def to_model(self) -> Sequence[MetadataProvisionAgreement]:
75
+ """Returns the requested metadata provision agreements."""
76
+ return [pa.to_model() for pa in self.metadataProvisionAgreements]
77
+
78
+
79
+ class JsonMetadataProvisionAgreementsMessage(
80
+ Struct, frozen=True, omit_defaults=True
81
+ ):
82
+ """SDMX-JSON payload for /metadataprovisionagreement queries."""
83
+
84
+ data: JsonMetadataProvisionAgreements
85
+
86
+ def to_model(self) -> Sequence[MetadataProvisionAgreement]:
87
+ """Returns the requested metadata provision agreements."""
88
+ return self.data.to_model()