dataobject.py 13 KB
Newer Older
1
# copyright 2016-2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 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/>.
"""cubicweb-seda views for data objects (BinaryDataObject / PhysicalDataObject)"""

18
19
import json

20
21
from six import text_type

22
23
from logilab.mtconverter import xml_escape

Sylvain Thénault's avatar
Sylvain Thénault committed
24
from cubicweb import tags, view, _
25
from cubicweb.predicates import match_form_params, is_instance
26
from cubicweb.web.views import uicfg, tabs, ibreadcrumbs
27
28

from cubes.relationwidget import views as rwdg
29
from cubes.skos.views import widgets as skos
30

31
32
33
34
35
from ..xsd2yams import SCHEME_FROM_CONTAINER
from ..entities import parent_and_container, simplified_profile, full_seda2_profile
from . import rtags_from_xsd_element, add_subobject_link
from . import viewlib
from . import uicfg as sedauicfg  # noqa - ensure those rules are defined first
36

37

38
pvs = uicfg.primaryview_section
39
pvdc = uicfg.primaryview_display_ctrl
40
41
rec = uicfg.reledit_ctrl
afs = uicfg.autoform_section
42
43
affk = uicfg.autoform_field_kwargs

44

45
46
47
48
49
50
51
52
53
54
55
56
def scheme_rql_expr(container, etype, rtype):
    """Return RQL expression (right part of the WHERE) to retrieve the scheme (mapped as 'CS'
    variable) for some (entity type / relation type) couple, in the context of the given
    `container`.
    """
    try:
        scheme_from_container = SCHEME_FROM_CONTAINER[(etype, rtype)]
    except KeyError:
        scheme_from_container = SCHEME_FROM_CONTAINER[rtype]
    return scheme_from_container[container.cw_etype]


57
class ContainedRelationFacetWidget(rwdg.RelationFacetWidget):
58

59
    def _render_triggers(self, w, domid, form, field, rtype):
60
        req = form._cw
61
        parent, container = parent_and_container(form.edited_entity)
62
        assert container is not None
63
64
65
        rql_expr = scheme_rql_expr(container, form.edited_entity.cw_etype, field.name)
        if not req.execute('Any CS WHERE ' + rql_expr, {'container': container.eid}):
            scheme_relations = [x for x in rql_expr.split() if x.startswith('seda_')]
66
67
68
69
            if not scheme_relations:
                w(req._('there is no scheme available for this relation. '
                        'Contact the site administrator.'))
                return
70
71
            if len(scheme_relations) == 1:
                scheme_relation = req._(scheme_relations[0])
72
            else:
73
74
75
76
77
78
                scheme_relation = req.__(scheme_relations[0] + '_object')
            w(req._('you must specify a scheme for {0} to select a value').format(
                scheme_relation))
        else:
            return super(ContainedRelationFacetWidget, self)._render_triggers(
                w, domid, form, field, rtype)
79
80
81
82
83
84
85

    def trigger_search_url(self, entity, url_params):
        """Overriden to add information about who is the container

        This information will be used later for proper vocabulary computation.
        """
        # first retrieve the container entity
86
        _, container = parent_and_container(entity)
87
        assert container is not None
88
        # and put it as an extra url param
89
        url_params['container'] = text_type(container.eid)
90
91
92
        return super(ContainedRelationFacetWidget, self).trigger_search_url(entity, url_params)


93
94
95
96
97
98
99
100
101
102
for key in SCHEME_FROM_CONTAINER:
    try:
        etype, rtype = key
    except ValueError:
        etype = '*'
        rtype = key
    affk.tag_subject_of((etype, rtype, '*'),
                        {'widget': ContainedRelationFacetWidget(dialog_options={'width': 800})})


103
class ContainedSearchForRelatedEntitiesView(skos.SearchForRelatedConceptsView):
104

105
    __select__ = skos.SearchForRelatedConceptsView.__select__ & match_form_params('container')
106

107
    def constrained_rql(self):
108
109
110
        container = self._cw.entity_from_eid(int(self._cw.form['container']))
        rql_expr = scheme_rql_expr(container, self.schema_rdef.subject, self.schema_rdef.rtype)
        return rql_expr + ', O in_scheme CS', {'container': container.eid}
111
112


113
pvs.tag_object_of(('*', 'seda_data_object_reference_id', '*'),
114
                  'relations')
115
pvdc.tag_object_of(('*', 'seda_data_object_reference_id', '*'),
116
117
118
                   {'vid': 'autolimited',
                    'subvid': 'seda.object-ref.archive-unit',
                    'label': _('referenced by:')})
119

120
pvs.tag_subject_of(('*', 'seda_algorithm', '*'), 'attributes')
121
pvs.tag_object_of(('*', 'seda_target', '*'), 'hidden')  # in the relationship tab
122

123
124
125
126
127
128
for rtype in ('seda_compressed', 'seda_data_object_version_from'):
    # hide relation from autoform because of limitation of _container_eid
    afs.tag_object_of(('*', rtype, '*'), 'main', 'hidden')
    pvs.tag_object_of(('*', rtype, '*'), 'attributes')
    rec.tag_object_of(('*', rtype, '*'),
                      {'rvid': 'seda.reledit.complexlink',
129
                       'novalue_label': ' '})
130
131
132

rec.tag_subject_of(('SEDABinaryDataObject', 'seda_alt_binary_data_object_attachment', '*'),
                   {'rvid': 'seda.reledit.alternative',
133
                    'novalue_label': ' '})
134

135
136
rec.tag_object_of(('*', 'seda_compressed', '*'),
                  {'rvid': 'seda.reledit.text',
137
                   'novalue_label': ' '})
138

139

140
141
def uri_cardinality_vocabulary(form, field):
    req = form._cw
142
143
144
145
146
147
148
149
150
151
    if form.edited_entity.has_eid():
        parent_type = form.edited_entity.cw_adapt_to('IContained').parent.cw_etype
    else:
        try:
            # inlined creation form
            parent_type = json.loads(req.form['arg'][1])
        except KeyError:
            # edition through reledit
            parent_eid = req.form['eid']
            parent_type = req.describe(int(parent_eid))[0]
152
153
154
155
    if parent_type in ('SEDABinaryDataObject', 'SEDAAltBinaryDataObjectAttachment'):
        return [u'1']
    return [u'0..1', u'1']

156

157
158
159
160
affk.tag_attribute(('SEDAUri', 'user_cardinality'),
                   {'choices': uri_cardinality_vocabulary})


161
162
163
164
bdo_ordered_fields = [
    ('user_cardinality', 'subject'),
    ('user_annotation', 'subject'),
    ('filename', 'subject'),
165
    ('seda_date_created_by_application', 'object'),
166
167
168
169
170
171
172
173
    ('seda_compressed', 'object'),
    ('seda_data_object_version_from', 'object'),
    ('seda_algorithm', 'object'),
]
affk.set_fields_order('SEDABinaryDataObject', bdo_ordered_fields)
pvdc.set_fields_order('SEDABinaryDataObject', bdo_ordered_fields)


174
class BinaryDataObjectTabbedPrimaryView(tabs.TabbedPrimaryView):
175

176
    __select__ = is_instance('SEDABinaryDataObject')
177
178
    tabs = [
        'main_tab',
179
        _('seda_bdo_format_identification'),
180
        _('seda_bdo_file_information'),
181
        _('seda_do_relations'),
182
183
184
    ]


185
class BinaryDataObjectFormatIdentificationTab(viewlib.PrimaryTabWithoutBoxes):
186
    """Display format identification information of a binary data object"""
187

188
    __regid__ = 'seda_bdo_format_identification'
189
    __select__ = is_instance('SEDABinaryDataObject')
190
191
192
193

    rsection, display_ctrl = rtags_from_xsd_element('SEDABinaryDataObject', 'FormatIdentification')


194
195
196
197
198
class SimplifiedBinaryDataObjectFormatIdentificationTab(BinaryDataObjectFormatIdentificationTab):

    __select__ = BinaryDataObjectFormatIdentificationTab.__select__ & simplified_profile()

    rsection, display_ctrl = rtags_from_xsd_element('SEDABinaryDataObject', 'FormatIdentification')
199
    rsection.tag_object_of(('*', 'seda_format_litteral', 'SEDABinaryDataObject'), 'hidden')
200
201


202
class BinaryDataObjectFileInfoTab(viewlib.PrimaryTabWithoutBoxes):
203
    """Display file information of a binary data object"""
204

205
    __regid__ = 'seda_bdo_file_information'
206
    __select__ = is_instance('SEDABinaryDataObject') & full_seda2_profile()
207
208
209
210

    rsection, display_ctrl = rtags_from_xsd_element('SEDABinaryDataObject', 'FileInfo')


211
class PhysicalDataObjectTabbedPrimaryView(tabs.TabbedPrimaryView):
212

213
    __select__ = is_instance('SEDAPhysicalDataObject')
214
215
    tabs = [
        'main_tab',
216
        _('seda_pdo_dimensions'),
217
        _('seda_do_relations'),
218
    ]
219
220


221
class PhysicalDataObjectDimensionsTab(viewlib.PrimaryTabWithoutBoxes):
222
    """Display physical dimensions of a physical data object"""
223

224
    __regid__ = 'seda_pdo_dimensions'
225
    __select__ = is_instance('SEDAPhysicalDataObject')
226
227
228
229

    rsection, display_ctrl = rtags_from_xsd_element('SEDAPhysicalDataObject', 'PhysicalDimensions')


