Skip to content

Commit

Permalink
Merge pull request #69 from laws-africa/pub-dates
Browse files Browse the repository at this point in the history
Use 0001-01-01 as a placeholder for None publication, amendment and repeal dates
  • Loading branch information
longhotsummer authored Apr 13, 2022
2 parents 84c6eb4 + 8fc721a commit 40015b1
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 16 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Change Log
- Cascade changes to FRBRlanguage into attachments
- Don't hardcode source
- Don't set ``contains="originalVersion"`` since it is the default value for that attribute.
- Use ``0001-01-01`` as a placeholder date for publication, amendment and repeal events with null dates

4.1.1
.....
Expand Down
24 changes: 19 additions & 5 deletions cobalt/akn.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,29 @@
}
DEFAULT_VERSION = '3.0'

# a placeholder date that indicates a null date, used in the XML where a date is required by may not be known
NULL_DATE = '0001-01-01'


def datestring(value):
""" Format a date as an XML-suitable string. If the date is None, uses NULL_DATE.
"""
if value is None:
return ""
return NULL_DATE
elif isinstance(value, str):
return value
else:
return "%04d-%02d-%02d" % (value.year, value.month, value.day)


def parsedate(value):
""" Parse an XML date string into a real date. If the value is the NULL_DATE, returns None.
"""
if value == NULL_DATE:
return None
return parse_date(value).date()


# Create a new objectify parser that doesn't remove blank text nodes
objectify_parser = etree.XMLParser()
objectify_parser.set_element_class_lookup(objectify.ObjectifyElementClassLookup())
Expand Down Expand Up @@ -91,7 +104,7 @@ def get_namespace(self):

raise ValueError(f"Expected to find one of the following Akoma Ntoso XML namespaces: {', '.join(akn_namespaces)}. Only these namespaces were found: {', '.join(namespaces)}")

def ensure_element(self, name, after, at=None):
def ensure_element(self, name, after, at=None, attribs=None):
""" Helper to get an element if it exists, or create it if it doesn't.
:param name: dotted path from `self` or `at`
Expand All @@ -101,7 +114,7 @@ def ensure_element(self, name, after, at=None):
node = self.get_element(name, root=at)
if node is None:
# TODO: what if nodes in the path don't exist?
node = self.make_element(name.split('.')[-1])
node = self.make_element(name.split('.')[-1], attribs)
after.addnext(node)

return node
Expand All @@ -123,8 +136,9 @@ def get_element(self, name, root=None):
return None
return node

def make_element(self, elem):
return getattr(self.maker, elem)()
def make_element(self, elem, attribs=None):
attribs = attribs or {}
return getattr(self.maker, elem)(**attribs)


class StructuredDocument(AkomaNtosoDocument):
Expand Down
22 changes: 11 additions & 11 deletions cobalt/hierarchical.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from iso8601 import parse_date

from .akn import StructuredDocument, datestring
from .akn import StructuredDocument, datestring, NULL_DATE, parsedate


class HierarchicalStructure(StructuredDocument):
Expand Down Expand Up @@ -33,7 +31,7 @@ def publication_name(self):
@publication_name.setter
def publication_name(self, value):
value = value or ""
pub = self.ensure_element('meta.publication', after=self.meta.identification)
pub = self.ensure_publication()
pub.set('name', value)
pub.set('showAs', value)

Expand All @@ -43,13 +41,12 @@ def publication_date(self):
"""
pub = self.get_element('meta.publication')
if pub is not None and pub.get('date'):
return parse_date(pub.get('date')).date()
return parsedate(pub.get('date'))
return None

@publication_date.setter
def publication_date(self, value):
self.ensure_element('meta.publication', after=self.meta.identification)\
.set('date', datestring(value))
self.ensure_publication().set('date', datestring(value))

@property
def publication_number(self):
Expand All @@ -60,15 +57,14 @@ def publication_number(self):

@publication_number.setter
def publication_number(self, value):
self.ensure_element('meta.publication', after=self.meta.identification)\
.set('number', value or "")
self.ensure_publication().set('number', value or "")

@property
def amendments(self):
amendments = []

for e in self.meta.iterfind(f'.//{{{self.namespace}}}lifecycle/{{{self.namespace}}}eventRef[@type="amendment"]'):
date = parse_date(e.get('date')).date()
date = parsedate(e.get('date'))
event = AmendmentEvent(date=date)
amendments.append(event)

Expand Down Expand Up @@ -132,7 +128,7 @@ def amendments(self, value):
def repeal(self):
e = self.meta.find(f'.//{{{self.namespace}}}lifecycle/{{{self.namespace}}}eventRef[@type="repeal"]')
if e is not None:
date = parse_date(e.get('date')).date()
date = parsedate(e.get('date'))
event = RepealEvent(date=date)

id = e.get('source')[1:]
Expand Down Expand Up @@ -183,6 +179,10 @@ def repeal(self, value):
except AttributeError:
pass

def ensure_publication(self):
return self.ensure_element('meta.publication', after=self.meta.identification,
attribs={'showAs': '', 'name': '', 'date': NULL_DATE})


class AmendmentEvent(object):
""" An event that amended a document.
Expand Down
12 changes: 12 additions & 0 deletions tests/test_act.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,43 @@ def test_empty_act(self):
def test_empty_body(self):
a = Act()
assert_not_equal(a.body.text, '')
assert_validates(a)

def test_publication_date(self):
a = Act()
assert_is_none(a.publication_date)
assert_validates(a)

a.publication_date = '2012-01-02'
assert_equal(datestring(a.publication_date), '2012-01-02')
assert_is_instance(a.publication_date, date)
assert_validates(a)

a.publication_date = None
assert_is_none(a.publication_date)
assert_validates(a)

def test_publication_number(self):
a = Act()
assert_is_none(a.publication_number)

a.publication_number = '1234'
assert_equal(a.publication_number, '1234')
assert_validates(a)

def test_publication_name(self):
a = Act()
assert_is_none(a.publication_name)

a.publication_name = 'Publication'
assert_equal(a.publication_name, 'Publication')
assert_validates(a)

def test_set_amendments(self):
a = Act()
a.frbr_uri = "/akn/za/act/1900/1"
a.amendments = [AmendmentEvent(date='2012-02-01', amending_uri='/za/act/1980/10', amending_title="Foo")]
assert_validates(a)

xml = a.to_xml(encoding='unicode', pretty_print=True)
xml = xml.replace(datestring(date.today()), 'TODAY')
Expand Down Expand Up @@ -104,6 +114,7 @@ def test_set_amendments(self):
AmendmentEvent(date='2012-02-01', amending_uri='/za/act/1980/22', amending_title="Corrected"),
AmendmentEvent(date='2013-03-03', amending_uri='/za/act/1990/5', amending_title="Bar"),
]
assert_validates(a)

xml = a.to_xml(encoding='unicode', pretty_print=True)
xml = xml.replace(datestring(date.today()), 'TODAY')
Expand Down Expand Up @@ -171,6 +182,7 @@ def test_set_amendments(self):

# clear them
a.amendments = []
assert_validates(a)
xml = a.to_xml(encoding='unicode', pretty_print=True)
xml = xml.replace(datestring(date.today()), 'TODAY')

Expand Down

0 comments on commit 40015b1

Please sign in to comment.