Add rules for "simplified profile must have one and only one access/appraisal rule"
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 14 Oct 2016 11:17:15 +0200
changeset 1833 8112742e0b48
parent 1784 a77fc051bb8a
child 1834 d3e384864e8f
Add rules for "simplified profile must have one and only one access/appraisal rule" Introduce a new 'simplified' fake format, indicating wether a profile may be simplified or not. Profiles where are/may not be simplified are not expected to be exportable to SEDA 1 / SEDA 0.2.
entities/diag.py
hooks.py
i18n/en.po
i18n/fr.po
test/test_diag.py
test/test_profile_generation.py
--- a/entities/diag.py	Thu Oct 13 12:49:48 2016 +0200
+++ b/entities/diag.py	Fri Oct 14 11:17:15 2016 +0200
@@ -22,7 +22,7 @@
 from cubicweb.selectors import is_instance
 
 
-ALL_FORMATS = frozenset(('SEDA 2.0', 'SEDA 1.0', 'SEDA 0.2'))
+ALL_FORMATS = frozenset(('SEDA 2.0', 'SEDA 1.0', 'SEDA 0.2', 'simplified'))
 
 Rule = namedtuple('Rule', ['impacted_formats', 'message', 'tab_id', 'watch'])
 RULES = {
@@ -36,6 +36,46 @@
             'seda_archive_unit',
             'seda_alt_archive_unit_archive_unit_ref_id',
             'seda_seq_alt_archive_unit_archive_unit_ref_id_management',
+            'seda_access_rule',
+        ])),
+
+    'rule_without_rule': Rule(
+        set(['SEDA 1.0', 'SEDA 0.2', 'simplified']),
+        _("Some management rule has no inner rule, one is required."),
+        'seda_management_tab',
+        set([
+            'seda_seq_access_rule_rule',
+            'seda_seq_appraisal_rule_rule',
+        ])),
+    'rule_with_too_much_rules': Rule(
+        set(['SEDA 1.0', 'SEDA 0.2', 'simplified']),
+        _("Some management rule has more than one inner rules, a single one is required."),
+        'seda_management_tab',
+        set([
+            'seda_seq_access_rule_rule',
+            'seda_seq_appraisal_rule_rule',
+        ])),
+    'rule_unsupported_card': Rule(
+        set(['SEDA 1.0', 'SEDA 0.2', 'simplified']),
+        _("Inner rule has cardinality other than 1."),
+        'seda_management_tab',
+        set([
+            ('SEDASeqAccessRuleRule', 'user_cardinality'),
+            ('SEDASeqAppraisalRuleRule', 'user_cardinality'),
+        ])),
+    'rule_need_start_date': Rule(
+        set(['SEDA 1.0', 'SEDA 0.2', 'simplified']),
+        _("Inner rule has no start date."),
+        'seda_management_tab',
+        set([
+            'seda_start_date',
+        ])),
+    'rule_start_unsupported_card': Rule(
+        set(['SEDA 1.0', 'SEDA 0.2', 'simplified']),
+        _("Start date has cardinality other than 1."),
+        'seda_management_tab',
+        set([
+            ('SEDAStartDate', 'user_cardinality'),
         ])),
 }
 
@@ -87,3 +127,27 @@
                 seq = archive_unit.first_level_choice.content_sequence
                 if not seq.reverse_seda_access_rule:
                     yield 'seda1_need_access_rule', archive_unit
+        # Access/appraisal rule must have one and only one sequence, which have one and only one
+        # start date (and both of them must have 1 cardinality)
+        for rule in self._cw.execute(
+                'Any X WHERE X is IN (SEDAAccessRule, SEDAAppraisalRule), '
+                'X container C, C eid %(c)s', {'c': profile.eid}).entities():
+            if not rule.rules:
+                yield 'rule_without_rule', _parent(rule)
+            elif len(rule.rules) > 1:
+                yield 'rule_with_too_much_rules', _parent(rule)
+            else:
+                rule_seq = rule.rules[0]
+                if rule_seq.user_cardinality != '1':
+                    yield 'rule_unsupported_card', _parent(rule)
+                if not rule_seq.start_date:
+                    yield 'rule_need_start_date', _parent(rule)
+                elif rule_seq.start_date.user_cardinality != '1':
+                    yield 'rule_start_unsupported_card', _parent(rule)
+
+
+def _parent(entity):
+    """Return the first encountered parent which is an ArchiveUnit"""
+    while entity.cw_etype not in ('SEDAArchiveTransfer', 'SEDAArchiveUnit'):
+        entity = entity.cw_adapt_to('IContained').parent
+    return entity
--- a/hooks.py	Thu Oct 13 12:49:48 2016 +0200
+++ b/hooks.py	Fri Oct 14 11:17:15 2016 +0200
@@ -280,7 +280,10 @@
         CheckProfileSEDACompatiblityOp.get_instance(self._cw).add_entity(self.entity)
 
 
