Commit abffca63 authored by Katia Saurfelt's avatar Katia Saurfelt
Browse files

feat: add DataObjectGroup on all [Binary|Physical]DataObject descendents of DataObjectPackage.

Use <rng:zeroOrMore/rng:choice> grouping all [Binary|Physical]DataObjects to avoid the problem
with [Binary|Physical]DataObject order (closes #35)

--HG--
branch : seda21
parent 3f22e0b6b094
Pipeline #96198 failed with stages
in 8 minutes and 58 seconds
......@@ -447,7 +447,7 @@ class SEDA2ExportAdapter(EntityAdapter):
class SEDA2RelaxNGExport(RNGMixin, SEDA2ExportAdapter):
"""Abstract Adapter to build a Relax NG representation of a SEDA profile, using SEDA 2.1
specification."""
specification."""
__regid__ = "SEDA-2.1.rng"
__abstract__ = True
......@@ -494,6 +494,26 @@ class SEDA2RelaxNGExport(RNGMixin, SEDA2ExportAdapter):
self.postprocess_dataobjectsreference(root, namespaces)
return root
def get_dataobject_nodes(self, dop, namespaces):
"""get all Binary|PhysicalDataObject of a DataObjectPackage"""
nodes = dop.xpath(
'./rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]',
namespaces=namespaces,
)
optional = dop.xpath(
'./rng:optional/rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]',
namespaces=namespaces,
)
oneormore = dop.xpath(
'./rng:oneOrMore/rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]',
namespaces=namespaces,
)
zeroormore = dop.xpath(
'./rng:zeroOrMore/rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]',
namespaces=namespaces,
)
return (nodes, optional, oneormore, zeroormore)
def postprocess_dataobjects(self, root, namespaces):
"""Insert rng:group or rng:DataObjectGroup node as parent of
[Binary|Physical]DataObject node to avoid forcing an order among them
......@@ -503,7 +523,7 @@ class SEDA2RelaxNGExport(RNGMixin, SEDA2ExportAdapter):
def postprocess_dataobjectsreference(self, root, namespaces):
"""transform DataObjectReferenceId into DataObjectGroupReferenceId
if wrap_dataobjects
if wrap_dataobjects
"""
return
......@@ -740,7 +760,6 @@ class SEDA22RelaxNGExport(SEDA2RelaxNGExport):
"""Insert rng:DataObjectGroup node as parent of [Binary|Physical]DataObject node
to avoid forcing an order among them
"""
# start by looking for the rng:element node for DataObjectPackage or its parent rng:optional
dops = root.xpath(
"/rng:grammar/rng:start/rng:element/"
'rng:element[@name="DataObjectPackage"]',
......@@ -752,26 +771,21 @@ class SEDA22RelaxNGExport(SEDA2RelaxNGExport):
'rng:optional/rng:element[@name="DataObjectPackage"]',
namespaces=namespaces,
)
if dops:
assert len(dops) == 1
dop = dops[0]
nodes = dop.xpath(
'rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]',
namespaces=namespaces,
)
opt_nodes = dop.xpath(
'rng:optional[rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]]',
namespaces=namespaces,
)
if nodes or opt_nodes:
# insert after definition of dop's id attribute
for node in chain(nodes, opt_nodes):
zeroormore = self.element("rng:zeroOrMore")
choice = self.element("rng:choice", zeroormore)
dop[0].addnext(zeroormore)
# find all DataObjectGroup [Binary|Physical]DataObjects descendents
nodes = self.get_dataobject_nodes(dop, namespaces)
if any(nodes):
zeroormore = self.element("rng:zeroOrMore")
choice = self.element("rng:choice", zeroormore)
dop[0].addnext(zeroormore)
for node in chain(*nodes):
parent = node.getparent()
# insert DataObjectGroup with id from [Physical|Binary]DataObject if found
group = self.element(
"rng:element", choice, attributes={"name": "DataObjectGroup"}
"rng:element", parent, attributes={"name": "DataObjectGroup"}
)
node_id = node.xpath(
'rng:attribute[@name="id"]/@xml:id[1]', namespaces=namespaces
......@@ -783,10 +797,14 @@ class SEDA22RelaxNGExport(SEDA2RelaxNGExport):
attr_element.attrib[self.qname("xml:id")] = f"g{node_id[0]}"
self.element("rng:data", attr_element, {"type": "ID"})
group.append(node)
if parent != dop:
choice.append(parent)
else:
choice.append(group)
def postprocess_dataobjectsreference(self, root, namespaces):
"""transform DataObjectReferenceId into DataObjectGroupReferenceId
if wrap_dataobjects
if wrap_dataobjects
"""
# DataObjectReferenceId into DataObjectGroupReferenceId
reference_nodes = root.xpath(
......@@ -796,9 +814,11 @@ class SEDA22RelaxNGExport(SEDA2RelaxNGExport):
)
for reference_node in reference_nodes:
reference_node.attrib["name"] = "DataObjectGroupReferenceId"
node_id = reference_node.xpath('@a:defaultValue[1]', namespaces=namespaces)
node_id = reference_node.xpath("@a:defaultValue[1]", namespaces=namespaces)
# change defaultValue from BinaryDataObject xml:id to DataObjectGroup xml:id
reference_node.attrib[f"{{{namespaces['a']}}}defaultValue"] = f"g{node_id[0]}"
reference_node.attrib[
f"{{{namespaces['a']}}}defaultValue"
] = f"g{node_id[0]}"
class SEDA21RelaxNGExport(SEDA2RelaxNGExport):
......@@ -823,20 +843,17 @@ class SEDA21RelaxNGExport(SEDA2RelaxNGExport):
if dops:
assert len(dops) == 1
dop = dops[0]
nodes = dop.xpath(
'rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]',
namespaces=namespaces,
)
opt_nodes = dop.xpath(
'rng:optional[rng:element[@name="BinaryDataObject" or @name="PhysicalDataObject"]]',
namespaces=namespaces,
)
if nodes or opt_nodes:
group = self.element("rng:group")
# insert after definition of dop's id attribute
dop[0].addnext(group)
for node in chain(nodes, opt_nodes):
group.append(node)
nodes = self.get_dataobject_nodes(dop, namespaces)
if any(nodes):
zeroormore = self.element("rng:zeroOrMore")
choice = self.element("rng:choice", zeroormore)
dop[0].addnext(zeroormore)
for node in chain(*nodes):
parent = node.getparent()
if parent != dop:
choice.append(parent)
else:
choice.append(node)
class XAttr(
......
......@@ -501,36 +501,44 @@ class SEDA2RNGExportTC(RelaxNGTestMixin, CubicWebTC):
profile = self.profile_etree(transfer)
dop = self.get_element(profile, "DataObjectPackage")
self.assertEqual(len(self.xpath(dop, "./rng:group/*")), 3)
self.assertEqual(len(self.xpath(dop, "./rng:zeroOrMore/rng:choice/*")), 3)
# setting some cardinality to 1 will remove rng:optional parent of the DataObjectPackage
# and BinaryDataObject nodes
bdo.cw_set(user_cardinality=u"1")
profile = self.profile_etree(transfer)
dop = self.get_element(profile, "DataObjectPackage")
self.assertEqual(len(self.xpath(dop, "./rng:group/*")), 3)
self.assertEqual(len(self.xpath(dop, "./rng:zeroOrMore/rng:choice/*")), 3)
def test_object_data_object_group(self):
def test_object_data_object_with_group(self):
with self.admin_access.cnx() as cnx:
transfer = cnx.create_entity(
"SEDAArchiveTransfer", title=u"test profile", wrap_dataobjects=True
)
bdo = cnx.create_entity(
cnx.create_entity(
"SEDABinaryDataObject",
user_cardinality="1",
user_annotation=u"I am number one",
seda_binary_data_object=transfer,
)
cnx.create_entity(
"SEDABinaryDataObject",
"SEDAPhysicalDataObject",
user_cardinality="1..n",
user_annotation=u"I am number two",
seda_physical_data_object=transfer,
)
cnx.create_entity(
"SEDABinaryDataObject",
user_cardinality="0..1",
user_annotation=u"I am number one",
seda_binary_data_object=transfer,
)
cnx.create_entity(
"SEDAPhysicalDataObject",
user_annotation=u"I am number three",
user_cardinality="0..n",
user_annotation=u"I am number two",
seda_physical_data_object=transfer,
)
profile = self.profile_etree(transfer)
dop = self.get_element(profile, "DataObjectPackage")
self.assertEqual(
......@@ -541,43 +549,110 @@ class SEDA2RNGExportTC(RelaxNGTestMixin, CubicWebTC):
'rng:element[@name="BinaryDataObject"]',
)
),
2,
1,
)
self.assertEqual(
len(
self.xpath(
dop,
'./rng:zeroOrMore/rng:choice/rng:element[@name="DataObjectGroup"]'
'./rng:zeroOrMore/rng:choice/rng:oneOrMore/rng:element[@name="DataObjectGroup"]' # noqa
'/rng:element[@name="PhysicalDataObject"]',
)
),
1,
)
# setting some cardinality to 1 will remove rng:optional parent of the DataObjectPackage
# and BinaryDataObject nodes
bdo.cw_set(user_cardinality=u"1")
self.assertEqual(
len(
self.xpath(
dop,
'./rng:zeroOrMore/rng:choice/rng:zeroOrMore/rng:element[@name="DataObjectGroup"]/' # noqa
'rng:element[@name="PhysicalDataObject"]',
)
),
1,
)
self.assertEqual(
len(
self.xpath(
dop,
'./rng:zeroOrMore/rng:choice/rng:optional/rng:element[@name="DataObjectGroup"]' # noqa
'/rng:element[@name="BinaryDataObject"]',
)
),
1,
)
def test_object_data_object_without_group(self):
with self.admin_access.cnx() as cnx:
transfer = cnx.create_entity(
"SEDAArchiveTransfer", title=u"test profile", wrap_dataobjects=False
)
cnx.create_entity(
"SEDABinaryDataObject",
user_cardinality="1",
user_annotation=u"I am number one",
seda_binary_data_object=transfer,
)
cnx.create_entity(
"SEDAPhysicalDataObject",
user_cardinality="1..n",
user_annotation=u"I am number two",
seda_physical_data_object=transfer,
)
cnx.create_entity(
"SEDABinaryDataObject",
user_cardinality="0..1",
user_annotation=u"I am number one",
seda_binary_data_object=transfer,
)
cnx.create_entity(
"SEDAPhysicalDataObject",
user_cardinality="0..n",
user_annotation=u"I am number two",
seda_physical_data_object=transfer,
)
profile = self.profile_etree(transfer)
dop = self.get_element(profile, "DataObjectPackage")
self.assertEqual(
len(
self.xpath(
dop,
'./rng:zeroOrMore/rng:choice/rng:element[@name="DataObjectGroup"]'
'/rng:element[@name="BinaryDataObject"]',
'./rng:zeroOrMore/rng:choice/rng:element[@name="BinaryDataObject"]',
)
),
2,
1,
)
self.assertEqual(
len(
self.xpath(
dop,
'./rng:zeroOrMore/rng:choice/rng:element[@name="DataObjectGroup"]'
"./rng:zeroOrMore/rng:choice/rng:oneOrMore/"
'/rng:element[@name="PhysicalDataObject"]',
)
),
1,
)
self.assertEqual(
len(
self.xpath(
dop,
"./rng:zeroOrMore/rng:choice/rng:zeroOrMore/"
'rng:element[@name="PhysicalDataObject"]',
)
),
1,
)
self.assertEqual(
len(
self.xpath(
dop,
"./rng:zeroOrMore/rng:choice/rng:optional/"
'/rng:element[@name="BinaryDataObject"]',
)
),
1,
)
def test_transfer_annotation(self):
with self.admin_access.cnx() as cnx:
......@@ -993,7 +1068,17 @@ class SEDARNGExportFuncTC(SEDAExportFuncTCMixIn, RelaxNGTestMixin, CubicWebTC):
self.assertEqual(len(ddt), 7)
self.assertEqual(
set(elmt.attrib["type"] for elmt in ddt),
set(["date", "dateTime", "gDay", "gYearMonth", "gMonthDay", "gMonth", "gYear"])
set(
[
"date",
"dateTime",
"gDay",
"gYearMonth",
"gMonthDay",
"gMonth",
"gYear",
]
),
)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment