Skip to content

Commit

Permalink
Extend writer.xml for hierarchical ItemSchemes, Annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
khaeru committed May 5, 2020
1 parent cb4e354 commit 1e92dc7
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 21 deletions.
13 changes: 9 additions & 4 deletions sdmx/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def __add__(self, other):
result.localizations.update(other.localizations)
return result

def localized_default(self, locale):
def localized_default(self, locale=None):
"""Return the string in *locale*, or else the first defined."""
try:
return self.localizations[locale]
Expand Down Expand Up @@ -311,19 +311,24 @@ def __init__(self, *args, **kwargs):

# Add this Item as a child of its parent
parent = kwargs.get('parent', None)
if parent and self not in parent.child:
parent.child.append(self)
if parent:
parent.append_child(self)

# Add this Item as a parent of its children
for c in kwargs.get('child', []):
c.parent = self
self.append_child(c)

def __contains__(self, item):
"""Recursive containment."""
for c in self.child:
if item == c or item in c:
return True

def append_child(self, other):
if other not in self.child:
self.child.append(other)
other.parent = self

def get_child(self, id):
"""Return the child with the given *id*."""
for c in self.child:
Expand Down
13 changes: 12 additions & 1 deletion sdmx/tests/writer/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from sdmx.message import StructureMessage
from sdmx.model import Agency, Code, Codelist
from sdmx.model import Agency, Annotation, Code, Codelist


@pytest.fixture
Expand All @@ -25,6 +25,17 @@ def codelist():
id='B',
name={'en': 'Beginning of period'},
)
cl.items['B1'] = Code(
id='B1',
name={'en': 'Child code of B'},
)
cl.items['B'].append_child(cl.items['B1'])

cl.items['A'].annotations.append(Annotation(
id='A1',
type='NOTE',
text={'en': 'Text annotation on Code A.'},
))

return cl

Expand Down
74 changes: 58 additions & 16 deletions sdmx/writer/xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@


def Element(name, *args, **kwargs):
return _element_maker(qname(*name.split(':')), *args, **kwargs)
name = name.split(':')
name = qname(*name) if len(name) == 2 else name[0]
return _element_maker(name, *args, **kwargs)


Writer = BaseWriter('XML')
Expand All @@ -37,20 +39,50 @@ def write(obj, **kwargs):

# Utility functions

def nameable(obj, elem):
for locale, label in obj.name.localizations.items():
child = Element('com:Name', label)
def i11lstring(obj, name):
"""InternationalString.
Returns a list of elements with name `name`.
"""
elems = []

for locale, label in obj.localizations.items():
child = Element(name, label)
child.set(qname('xml', 'lang'), locale)
elem.append(child)
elems.append(child)

return elems


def maintainable(obj):
urn = sdmx.urn.make(obj)
elem = Element(f'str:{obj.__class__.__name__}', urn=urn)
nameable(obj, elem)
def annotable(obj, name, *args, **kwargs):
elem = Element(name, *args, **kwargs)

if len(obj.annotations):
e_anno = Element('com:Annotations')
e_anno.extend(Writer.recurse(a) for a in obj.annotations)
elem.append(e_anno)

return elem


def identifiable(obj, name, *args, **kwargs):
return annotable(obj, name, *args, id=obj.id, **kwargs)


def nameable(obj, name, *args, **kwargs):
elem = identifiable(obj, name, *args, **kwargs)
elem.extend(i11lstring(obj.name, 'com:Name'))
return elem


def maintainable(obj, parent=None):
return nameable(
obj,
f'str:{obj.__class__.__name__}',
urn=sdmx.urn.make(obj, parent),
)


@Writer.register
def _(obj: message.StructureMessage):
msg = Element('mes:Structure')
Expand All @@ -77,11 +109,21 @@ def _(obj: model.ItemScheme):

@Writer.register
def _(obj: model.Item, parent):
# NB this isn't correct: produces .Codelist instead of .Code
elem = Element(
f'str:{obj.__class__.__name__}',
id=obj.id,
urn=sdmx.urn.make(obj, parent),
)
nameable(obj, elem)
elem = maintainable(obj, parent=parent)

if obj.parent:
# Reference to parent code
e_parent = Element('str:Parent')
e_parent.append(Element('Ref', id=obj.parent.id))
elem.append(e_parent)

return elem


@Writer.register
def _(obj: model.Annotation):
elem = Element('com:Annotation')
if obj.id:
elem.attrib['id'] = obj.id
elem.extend(i11lstring(obj.text, 'com:AnnotationText'))
return elem

0 comments on commit 1e92dc7

Please sign in to comment.