-WATCH_RTYPES_SET = set().union(*(rule.watch for rule in diag.RULES.values()))
+WATCH_RTYPES_SET = set().union(*((rtype for rtype in rule.watch
+                                  # filter out (entity type, attribute)
+                                  if not isinstance(rtype, tuple))
+                                 for rule in diag.RULES.values()))
 
 
 class AddOrRemoveChildrenHook(hook.Hook):
--- a/i18n/en.po	Thu Oct 13 12:49:48 2016 +0200
+++ b/i18n/en.po	Fri Oct 14 11:17:15 2016 +0200
@@ -119,6 +119,12 @@
 msgid "Impacted formats"
 msgstr ""
 
+msgid "Inner rule has cardinality other than 1."
+msgstr ""
+
+msgid "Inner rule has no start date."
+msgstr ""
+
 msgid "Message"
 msgstr ""
 
@@ -1840,6 +1846,13 @@
 msgstr ""
 
 msgid ""
+"Some management rule has more than one inner rules, a single one is required."
+msgstr ""
+
+msgid "Some management rule has no inner rule, one is required."
+msgstr ""
+
+msgid ""
 "Special relation from a concept scheme to a relation type, that may be used "
 "to restrict possible concept of a particular relation without depending on "
 "the scheme's name or other kind of ambiguous reference mecanism."
@@ -1851,6 +1864,9 @@
 "`scheme_relation_type`)."
 msgstr ""
 
+msgid "Start date has cardinality other than 1."
+msgstr ""
+
 #, python-brace-format
 msgid "Supported formats: {0}."
 msgstr ""
--- a/i18n/fr.po	Thu Oct 13 12:49:48 2016 +0200
+++ b/i18n/fr.po	Fri Oct 14 11:17:15 2016 +0200
@@ -125,6 +125,12 @@
 msgid "Impacted formats"
 msgstr "Formats impactés"
 
+msgid "Inner rule has cardinality other than 1."
+msgstr "Une règle à une cardinalité différente de 1."
+
+msgid "Inner rule has no start date."
+msgstr "Une règle n'a pas de date de départ."
+
 msgid "Message"
 msgstr "Message"
 
@@ -1846,6 +1852,13 @@
 msgstr "Séquence"
 
 msgid ""
+"Some management rule has more than one inner rules, a single one is required."
+msgstr "Un élément de gestion a plusieurs règles associées, une seule est requise."
+
+msgid "Some management rule has no inner rule, one is required."
+msgstr "Un élément de gestion n'a pas de règle associée, une seule est requise."
+
+msgid ""
 "Special relation from a concept scheme to a relation type, that may be used "
 "to restrict possible concept of a particular relation without depending on "
 "the scheme's name or other kind of ambiguous reference mecanism."
@@ -1857,6 +1870,9 @@
 "`scheme_relation_type`)."
 msgstr ""
 
+msgid "Start date has cardinality other than 1."
+msgstr "Une date de départ à une cardinality différente de 1."
+
 #, python-brace-format
 msgid "Supported formats: {0}."
 msgstr "Formats supportés : {0}."
--- a/test/test_diag.py	Thu Oct 13 12:49:48 2016 +0200
+++ b/test/test_diag.py	Fri Oct 14 11:17:15 2016 +0200
@@ -26,15 +26,49 @@
         with self.admin_access.repo_cnx() as cnx:
             transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'diagnosis testing')
             doctor = transfer.cw_adapt_to('ISEDACompatAnalyzer')
+            cnx.commit()
+            self.assertDiagnostic(doctor, ['SEDA 2.0', 'SEDA 1.0', 'SEDA 0.2', 'simplified'])
 
-            cnx.commit()
-            self.assertDiagnostic(doctor, ('SEDA 2.0', 'SEDA 1.0', 'SEDA 0.2'))
             unit, unit_alt, unit_alt_seq = create_archive_unit(transfer)
             transfer.cw_clear_all_caches()
             cnx.commit()
-            self.assertDiagnostic(doctor, ('SEDA 2.0', 'SEDA 0.2'), 'seda1_need_access_rule')
+            self.assertDiagnostic(doctor, ['SEDA 2.0', 'SEDA 0.2', 'simplified'],
+                                  'seda1_need_access_rule')
+
+            access_rule = cnx.create_entity('SEDAAccessRule', seda_access_rule=unit_alt_seq)
+            unit_alt_seq.cw_clear_all_caches()
+            cnx.commit()
+            self.assertDiagnostic(doctor, ['SEDA 2.0'], 'rule_without_rule')
+
+            access_rule_seq = cnx.create_entity('SEDASeqAccessRuleRule',
+                                                user_cardinality=u'1..n',
+                                                reverse_seda_seq_access_rule_rule=access_rule)
+            access_rule.cw_clear_all_caches()
+            cnx.commit()
+            self.assertDiagnostic(doctor, ['SEDA 2.0'],
+                                  'rule_unsupported_card', 'rule_need_start_date')
+
+            access_rule_seq.cw_set(user_cardinality=u'1')
+            start_date = cnx.create_entity('SEDAStartDate',
+                                           user_cardinality=u'0..1',
+                                           seda_start_date=access_rule_seq)
+            access_rule_seq.cw_clear_all_caches()
+            cnx.commit()
+            self.assertDiagnostic(doctor, ['SEDA 2.0'],
+                                  'rule_start_unsupported_card')
+
+            start_date.cw_set(user_cardinality=u'1')
+            start_date.cw_clear_all_caches()
+            access_rule_seq = cnx.create_entity('SEDASeqAccessRuleRule',
+                                                user_cardinality=u'1..n',
+                                                reverse_seda_seq_access_rule_rule=access_rule)
+            access_rule.cw_clear_all_caches()
+            cnx.commit()
+            self.assertDiagnostic(doctor, ['SEDA 2.0'],
+                                  'rule_with_too_much_rules')
 
     def assertDiagnostic(self, doctor, expected_formats, *expected_rule_ids):
+        doctor.entity.cw_clear_all_caches()
         rule_ids = set(rule_id for rule_id, entity in doctor.failing_rules())
         self.assertEqual(rule_ids, set(expected_rule_ids))
         self.assertEqual(doctor.diagnose(), set(expected_formats))
--- a/test/test_profile_generation.py	Thu Oct 13 12:49:48 2016 +0200
+++ b/test/test_profile_generation.py	Fri Oct 14 11:17:15 2016 +0200
@@ -697,7 +697,8 @@
             create('SEDAAccessRule',  # XXX mandatory for seda 1.0
                    user_cardinality=u'1',
                    seda_access_rule=unit_alt_seq,
-                   seda_seq_access_rule_rule=create('SEDASeqAccessRuleRule'))
+                   seda_seq_access_rule_rule=create(
+                       'SEDASeqAccessRuleRule', reverse_seda_start_date=create('SEDAStartDate')))
 
             content = create('SEDAContent',
                              user_cardinality=u'1',
@@ -721,7 +722,8 @@
 
             appraisal_rule_rule = create('SEDASeqAppraisalRuleRule',
                                          seda_rule=concepts['P10Y'],
-                                         user_annotation=u"C'est dans 10ans je m'en irai")
+                                         user_annotation=u"C'est dans 10ans je m'en irai",
+                                         reverse_seda_start_date=create('SEDAStartDate'))
             create('SEDAAppraisalRule',
                    seda_appraisal_rule=unit_alt_seq,
                    seda_final_action=concepts['detruire'],
@@ -738,13 +740,16 @@
             cnx.create_entity('SEDATitle', seda_title=content)
 
             create('SEDAAppraisalRule',
-                   seda_appraisal_rule=subunit_alt_seq)
+                   seda_appraisal_rule=subunit_alt_seq,
+                   seda_seq_appraisal_rule_rule=create(
+                       'SEDASeqAppraisalRuleRule', reverse_seda_start_date=create('SEDAStartDate')))
 
             create('SEDAAccessRule',
                    user_cardinality=u'1',
                    user_annotation=u'restrict',
                    seda_access_rule=subunit_alt_seq,
                    seda_seq_access_rule_rule=create('SEDASeqAccessRuleRule',
+                                                    reverse_seda_start_date=create('SEDAStartDate'),
                                                     seda_rule=concepts['AR038']))
 
             # Add minimal document to first level archive