xsd2uicfg.py 9.97 KB
Newer Older
1
# copyright 2016-2021 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# contact http://www.logilab.fr -- mailto:contact@logilab.fr
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Generate CubicWeb's uicfg rules from XSD file.

XSD parsing is done using generateDS, which has been copied into the `gends` directory (only the
used bits).
"""

22
from cubicweb import neg_role, _
23

24
25
from cubicweb_seda.xsd import XSDM_MAPPING
from cubicweb_seda.xsd2yams import CodeGenerator
26
27


28
29
FIRST_LEVEL_ETYPES = set(('SEDAArchiveTransfer',
                          'SEDABinaryDataObject', 'SEDAPhysicalDataObject',
30
                          'SEDAArchiveUnit'))
31

32
33
34
RTYPES_IN_TAB = set((
    'seda_binary_data_object',
    'seda_physical_data_object',
35
    'seda_related_transfer_reference',
36
    'seda_archive_unit',
37
    'seda_relationship',
38
39
40
41
42
43
44
    'seda_storage_rule',
    'seda_appraisal_rule',
    'seda_access_rule',
    'seda_dissemination_rule',
    'seda_reuse_rule',
    'seda_classification_rule',
    'seda_need_authorization',
45
46
47
    'seda_restriction_rule_id_ref',
    'seda_restriction_value',
    'seda_restriction_end_date',
48
))
49
for element_name in ('CodeListVersions', 'FormatIdentification', 'FileInfo', 'PhysicalDimensions',
50
                     'Gps', 'RelatedObjectReference', 'CustodialHistory', 'Coverage'):
51
52
    for rtype, role, path in XSDM_MAPPING.iter_rtype_role(element_name):
        RTYPES_IN_TAB.add(rtype)
53

54

55
56
57
class UICFGGenerator(CodeGenerator):
    """UICFG rules generator"""

58
    rtags_info = {
59
60
61
62
63
        'actionbox_appearsin_addmenu': {
            'shortname': 'abaa',
            'subject': "abaa.tag_subject_of(('*', '{rtype}', '*'), {value})",
            'object': "abaa.tag_object_of(('*', '{rtype}', '*'), {value})",
        },
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
        'autoform_section': {
            'shortname': 'afs',
            'subject': "afs.tag_subject_of(('*', '{rtype}', '*'), 'main', '{value}')",
            'object': "afs.tag_object_of(('*', '{rtype}', '*'), 'main', '{value}')",
        },
        'autoform_field_kwargs': {
            'shortname': 'affk',
            'subject': "affk.tag_subject_of(('*', '{rtype}', '*'), {value})",
            'object': "affk.tag_object_of(('*', '{rtype}', '*'), {value})",
        },
        'primaryview_section': {
            'shortname': 'pvs',
            'subject': "pvs.tag_subject_of(('*', '{rtype}', '*'), '{value}')",
            'object': "pvs.tag_object_of(('*', '{rtype}', '*'), '{value}')",
        },
79
80
81
        'reledit_ctrl': {
            'shortname': 'rec',
            'subject': "rec.tag_subject_of(('*', '{rtype}', '*'), "
82
            "{{'novalue_label': '{value}'}})",
83
            'object': "rec.tag_object_of(('*', '{rtype}', '*'), "
84
            "{{'novalue_label': '{value}'}})",
85
        },
86
    }
87

88
    def _generate(self, mapping, stream):
89
        self._processed = set()
90
        stream.write('from cubicweb.web import formwidgets as fw\n')
91
92
93
94
95
96
97
        stream.write('from cubicweb.web.views import uicfg\n\n')
        # indexview_etype_section configuration
        stream.write('ives = uicfg.indexview_etype_section\n')
        all_etypes = set()
        for mapping_element in mapping.ordered:
            etypes = self._callback('etypes_for', mapping_element)
            all_etypes.update(set(etypes))
98
        all_etypes.remove('SEDAArchiveTransfer')
99
100
        for etype in all_etypes:
            stream.write("ives['{0}'] = 'subobject'\n".format(etype))
101
        # autoform / primary view relation rtags
102
        for rtag, rtag_info in sorted(self.rtags_info.items()):
103
            alias = rtag_info['shortname']
104
105
106
107
108
            stream.write('\n\n{0} = uicfg.{1}\n'.format(rtag_info['shortname'], rtag))
            for mapping_element in mapping.ordered:
                for rtype, role, value in self._callback(rtag + '_for', mapping_element):
                    template = rtag_info[role]
                    stream.write(template.format(**locals()) + '\n')
109
110
111
112
113
114
        # fields order
        stream.write('pvds = uicfg.primaryview_display_ctrl\n')
        for mapping_element in mapping.ordered:
            for etype, attributes in self._callback('order_for', mapping_element):
                stream.write("affk.set_fields_order('{0}', {1})\n".format(etype, attributes))
                stream.write("pvds.set_fields_order('{0}', {1})\n".format(etype, attributes))
115
116
117
118
119
120
121
        # fields documentation
        stream.write('\nETYPE_ATTR_DOC = {}\n')
        template = "ETYPE_ATTR_DOC[('{etype}', '{rtype}', '{role}')] = ('{element}', {doc})\n"
        for mapping_element in mapping.ordered:
            for etype, rtype, role, element, doc in self._callback('doc_for', mapping_element):
                stream.write(template.format(etype=etype, rtype=rtype, role=role,
                                             element=element, doc=doc))
122
123

    def etypes_for_e_type_mapping(self, mapping):
124
        yield mapping.etype
125

126
    def autoform_section_for_rdef_mapping(self, mapping):
127
128
129
        if ('afs', mapping.rtype) in self._processed:
            return
        self._processed.add(('afs', mapping.rtype))
130
        if mapping.rtype in RTYPES_IN_TAB:
131
132
133
134
135
136
137
138
            section = 'hidden'
            role = mapping.composite or 'subject'
        elif mapping.composite:
            section = 'inlined'
            role = mapping.composite
        else:
            section = 'attributes'
            role = 'subject'
139
        yield mapping.rtype, neg_role(role), 'hidden'
140
        yield mapping.rtype, role, section
141

142
143
    def autoform_field_kwargs_for_e_type_mapping(self, mapping):
        for rtype, target_etype in sorted(mapping.attributes.items()):
144
145
            if rtype == 'id':
                continue
146
147
            if target_etype == 'String' and ('affk', rtype) not in self._processed:
                self._processed.add(('affk', rtype))
148
                yield rtype, 'subject', {'widget': Code("fw.TextInput({'size': 80})")}
149
150
151
            elif target_etype == 'Boolean' and ('aff', rtype) not in self._processed:
                self._processed.add(('aff', rtype))
                yield rtype, 'subject', {'allow_none': True}
152

153
    def autoform_field_kwargs_for_rdef_mapping(self, mapping):
154
155
        if mapping.rtype.endswith('code_list_version_to'):
            yield mapping.rtype, 'subject', {'label': 'value'}
156
157

    def primaryview_section_for_rdef_mapping(self, mapping):
158
159
160
        if ('pvs', mapping.rtype) in self._processed:
            return
        self._processed.add(('pvs', mapping.rtype))
161
        if mapping.rtype in RTYPES_IN_TAB:
162
163
164
165
            section = 'hidden'
            role = mapping.composite or 'subject'
            yield mapping.rtype, neg_role(role), section
            yield mapping.rtype, role, section
166
167
168
        elif ('Concept' in mapping.objtypes
              or 'ConceptScheme' in mapping.objtypes
              or 'AuthorityRecord' in mapping.objtypes):
169
            yield mapping.rtype, 'object', 'hidden'
170

171
172
    def reledit_ctrl_for_e_type_mapping(self, mapping):
        for rtype, target_etype in sorted(mapping.attributes.items()):
173
174
            if rtype == 'id':
                continue
175
176
            if ('rec', rtype) not in self._processed:
                self._processed.add(('rec', rtype))
177
                yield rtype, 'subject', _('<no value specified>')
178
179
180
181
182
183

    def reledit_ctrl_for_rdef_mapping(self, mapping):
        if ('rec', mapping.rtype) in self._processed:
            return
        self._processed.add(('rec', mapping.rtype))
        if mapping.composite is None:
184
            yield mapping.rtype, 'subject', '<no value specified>'
185
186
187
        else:
            card = mapping.card[mapping.composite == 'object']
            if card == '1':
188
                yield mapping.rtype, mapping.composite, _('<no value specified>')
189
            elif card == '?':
190
                yield mapping.rtype, mapping.composite, ' '
191

192
193
194
195
196
197
198
199
    def actionbox_appearsin_addmenu_for_rdef_mapping(self, mapping):
        if ('abaa', mapping.rtype) in self._processed:
            return
        self._processed.add(('abaa', mapping.rtype))
        role = mapping.composite or 'subject'
        yield mapping.rtype, neg_role(role), False
        yield mapping.rtype, role, False

200
    def order_for_e_type_mapping(self, mapping):
201
        attributes = ordered_attributes(mapping)
202
203
204
        if len(attributes) > 1:
            yield mapping.etype, attributes

205
206
207
208
209
210
211
    def doc_for_rdef_mapping(self, mapping):
        if mapping.element_name or mapping.desc:
            rtype = mapping.rtype
            yield mapping.subjtype, rtype, 'subject', mapping.element_name, mapping.desc
            for etype in mapping.objtypes:
                yield etype, rtype, 'object', mapping.element_name, mapping.desc

212

Noé Gaumont's avatar
Noé Gaumont committed
213
class Code(str):
214
215
    """Special string subclass whose repr() doesn't add quotes, for insertion of python code in a
    data structure
216
    """
217

218
219
220
221
    def __repr__(self):
        return str(self)


222
223
224
def ordered_attributes(mapping):
    """Given an ETypeMapping, return a list of its attributes sorted by desired order of appearance
    """
225
    attributes = [attr for attr in mapping.attributes if attr != 'id']
226
227
228
    if mapping.etype == 'SEDAArchiveTransfer':
        attributes.append('title')
        attributes.append('user_annotation')
229
230
231
    elif mapping.etype in ('SEDAArchiveUnit', 'SEDABinaryDataObject', 'SEDAPhysicalDataObject'):
        attributes.append('user_cardinality')
        attributes.append('user_annotation')
232
233
    elif attributes:
        if len(mapping.cards) > 1:
234
            attributes.insert(0, 'user_cardinality')
235
236
237
            attributes.append('user_annotation')
    return attributes

238

239
if __name__ == '__main__':
240
    UICFGGenerator.main()