230
class DataObjectRelationsTab(viewlib.PrimaryTabWithoutBoxes):
231
    """Display relations of a binary or physical data object"""
232

233
    __regid__ = 'seda_do_relations'
234
235
    __select__ = (is_instance('SEDABinaryDataObject', 'SEDAPhysicalDataObject')
                  & full_seda2_profile())
236

237
238
239
240
241
    _('creating SEDARelationship (SEDARelationship seda_relationship '
      'SEDABinaryDataObject %(linkto)s)')
    _('creating SEDARelationship (SEDARelationship seda_relationship '
      'SEDAPhysicalDataObject %(linkto)s)')

242
243
244
245
246
247
248
249
250
251
252
253
    def entity_call(self, entity):
        rschema = self._cw.vreg.schema.rschema('seda_relationship')
        if rschema.has_perm(self._cw, 'add', toeid=entity.eid):
            urlparams = {'__redirectparams': 'tab=' + self.__regid__}
            self.w(add_subobject_link(entity, 'seda_relationship', 'object', urlparams,
                                      msg=self._cw._('add a SEDARelationship'),
                                      klass='btn btn-success pull-right'))
            self.w(tags.div(klass='clearfix'))
        rset = entity.related('seda_relationship', 'object')
        if rset:
            self._cw.view('list', rset=rset, parent=entity, w=self.w, tabid=self.__regid__,
                          subvid='seda.listitem')
254
255
256
257
258
259
260
        rset = entity.related('seda_target', 'object')
        if rset:
            self.w(u'<h2>{0}</h2>'.format(self._cw._('Relationship target of')))
            self.w(u'<div>{0}</div>'.format(
                self._cw._('This object is used as a relationship target of the following '
                           'entities')))
            self._cw.view('list', rset=rset, subvid='seda.relationship.reverse', w=self.w)
261
262
263


class RelationshipBusinessValueLinkEntityView(viewlib.BusinessValueLinkEntityView):
264

265
    __select__ = is_instance('SEDARelationship')
266

267
268
269
270
271
272
    def entity_value(self, entity):
        target = entity.seda_target[0] if entity.seda_target else None
        if target:
            value = tags.a(target.dc_title(), href=target.absolute_url())
        else:
            value = xml_escape(self._cw._('<no data-object specified>'))
273
274
        if entity.seda_type_relationship:
            concept = entity.seda_type_relationship[0]
275
            msg = self._cw._(', of relationship type %s') % concept.label()
276
        else:
277
278
279
            msg = self._cw._(', no relationship type specified')
        value += xml_escape(msg)
        return value
280
281


282
283
284
285
286
287
288
289
290
291
292
293
294
295
class CompressedBusinessValueEntityView(viewlib.BusinessValueEntityView):
    __select__ = is_instance('SEDACompressed')

    def entity_value(self, entity):
        if entity.compressed is None:
            value = self._cw.__('indifferent')
        else:
            value = self._cw.__('yes' if entity.compressed else 'no')
        if entity.seda_algorithm:
            algorithm = entity.seda_algorithm[0].label()
            value += self._cw._(u', using {algorithm}').format(algorithm=algorithm)
        return value


296
class RelationshipReverseEntityView(view.EntityView):
297

298
299
300
301
302
303
304
305
306
307
308
309
310
311
    __regid__ = 'seda.relationship.reverse'
    __select__ = is_instance('SEDARelationship')

    def entity_call(self, entity):
        target = entity.seda_relationship[0]
        self.w(tags.a(target.dc_title(), href=target.absolute_url()))
        if entity.seda_type_relationship:
            concept = entity.seda_type_relationship[0]
            msg = self._cw._(', of relationship type %s') % concept.label()
        else:
            msg = self._cw._(', no relationship type specified')
        self.w(xml_escape(msg))


312
class UnitBusinessValueEntityView(viewlib.BusinessValueEntityView):
313

314
    __select__ = is_instance('SEDAWidth', 'SEDAHeight', 'SEDADepth',
315
316
317
318
319
320
321
322
323
                             'SEDADiameter', 'SEDALength', 'SEDAThickness', 'SEDAWeight')

    def entity_call(self, entity):
        super(UnitBusinessValueEntityView, self).entity_call(entity)
        if entity.seda_unit:
            unit = self._cw._('unit: {0}').format(entity.seda_unit[0].label())
        else:
            unit = self._cw._('<no unit specified>')
        self.w(u' (%s)' % xml_escape(unit))
324
325
326
327
328
329
330
331
332
333


class IBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):
    """Override adapter from compound when BDO is within a simplified profile to display the archive
    unit as parent.
    """
    __select__ = is_instance('SEDABinaryDataObject') & simplified_profile()

    def parent_entity(self):
        return self.entity.cw_adapt_to('ITreeBase').parent()