Projet

Général

Profil

0001-can-notify-exceptions-on-formdata-only-in-UI-24327.patch

Thomas Noël, 07 juin 2018 16:34

Télécharger (9,74 ko)

Voir les différences:

Subject: [PATCH] can notify exceptions on formdata only in UI (#24327)

 tests/test_form_pages.py   | 11 +++++-
 wcs/admin/logged_errors.py | 17 +++++++++
 wcs/logged_errors.py       | 78 +++++++++++++++++++++++++++++++-------
 wcs/publisher.py           |  2 +-
 wcs/workflows.py           | 12 +++---
 5 files changed, 99 insertions(+), 21 deletions(-)
tests/test_form_pages.py
1694 1694
    assert 'barXYZ' in resp.body # unchanged value is still there
1695 1695
    assert formdef.data_class().get(data_id).status == 'wf-%s' % st2.id
1696 1696

  
1697
    # jump to a nonexistent status == do not jump
1697
    # jump to a nonexistent status == do not jump, but add a LoggedError
1698
    LoggedError.wipe()
1699
    assert LoggedError.count() == 0
1698 1700
    editable.status = 'deleted_status_id'
1699 1701
    workflow.store()
1700 1702
    # go back to st1
......
1710 1712
    resp = resp.forms[0].submit('submit')
1711 1713
    resp = resp.follow()
1712 1714
    assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id  # stay on st1
1715
    assert LoggedError.count() == 1
1716
    logged_error = LoggedError.select()[0]
1717
    assert logged_error.formdata_id == str(formdata.id)
1718
    assert logged_error.formdef_id == formdef.id
1719
    assert logged_error.workflow_id == workflow.id
1720
    assert logged_error.status_id == st1.id
1721
    assert logged_error.status_item_id == editable.id
1713 1722

  
1714 1723
def test_form_count_dispatching(pub):
1715 1724
    user = create_user(pub)
wcs/admin/logged_errors.py
56 56
        if workflow:
57 57
            r += htmltext('  <li>%s <a href="/backoffice/workflows/%s/">%s</a></li>') % (
58 58
                    _('Workflow: '), workflow.id, workflow.name)
59
            status = self.error.get_status()
60
            if status:
61
                r += htmltext('<ul>')
62
                r += htmltext(
63
                    '<li>%s <a href="/backoffice/workflows/%s/status/%s/">%s</a></li>') % (
64
                        _('Status:'), workflow.id, status.id, status.name)
65
                status_item = self.error.get_status_item()
66
                if status_item:
67
                    r += htmltext(
68
                        '<li>%s <a href="/backoffice/workflows/%s/status/%s/items/%s/">%s</a></li>') % (
69
                            _('Action:'), workflow.id, status.id, status_item.id,
70
                            _(status_item.description))
71
                r += htmltext('</ul>')
59 72

  
60 73
        if formdef:
61 74
            formdata = self.error.get_formdata()
......
68 81
                r += htmltext('</li>')
69 82

  
70 83
        r += htmltext('</ul>')
84

  
85
        if not self.error.traceback:
86
            return r.getvalue()
87

  
71 88
        parts = (N_('Exception'), N_('Stack trace (most recent call first)'),
72 89
                N_('Form'), N_('Cookies'), N_('Environment'))
73 90
        current_part = None
wcs/logged_errors.py
31 31
    formdata_id = None
32 32
    formdef_id = None
33 33
    workflow_id = None
34
    status_id = None
35
    status_item_id = None
34 36
    traceback = None
35 37
    occurences_count = 0
36 38
    first_occurence_timestamp = None
......
41 43
    XML_NODES = [
42 44
            ('summary', 'str'), ('traceback', 'str'),
43 45
            ('formdata_id', 'str'), ('formdef_id', 'str'), ('workflow_id', 'str'),
46
            ('status_id', 'str'), ('status_item_id', 'str'),
44 47
            ('occurences_count', 'int'),
45 48
            ('first_occurence_timestamp', 'datetime'),
46 49
            ('latest_occurence_timestamp', 'datetime'),
47 50
            ('acked', 'bool')]
48 51

  
49 52
    @classmethod
50
    def record(cls, error_summary, plain_error_msg, publisher):
53
    def record(cls, error_summary, plain_error_msg=None, formdata=None, formdata_id=None,
54
               formdef=None, workflow=None, status=None, status_item=None):
51 55
        error = cls()
52 56
        error.summary = error_summary
53 57
        error.traceback = plain_error_msg
54
        try:
55
            context = publisher.substitutions.get_context_variables()
56
        except:
57
            return
58 58

  
59
        formdef_urlname = context.get('form_slug')
60
        if not formdef_urlname:
59
        if formdata:
60
            error.formdata_id = str(formdata.id)
61
            error.formdef_id = formdata.formdef.id
62
            error.workflow_id = formdata.formdef.workflow.id
63
        else:
64
            error.formdata_id = str(formdata_id)
65
            if formdef:
66
                error.formdef_id = formdef.id
67
                error.workflow_id = formdef.workflow.id
68

  
69
        if not error.formdef_id:
61 70
            # cannot attach error to formdef, don't record in journal, it will
62 71
            # still be sent by email to administrators.
63 72
            return
64 73

  
65
        error.formdata_id = context.get('form_number_raw')
66
        if formdef_urlname:
67
            formdef = FormDef.get_by_urlname(formdef_urlname)
68
            error.formdef_id = formdef.id
69
            error.workflow_id = formdef.workflow_id
74
        if not error.workflow_id:
75
            error.workflow_id = workflow.id
76

  
77
        if status_item:
78
            error.status_item_id = status_item.id
79
            if status_item.parent:
80
                error.status_id = status_item.parent.id
81
        if status:
82
            error.status_id = status.id
70 83

  
71 84
        error.first_occurence_timestamp = datetime.datetime.now()
72 85
        error.id = '%s-%s' % (
......
80 93
        error.latest_occurence_timestamp = datetime.datetime.now()
81 94
        error.store()
82 95

  
96
    @classmethod
97
    def record_exception(cls, error_summary, plain_error_msg, publisher):
98
        try:
99
            context = publisher.substitutions.get_context_variables()
100
        except Exception as e:
101
            return
102
        formdata_id = context.get('form_number_raw')
103
        formdef_urlname = context.get('form_slug')
104
        if not formdef_urlname:
105
            # cannot attach error to formdef, don't record in journal, it will
106
            # still be sent by email to administrators.
107
            return
108
        formdef = FormDef.get_by_urlname(formdef_urlname)
109
        cls.record(error_summary, plain_error_msg, formdata_id=formdata_id,
110
                   formdef=formdef, workflow=formdef.workflow)
111

  
83 112
    @property
84 113
    def tech_id(self):
85
        return ('%s-%s-%s' % (self.formdef_id, self.workflow_id, simplify(self.summary)))[:200]
114
        return ('%s-%s-%s-%s-%s' % (self.formdef_id, self.workflow_id, self.status_id,
115
                                    self.status_item_id, simplify(self.summary)))[:200]
86 116

  
87 117
    def get_formdef(self):
88 118
        return FormDef.get(self.formdef_id, ignore_errors=True)
......
91 121
        return Workflow.get(self.workflow_id, ignore_errors=True)
92 122

  
93 123
    def get_formdata(self):
124
        if not self.formdata_id:
125
            return None
94 126
        return self.get_formdef().data_class().get(self.formdata_id, ignore_errors=True)
127

  
128
    def get_status(self):
129
        if not self.status_id:
130
            return None
131
        workflow = self.get_workflow()
132
        if not workflow:
133
            return None
134
        for status in workflow.possible_status:
135
            if status.id == self.status_id:
136
                return status
137
        return None
138

  
139
    def get_status_item(self):
140
        status = self.get_status()
141
        if not status or not status.items:
142
            return None
143
        for status_item in status.items:
144
            if status_item.id == self.status_item_id:
145
                return status_item
146
        return None
wcs/publisher.py
287 287
        super(WcsPublisher, self).log_internal_error(error_summary,
288 288
                plain_error_msg, record=record)
289 289
        if record:
290
            LoggedError.record(error_summary, plain_error_msg, publisher=self)
290
            LoggedError.record_exception(error_summary, plain_error_msg, publisher=self)
291 291

  
292 292
    def apply_global_action_timeouts(self):
293 293
        from wcs.workflows import Workflow, WorkflowGlobalActionTimeoutTrigger
wcs/workflows.py
1725 1725
            return []
1726 1726

  
1727 1727
        targets = [x for x in self.parent.parent.possible_status if x.id == self.status]
1728
        if not targets:
1729
            get_publisher().get_app_logger().error(
1730
                            'reference to invalid status in workflow %r, status %r, item %r' % (
1731
                                    self.parent.parent.name,
1732
                                    self.parent.name,
1733
                                    self.description))
1728
        if not targets and formdata:  # do not log in presentation context: formdata is needed
1729
            from wcs.logged_errors import LoggedError
1730
            message = _('reference to invalid status %s in status %s, action %s') % (
1731
                self.status, self.parent.name, _(self.description))
1732
            LoggedError.record(message, formdata=formdata, status_item=self)
1733

  
1734 1734
        return targets
1735 1735

  
1736 1736
    def get_jump_label(self):
1737
-