Commit 8aae3c41 authored by Sylvain Thénault's avatar Sylvain Thénault
Browse files

[profile gen] Support for multiple concepts on mime type and format id

since this may now occurs with values deducted from file category. In such case,
generates proper rng:choices / xsd:enumeration.

Related to #36331831
parent a9d8ac0426d6
......@@ -345,8 +345,8 @@ class SEDASeqStorageRuleRule(RuleRuleMixIn, generated.SEDASeqStorageRuleRule):
class SEDAFormatId(generated.SEDAFormatId):
@property
def concept(self):
return self.seda_format_id_to[0] if self.seda_format_id_to else None
def concepts(self):
return self.seda_format_id_to
class SEDAEncoding(generated.SEDAEncoding):
......@@ -359,8 +359,8 @@ class SEDAEncoding(generated.SEDAEncoding):
class SEDAMimeType(generated.SEDAMimeType):
@property
def concept(self):
return self.seda_mime_type_to[0] if self.seda_mime_type_to else None
def concepts(self):
return self.seda_mime_type_to
class SEDAType(generated.SEDAType):
......
......@@ -230,7 +230,12 @@ class RNGMixin(object):
datatype = 'string'
type_attrs = {'type': datatype}
if fixed_value is not None:
self.element('rng:value', element, type_attrs, text=fixed_value)
if isinstance(fixed_value, list):
choice = self.element('rng:choice', element)
for value in fixed_value:
self.element('rng:value', choice, type_attrs, text=value)
else:
self.element('rng:value', element, type_attrs, text=fixed_value)
elif default_value is not None:
element.attrib[self.qname('a:defaultValue')] = default_value
self.element('rng:data', element, type_attrs)
......@@ -548,6 +553,8 @@ class SEDA2RelaxNGExport(RNGMixin, SEDA2ExportAdapter):
# of label of its concept value
if value is not None and xselement.local_name == 'KeywordReference':
fixed_value = self.cwuri_url(value)
elif isinstance(value, list):
fixed_value = [serialize(val, self.cwuri_url) for val in value]
else:
fixed_value = serialize(value, self.cwuri_url)
if fixed_value is not None:
......@@ -560,7 +567,12 @@ class SEDA2RelaxNGExport(RNGMixin, SEDA2ExportAdapter):
xstype = profile_element[-1].attrib.get('type')
profile_element.remove(profile_element[-1])
attrs = {'type': xstype} if xstype else {}
self.element('rng:value', profile_element, attrs, text=fixed_value)
if isinstance(fixed_value, list):
choice = self.element('rng:choice', profile_element)
for val in fixed_value:
self.element('rng:value', choice, attrs, text=val)
else:
self.element('rng:value', profile_element, attrs, text=fixed_value)
elif xstype is not None:
self.element('rng:data', profile_element, {'type': xstype})
......@@ -639,8 +651,12 @@ class XAttr(namedtuple('_XAttr', ['name', 'qualified_type', 'cardinality', 'fixe
"""
def __new__(cls, name, qualified_type, cardinality='0..1', fixed_value=None):
assert cardinality in (None, '1', '0..1'), cardinality
if fixed_value is not None:
if fixed_value:
cardinality = '1'
if isinstance(fixed_value, list) and len(fixed_value) == 1:
fixed_value = fixed_value[0]
else:
fixed_value = None
return super(XAttr, cls).__new__(cls, name, qualified_type, cardinality, fixed_value)
......@@ -717,16 +733,24 @@ class SEDA1XSDExport(SEDA2ExportAdapter):
return children_parent
def attribute_schema(self, parent, xattr):
attrs = {'name': xattr.name, 'type': xattr.qualified_type}
attrs = {'name': xattr.name}
if xattr.cardinality is None:
attrs['use'] = 'prohibited'
elif xattr.cardinality == '1':
attrs['use'] = 'required'
else:
attrs['use'] = 'optional'
if xattr.fixed_value is not None:
attrs['fixed'] = text_type(xattr.fixed_value)
self.element('xsd:attribute', parent, attrs)
if not isinstance(xattr.fixed_value, list):
attrs['type'] = xattr.qualified_type
if isinstance(xattr.fixed_value, string_types):
attrs['fixed'] = text_type(xattr.fixed_value)
attribute_element = self.element('xsd:attribute', parent, attrs)
if isinstance(xattr.fixed_value, list):
type_element = self.element('xsd:simpleType', attribute_element)
restriction_element = self.element('xsd:restriction', type_element,
{'base': 'xsd:token'})
for value in xattr.fixed_value:
self.element('xsd:enumeration', restriction_element, {'value': value})
# business visit methods #######################################################################
......@@ -858,20 +882,24 @@ class SEDA1XSDExport(SEDA2ExportAdapter):
_safe_concept = partial(_safe_concept_value, concepts_language=self.concepts_language)
format_id = data_object.format_id
encoding = data_object.encoding
format_ids = [_concept_value(concept, self.concepts_language)
for concept in format_id.concepts]
mime_type = data_object.mime_type
mime_types = [_concept_value(concept, self.concepts_language)
for concept in mime_type.concepts]
encoding = data_object.encoding
self.element_schema(parent, 'Attachment', 'qdt:ArchivesBinaryObjectType',
xsd_attributes=[
XAttr('format', 'clmDAFFileTypeCode:FileTypeCodeType',
cardinality=_safe_cardinality(format_id),
fixed_value=_safe_concept(format_id)),
cardinality=format_id.user_cardinality,
fixed_value=format_ids),
XAttr('encodingCode',
'clm60133:CharacterSetEncodingCodeContentType',
cardinality=_safe_cardinality(encoding),
fixed_value=_safe_concept(encoding)),
XAttr('mimeCode', 'clmIANAMIMEMediaType:MIMEMediaTypeContentType',
cardinality=_safe_cardinality(mime_type),
fixed_value=_safe_concept(mime_type)),
cardinality=mime_type.user_cardinality,
fixed_value=mime_types),
XAttr('filename', 'xsd:string',
cardinality='0..1',
fixed_value=data_object.filename),
......@@ -1384,7 +1412,12 @@ def _complex_path_target_values(entity, path):
rtype_targets.append((entity, None))
continue
if targets:
rtype_targets += [(entity, t) for t in targets]
if len(targets) > 1 and targets[0].cw_etype == 'Concept':
# same element with several allowed values
rtype_targets += [(entity, targets)]
else:
# different children
rtype_targets += [(entity, t) for t in targets]
# if relation is not composite, that means it's a "value" relation, hence we should
# always emit a value (its associated XSD element must be defined)
elif not rdefschema.composite:
......
......@@ -384,7 +384,10 @@
</rng:optional>
<rng:element name="Attachment">
<rng:attribute name="format">
<rng:value type="string">fmt/123</rng:value>
<rng:choice>
<rng:value type="string">fmt/123</rng:value>
<rng:value type="string">fmt/987</rng:value>
</rng:choice>
</rng:attribute>
<rng:attribute name="encodingCode">
<rng:value type="string">6</rng:value>
......
......@@ -284,7 +284,14 @@
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="qdt:ArchivesBinaryObjectType">
<xsd:attribute fixed="fmt/123" name="format" type="clmDAFFileTypeCode:FileTypeCodeType" use="required"/>
<xsd:attribute name="format" use="required">
<xsd:simpleType>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="fmt/123"/>
<xsd:enumeration value="fmt/987"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute fixed="6" name="encodingCode" type="clm60133:CharacterSetEncodingCodeContentType" use="required"/>
<xsd:attribute name="mimeCode" type="clmIANAMIMEMediaType:MIMEMediaTypeContentType" use="optional"/>
<xsd:attribute fixed="this_is_the_filename.pdf" name="filename" type="xsd:string" use="required"/>
......
......@@ -531,7 +531,10 @@
</rng:optional>
<rng:element name="Attachment">
<rng:attribute name="format">
<rng:value type="string">fmt/123</rng:value>
<rng:choice>
<rng:value type="string">fmt/123</rng:value>
<rng:value type="string">fmt/987</rng:value>
</rng:choice>
</rng:attribute>
<rng:attribute name="encodingCode">
<rng:value type="string">6</rng:value>
......
......@@ -429,7 +429,14 @@
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="qdt:ArchivesBinaryObjectType">
<xsd:attribute fixed="fmt/123" name="format" type="clmDAFFileTypeCode:FileTypeCodeType" use="required"/>
<xsd:attribute name="format" use="required">
<xsd:simpleType>
<xsd:restriction base="xsd:token">
<xsd:enumeration value="fmt/123"/>
<xsd:enumeration value="fmt/987"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute fixed="6" name="encodingCode" type="clm60133:CharacterSetEncodingCodeContentType" use="required"/>
<xsd:attribute name="mimeCode" type="clmIANAMIMEMediaType:MIMEMediaTypeContentType" use="optional"/>
<xsd:attribute fixed="this_is_the_filename.pdf" name="filename" type="xsd:string" use="required"/>
......
......@@ -533,6 +533,27 @@ class SEDA2RNGExportTC(RelaxNGTestMixin, CubicWebTC):
{'name': 'listURI', 'use': 'optional', 'type': 'xsd:anyURI'},
{'name': 'listVersionID', 'use': 'optional', 'type': 'xsd:token'}])
def test_multiple_concepts(self):
with self.admin_access.cnx() as cnx:
transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
scheme = testutils.scheme_for_type(cnx, None, None,
u'application/msword', u'application/pdf')
cnx.create_entity('SEDAMimeTypeCodeListVersion',
seda_mime_type_code_list_version_from=transfer,
seda_mime_type_code_list_version_to=scheme)
bdo = testutils.create_data_object(transfer)
bdo.mime_type.cw_set(user_cardinality=u'1',
seda_mime_type_to=scheme.reverse_in_scheme)
profile = self.profile_etree(transfer)
mt = self.get_element(profile, 'MimeType')
self.assertEqual('\n'.join(etree.tostring(mt, pretty_print=True).splitlines()[1:-1]),
'''\
<rng:choice>
<rng:value type="token">application/msword</rng:value>
<rng:value type="token">application/pdf</rng:value>
</rng:choice>''')
def test_seda2_concept(self):
with self.admin_access.cnx() as cnx:
create = cnx.create_entity
......@@ -710,18 +731,22 @@ class OldSEDAExportMixin(object):
create = cnx.create_entity
concepts = {}
for rtype, etype, value in [
('seda_format_id_to', None, u'fmt/123'),
('seda_encoding_to', None, u'6'),
('seda_type_to', None, u'CDO'),
('seda_description_level', None, u'file'),
('seda_algorithm', 'SEDABinaryDataObject', u'md5'),
('seda_rule', 'SEDASeqAppraisalRuleRule', u'P10Y'),
('seda_rule', 'SEDASeqAccessRuleRule', u'AR038'),
('seda_final_action', 'SEDAAppraisalRule', u'detruire'),
for rtype, etype, labels in [
('seda_format_id_to', None, [u'fmt/123', u'fmt/987']),
('seda_encoding_to', None, [u'6']),
('seda_type_to', None, [u'CDO']),
('seda_description_level', None, [u'file']),
('seda_algorithm', 'SEDABinaryDataObject', [u'md5']),
('seda_rule', 'SEDASeqAppraisalRuleRule', [u'P10Y']),
('seda_rule', 'SEDASeqAccessRuleRule', [u'AR038']),
('seda_final_action', 'SEDAAppraisalRule', [u'detruire']),
]:
scheme = testutils.scheme_for_type(cnx, rtype, etype, value)
concepts[value] = scheme.reverse_in_scheme[0]
scheme = testutils.scheme_for_type(cnx, rtype, etype, *labels)
if len(labels) == 1:
concepts[labels[0]] = scheme.reverse_in_scheme[0]
else:
for concept in scheme.reverse_in_scheme:
concepts[concept.label()] = concept
# ensure we're able to export concept with unexpected language code
concepts['md5'].preferred_label[0].cw_set(language_code=u'de')
......@@ -820,7 +845,7 @@ class OldSEDAExportMixin(object):
bdo.format_id.cw_set(
user_cardinality=u'1',
seda_format_id_to=concepts['fmt/123'])
seda_format_id_to=[concepts['fmt/123'], concepts['fmt/987']])
create('SEDAEncoding',
user_cardinality=u'1',
seda_encoding_from=bdo,
......
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