Projet

Général

Profil

0001-backoffice-list-related-forms-and-cards-in-inspect-v.patch

Lauréline Guérin, 14 septembre 2020 14:18

Télécharger (18,8 ko)

Voir les différences:

Subject: [PATCH] backoffice: list related forms and cards in inspect view
 (#43359)

 tests/test_backoffice_pages.py | 231 ++++++++++++++++++++++++++++++++-
 wcs/backoffice/management.py   |  29 ++++-
 wcs/formdata.py                |  50 +++++++
 wcs/wf/external_workflow.py    |  32 +----
 4 files changed, 299 insertions(+), 43 deletions(-)
tests/test_backoffice_pages.py
3 3
import json
4 4
import os
5 5
import re
6
import shutil
7 6
import time
8
import hashlib
9 7
import random
10 8
import xml.etree.ElementTree as ET
11 9
import zipfile
......
18 16
except ImportError:
19 17
    xlwt = None
20 18

  
21
from django.utils import six
22 19
from django.utils.six import StringIO, BytesIO
23 20
from django.utils.six.moves.urllib import parse as urllib
24 21
from django.utils.six.moves.urllib import parse as urlparse
25 22

  
26 23
from webtest import Upload
27 24

  
28
from quixote import cleanup, get_publisher, get_response
25
from quixote import get_publisher
29 26
from quixote.http_request import Upload as QuixoteUpload
30 27
from wcs.qommon import ods
31 28
from wcs.api_utils import sign_url
32 29
from wcs.blocks import BlockDef
33
from wcs.qommon import errors, sessions
34 30
from wcs.qommon.form import PicklableUpload
35 31
from wcs.qommon.form import UploadedFile
36 32
from wcs.qommon.ident.password_accounts import PasswordAccount
37 33
from wcs.qommon.http_request import HTTPRequest
38
from wcs.qommon.substitution import CompatibilityNamesDict
39 34
from wcs.roles import Role, logged_users_role
40 35
from wcs.workflows import (Workflow, CommentableWorkflowStatusItem,
41 36
        ChoiceWorkflowStatusItem, EditableWorkflowStatusItem,
......
5261 5256

  
5262 5257

  
5263 5258
def test_inspect_page(pub, local_user):
5259
    if not pub.is_using_postgresql():
5260
        pytest.skip('this requires SQL')
5261
        return
5262

  
5264 5263
    create_user(pub)
5265 5264
    create_environment(pub)
5266 5265

  
......
5417 5416
    assert 'Invalid block tag' in resp.text
5418 5417

  
5419 5418

  
5419
def test_inspect_page_with_related_objects(pub):
5420
    if not pub.is_using_postgresql():
5421
        pytest.skip('this requires SQL')
5422
        return
5423

  
5424
    user = create_user(pub, is_admin=True)
5425

  
5426
    FormDef.wipe()
5427
    CardDef.wipe()
5428

  
5429
    # test ExternalWorkflowGlobalAction
5430
    external_wf = Workflow(name='External Workflow')
5431
    st1 = external_wf.add_status(name='New')
5432
    action = external_wf.add_global_action('Delete', 'delete')
5433
    action.append_item('remove')
5434
    trigger = action.append_trigger('webservice')
5435
    trigger.identifier = 'delete'
5436
    external_wf.store()
5437

  
5438
    external_formdef = FormDef()
5439
    external_formdef.name = 'External Form'
5440
    external_formdef.fields = [
5441
        fields.StringField(id='0', label='string', varname='form_string'),
5442
    ]
5443
    external_formdef.workflow = external_wf
5444
    external_formdef.store()
5445

  
5446
    external_carddef = CardDef()
5447
    external_carddef.name = 'External Card'
5448
    external_carddef.fields = [
5449
        fields.StringField(id='0', label='string', varname='card_string'),
5450
    ]
5451
    external_carddef.backoffice_submission_roles = user.roles
5452
    external_carddef.workflow = external_wf
5453
    external_carddef.store()
5454

  
5455
    wf = Workflow(name='External actions')
5456
    st1 = wf.add_status('Create external formdata')
5457
    create_formdata = CreateFormdataWorkflowStatusItem()
5458
    create_formdata.label = 'create linked form'
5459
    create_formdata.formdef_slug = external_formdef.url_name
5460
    create_formdata.varname = 'created_form'
5461
    create_formdata.id = '_create_form'
5462
    mappings = [
5463
        Mapping(field_id='0', expression='{{ form_var_string }}')
5464
    ]
5465
    create_formdata.mappings = mappings
5466
    create_formdata.parent = st1
5467

  
5468
    create_carddata = CreateCarddataWorkflowStatusItem()
5469
    create_carddata.label = 'create linked card'
5470
    create_carddata.formdef_slug = external_carddef.url_name
5471
    create_carddata.varname = 'created_card'
5472
    create_carddata.id = '_create_card'
5473
    create_carddata.mappings = mappings
5474
    create_carddata.parent = st1
5475

  
5476
    st1.items.append(create_formdata)
5477
    st1.items.append(create_carddata)
5478

  
5479
    global_action = wf.add_global_action('Delete external linked object', 'delete')
5480
    action = global_action.append_item('external_workflow_global_action')
5481
    action.slug = 'formdef:%s' % external_formdef.url_name
5482
    action.trigger_id = 'action:%s' % trigger.identifier
5483
    wf.store()
5484

  
5485
    formdef = FormDef()
5486
    formdef.name = 'External action form'
5487
    formdef.fields = [
5488
        fields.StringField(id='0', label='string', varname='string'),
5489
    ]
5490
    formdef.workflow = wf
5491
    formdef.store()
5492

  
5493
    carddef = CardDef()
5494
    carddef.name = 'External action card'
5495
    carddef.fields = [
5496
        fields.StringField(id='0', label='string', varname='string'),
5497
    ]
5498
    carddef.backoffice_submission_roles = user.roles
5499
    carddef.workflow = wf
5500
    carddef.store()
5501

  
5502
    assert formdef.data_class().count() == 0
5503
    assert carddef.data_class().count() == 0
5504
    assert external_formdef.data_class().count() == 0
5505
    assert external_carddef.data_class().count() == 0
5506

  
5507
    formdata = formdef.data_class()()
5508
    formdata.data = {'0': 'test form'}
5509
    formdata.user = user
5510
    formdata.store()
5511
    formdata.just_created()
5512
    formdata.perform_workflow()
5513

  
5514
    assert formdef.data_class().count() == 1
5515
    assert carddef.data_class().count() == 0
5516
    # related form and card
5517
    assert external_formdef.data_class().count() == 1
5518
    assert external_carddef.data_class().count() == 1
5519

  
5520
    app = login(get_app(pub))
5521
    resp = app.get('/backoffice/management/external-action-form/1/inspect')
5522
    assert 'Related Forms/Cards' in resp.text
5523
    # related form and card
5524
    assert '<li><a href="http://example.net/backoffice/management/external-form/1/">External Form #1-1</a></li>' in resp.text
5525
    assert '<li><a href="http://example.net/backoffice/data/external-card/1/">External Card #1-1</a></li>' in resp.text
5526
    # check related form
5527
    resp = app.get('/backoffice/management/external-form/1/')
5528
    assert '<h3>Original form</h3><p><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1</a></p>' in resp.text
5529
    resp = app.get('/backoffice/management/external-form/1/inspect')
5530
    # parent
5531
    assert '<li><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1</a></li>' in resp.text
5532
    # check related card
5533
    resp = app.get('/backoffice/data/external-card/1/')
5534
    assert '<h3>Original form</h3><p><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1</a></p>' in resp.text
5535
    resp = app.get('/backoffice/data/external-card/1/inspect')
5536
    # parent
5537
    assert '<li><a href="http://example.net/backoffice/management/external-action-form/1/">External action form #2-1</a></li>' in resp.text
5538

  
5539
    formdef.data_class().wipe()
5540
    carddef.data_class().wipe()
5541
    external_formdef.data_class().wipe()
5542
    external_carddef.data_class().wipe()
5543

  
5544
    carddata = carddef.data_class()()
5545
    carddata.data = {'0': 'test card'}
5546
    carddata.user = user
5547
    carddata.store()
5548
    carddata.just_created()
5549
    carddata.perform_workflow()
5550

  
5551
    assert formdef.data_class().count() == 0
5552
    assert carddef.data_class().count() == 1
5553
    # related form and card
5554
    assert external_formdef.data_class().count() == 1
5555
    assert external_carddef.data_class().count() == 1
5556

  
5557
    resp = app.get('/backoffice/data/external-action-card/1/inspect')
5558
    assert 'Related Forms/Cards' in resp.text
5559
    # related form and card
5560
    assert '<li><a href="http://example.net/backoffice/management/external-form/2/">External Form #1-2</a></li>' in resp.text
5561
    assert '<li><a href="http://example.net/backoffice/data/external-card/2/">External Card #1-2</a></li>' in resp.text
5562
    # check related form
5563
    resp = app.get('/backoffice/management/external-form/2/')
5564
    assert '<h3>Original card</h3><p><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1</a></p>' in resp.text
5565
    resp = app.get('/backoffice/management/external-form/2/inspect')
5566
    # parent
5567
    assert '<li><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1</a></li>' in resp.text
5568
    # check related card
5569
    resp = app.get('/backoffice/data/external-card/2/')
5570
    assert '<h3>Original card</h3><p><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1</a></p>' in resp.text
5571
    resp = app.get('/backoffice/data/external-card/2/inspect')
5572
    # parent
5573
    assert '<li><a href="http://example.net/backoffice/data/external-action-card/1/">External action card #2-1</a></li>' in resp.text
5574

  
5575
    FormDef.wipe()
5576
    CardDef.wipe()
5577
    Workflow.wipe()
5578

  
5579
    # test linked Card in datasource
5580
    wf = Workflow(name='WF')
5581
    st1 = wf.add_status('NEW')
5582
    wf.store()
5583
    wfc = Workflow(name='WFC')
5584
    wfc.add_status('NEW')
5585
    wfc.store()
5586

  
5587
    carddef = CardDef()
5588
    carddef.name = 'CARD A'
5589
    carddef.fields = [
5590
        fields.StringField(id='0', label='string', varname='string'),
5591
    ]
5592
    carddef.backoffice_submission_roles = user.roles
5593
    carddef.workflow = wfc
5594
    carddef.store()
5595

  
5596
    datasource = {'type': 'carddef:%s' % carddef.url_name}
5597
    formdef = FormDef()
5598
    formdef.name = 'FORM A'
5599
    formdef.fields = [
5600
        fields.ItemField(
5601
            id='0', label='Card',
5602
            type='item', varname='card',
5603
            data_source=datasource),
5604
    ]
5605
    formdef.workflow = wf
5606
    formdef.store()
5607

  
5608
    carddata = carddef.data_class()()
5609
    carddata.data = {'0': 'Text'}
5610
    carddata.store()
5611

  
5612
    formdata = formdef.data_class()()
5613
    formdata.data = {'0': '1'}
5614
    formdata.store()
5615
    formdata.just_created()
5616
    formdata.perform_workflow()
5617

  
5618
    assert formdef.data_class().count() == 1
5619
    assert carddef.data_class().count() == 1
5620

  
5621
    resp = app.get('/backoffice/management/form-a/1/inspect')
5622
    assert 'Related Forms/Cards' in resp.text
5623
    # related card
5624
    assert '<li><a href="http://example.net/backoffice/data/card-a/1/">CARD A #1-1</a></li>' in resp.text
5625

  
5626

  
5420 5627
def test_workflow_jump_previous(pub):
5628
    if not pub.is_using_postgresql():
5629
        pytest.skip('this requires SQL')
5630
        return
5631

  
5421 5632
    user = create_user(pub)
5422 5633
    create_environment(pub)
5423 5634

  
......
6600 6811

  
6601 6812
@pytest.fixture(params=[{'attach_to_history': True}, {}])
6602 6813
def create_formdata(request, pub):
6814
    if not pub.is_using_postgresql():
6815
        pytest.skip('this requires SQL')
6816
        return
6817

  
6603 6818
    admin = create_user(pub, is_admin=True)
6604 6819

  
6605 6820
    FormDef.wipe()
......
6857 7072

  
6858 7073

  
6859 7074
def test_backoffice_create_carddata_from_formdata(pub, studio):
7075
    if not pub.is_using_postgresql():
7076
        pytest.skip('this requires SQL')
7077
        return
7078

  
6860 7079
    CardDef.wipe()
6861 7080
    FormDef.wipe()
6862 7081

  
wcs/backoffice/management.py
2740 2740
            extra_context = formdata.submission_context or {}
2741 2741
            r += htmltext('<div class="extra-context">')
2742 2742
            if extra_context.get('orig_formdef_id'):
2743
                r += htmltext('<h3>%s</h3>') % _('Original form')
2743
                object_type = extra_context.get('orig_object_type', 'formdef')
2744
                if object_type == 'formdef':
2745
                    r += htmltext('<h3>%s</h3>') % _('Original form')
2746
                    object_class = FormDef
2747
                else:
2748
                    r += htmltext('<h3>%s</h3>') % _('Original card')
2749
                    object_class = CardDef
2744 2750
                try:
2745
                    orig_formdata = FormDef.get(extra_context.get('orig_formdef_id')
2746
                            ).data_class().get(extra_context.get('orig_formdata_id'))
2751
                    orig_formdata = object_class.get(
2752
                        extra_context.get('orig_formdef_id')
2753
                    ).data_class().get(extra_context.get('orig_formdata_id'))
2747 2754
                except KeyError:
2748 2755
                    r += htmltext('<p>%s</p>') % _('(deleted)')
2749 2756
                else:
2750
                    r += htmltext('<p><a href="%s">%s %s</a></p>') % (
2757
                    r += htmltext('<p><a href="%s">%s</a></p>') % (
2751 2758
                             orig_formdata.get_url(backoffice=True),
2752
                             orig_formdata.formdef.name,
2753
                             orig_formdata.get_display_id())
2759
                             orig_formdata.get_display_name())
2754 2760
            if formdata.submission_channel:
2755 2761
                r += htmltext('<h3>%s</h3>') % '%s: %s' % (
2756 2762
                        _('Channel'), formdata.get_submission_channel_label())
......
3141 3147
            r += htmltext('</ul>')
3142 3148
            r += htmltext('</div>')
3143 3149

  
3150
        children = list(self.filled.iter_target_datas())
3151
        if children:
3152
            r += htmltext('<div id="inspect-related" class="section">')
3153
            r += htmltext('<h2>%s</h2>') % _('Related Forms/Cards')
3154
            r += htmltext('<ul class="form-inspector biglist">')
3155
            for child in children:
3156
                r += htmltext('<li><a href="%s">%s</a></li>') % (
3157
                    child.get_url(backoffice=True), child.get_display_name())
3158
            r += htmltext('</ul>')
3159
            r += htmltext('</div>')
3160

  
3144 3161
        return r.getvalue()
3145 3162

  
3146 3163
    def inspect_tool(self):
wcs/formdata.py
1195 1195
            for part in evo.parts or []:
1196 1196
                yield part
1197 1197

  
1198
    def iter_target_datas(self, objectdef=None, slug=None, status_item=None):
1199
        # objectdef, slug and status_item are provided when called from ExternalWorkflowGlobalAction
1200
        from wcs.wf.create_formdata import LinkedFormdataEvolutionPart
1201
        from wcs.logged_errors import LoggedError
1202
        from .carddef import CardDef
1203
        from .formdef import FormDef
1204

  
1205
        parent = self.get_parent()
1206
        if parent:
1207
            parent_identifier = '%s:%s' % (parent.formdef.xml_root_node, parent.formdef.url_name)
1208
            if not objectdef or parent_identifier == slug:
1209
                yield parent
1210

  
1211
        data_ids = []
1212
        # search linked objects in data sources
1213
        for field in self.get_formdef().get_all_fields():
1214
            if getattr(field, 'data_source', None) and (not objectdef or field.data_source['type'] == slug):
1215
                linked_id = self.data.get(field.id)
1216
                if linked_id:
1217
                    data_ids.append((field.data_source['type'], linked_id))
1218

  
1219
        # search in evolution
1220
        for part in self.iter_evolution_parts():
1221
            if isinstance(part, LinkedFormdataEvolutionPart):
1222
                part_identifier = '%s:%s' % (part.formdef.xml_root_node, part.formdef.url_name)
1223
                if not objectdef or part_identifier == slug:
1224
                    data_ids.append((part_identifier, part.formdata_id))
1225

  
1226
        for (slug, target_id) in data_ids:
1227
            if objectdef:
1228
                try:
1229
                    yield objectdef.data_class().get(target_id)
1230
                except KeyError as e:
1231
                    # use custom error message depending on target type
1232
                    LoggedError.record(_('Could not find linked "%(object_name)s" object by id %(object_id)s') % {
1233
                        'object_name': objectdef.name, 'object_id': target_id},
1234
                        formdata=self, status_item=self, exception=e)
1235
            else:
1236
                try:
1237
                    object_type, slug = slug.split(':')
1238
                    if object_type == 'formdef':
1239
                        object_class = FormDef
1240
                    elif object_type == 'carddef':
1241
                        object_class = CardDef
1242
                    yield object_class.get_by_urlname(slug).data_class().get(target_id)
1243
                except ValueError:
1244
                    pass
1245
                except KeyError:
1246
                    pass
1247

  
1198 1248
    def __getattr__(self, attr):
1199 1249
        try:
1200 1250
            return self.__dict__[attr]
wcs/wf/external_workflow.py
22 22
from wcs.logged_errors import LoggedError
23 23
from wcs.workflows import WorkflowStatusItem, perform_items, register_item_class
24 24
from wcs.workflows import WorkflowGlobalActionWebserviceTrigger, Workflow
25
from wcs.wf.create_formdata import LinkedFormdataEvolutionPart
26 25
from wcs.carddef import CardDef
27 26
from wcs.formdef import FormDef
28 27

  
......
115 114
        return _('not completed')
116 115

  
117 116
    def iter_target_datas(self, formdata, objectdef):
118
        parent = formdata.get_parent()
119
        if parent:
120
            parent_identifier = '%s:%s' % (parent.formdef.xml_root_node,
121
                    parent.formdef.url_name)
122
            if parent_identifier == self.slug:
123
                yield parent
124

  
125
        data_ids = []
126
        # search linked objects in data sources
127
        for field in formdata.get_formdef().get_all_fields():
128
            if getattr(field, 'data_source', None) and field.data_source['type'] == self.slug:
129
                linked_id = formdata.data.get(field.id)
130
                if linked_id:
131
                    data_ids.append(linked_id)
132

  
133
        # search in evolution
134
        for part in formdata.iter_evolution_parts():
135
            if isinstance(part, LinkedFormdataEvolutionPart):
136
                part_identifier = '%s:%s' % (part.formdef.xml_root_node, part.formdef.url_name)
137
                if part_identifier == self.slug:
138
                    data_ids.append(part.formdata_id)
139

  
140
        for target_id in data_ids:
141
            try:
142
                yield objectdef.data_class().get(target_id)
143
            except KeyError as e:
144
                # use custom error message depending on target type
145
                LoggedError.record(_('Could not find linked "%(object_name)s" object by id %(object_id)s') % {
146
                    'object_name': objectdef.name, 'object_id': target_id},
147
                    formdata=formdata, status_item=self, exception=e)
117
        yield from formdata.iter_target_datas(objectdef=objectdef, slug=self.slug, status_item=self)
148 118

  
149 119
    def get_parameters(self):
150 120
        return ('slug', 'trigger_id', 'condition')
151
-