Compare commits

..

5 Commits

Author SHA1 Message Date
Emmanuel Cazenave fda1bf7cad backoffice: display drafts stats (#72542)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-04 18:24:07 +01:00
Frédéric Péters ab38410f34 translation update
gitea/wcs/pipeline/head This commit looks good Details
2024-03-04 17:49:49 +01:00
Frédéric Péters 2eb29089a1 templatetags: make sure |get_preference gets a user (#87741)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-04 16:50:09 +01:00
Valentin Deniaud f418c515f5 testdef: allow setting status code of webservice response (#87542)
gitea/wcs/pipeline/head This commit looks good Details
2024-03-04 16:06:35 +01:00
Valentin Deniaud d784acba07 translation update
gitea/wcs/pipeline/head This commit looks good Details
2024-03-04 15:33:45 +01:00
10 changed files with 151 additions and 33 deletions

View File

@ -2,6 +2,7 @@ import os
import pytest
from wcs.formdef import FormDef
from wcs.qommon.http_request import HTTPRequest
from wcs.qommon.ident.password_accounts import PasswordAccount
@ -86,6 +87,10 @@ def test_admin_for_all(pub):
user = create_superuser(pub)
role = create_role(pub)
formdef = FormDef()
formdef.name = 'test'
formdef.store()
try:
with open(os.path.join(pub.app_dir, 'ADMIN_FOR_ALL'), 'w'):
pass # create empty file

View File

@ -4712,7 +4712,7 @@ def test_admin_form_inspect_drafts(pub):
app = login(get_app(pub))
resp = app.get('/backoffice/forms/%s/inspect' % formdef.id)
assert resp.pyquery('#inspect-drafts p').text() == 'No drafts found for this form'
assert resp.pyquery('#inspect-drafts p').text() == 'There are currently no drafts for this form.'
data_class = formdef.data_class()
formdata = data_class()
@ -4739,28 +4739,28 @@ def test_admin_form_inspect_drafts(pub):
resp = app.get('/backoffice/forms/%s/inspect' % formdef.id)
assert resp.pyquery('div#inspect-drafts tr#0').length == 1
assert resp.pyquery('div#inspect-drafts tr#0 td.label').text() == '1st page'
assert resp.pyquery('div#inspect-drafts tr#0 td.percent').text() == '20.0\xa0%'
assert resp.pyquery('div#inspect-drafts tr#0 td.percent').text() == '20.0%'
assert resp.pyquery('div#inspect-drafts tr#0 td.total').text() == '(1/5)'
assert resp.pyquery('div#inspect-drafts tr#2').length == 1
assert resp.pyquery('div#inspect-drafts tr#2 td.label').text() == '2nd page'
assert resp.pyquery('div#inspect-drafts tr#2 td.percent').text() == '20.0\xa0%'
assert resp.pyquery('div#inspect-drafts tr#2 td.percent').text() == '20.0%'
assert resp.pyquery('div#inspect-drafts tr#2 td.total').text() == '(1/5)'
assert resp.pyquery('div#inspect-drafts tr#4').length == 1
assert resp.pyquery('div#inspect-drafts tr#4 td.label').text() == '3rd page'
assert resp.pyquery('div#inspect-drafts tr#4 td.percent').text() == '20.0\xa0%'
assert resp.pyquery('div#inspect-drafts tr#4 td.percent').text() == '20.0%'
assert resp.pyquery('div#inspect-drafts tr#4 td.total').text() == '(1/5)'
assert resp.pyquery('div#inspect-drafts tr#_confirmation_page').length == 1
assert resp.pyquery('div#inspect-drafts tr#_confirmation_page td.label').text() == 'Confirmation page'
assert resp.pyquery('div#inspect-drafts tr#_confirmation_page td.percent').text() == '20.0\xa0%'
assert resp.pyquery('div#inspect-drafts tr#_confirmation_page td.percent').text() == '20.0%'
assert resp.pyquery('div#inspect-drafts tr#_confirmation_page td.total').text() == '(1/5)'
assert resp.pyquery('div#inspect-drafts tr#_unkown').length == 1
assert resp.pyquery('div#inspect-drafts tr#_unkown td.label').text() == 'Unkown'
assert resp.pyquery('div#inspect-drafts tr#_unkown td.percent').text() == '20.0\xa0%'
assert resp.pyquery('div#inspect-drafts tr#_unkown td.total').text() == '(1/5)'
assert resp.pyquery('div#inspect-drafts tr#_unknown').length == 1
assert resp.pyquery('div#inspect-drafts tr#_unknown td.label').text() == 'Unknown'
assert resp.pyquery('div#inspect-drafts tr#_unknown td.percent').text() == '20.0%'
assert resp.pyquery('div#inspect-drafts tr#_unknown td.total').text() == '(1/5)'
def test_form_import_fields(pub):

View File

@ -1369,6 +1369,7 @@ def test_tests_webservice_response(pub):
resp = resp.click('Test response')
resp.form['url'] = 'http://example.com/'
resp.form['payload'] = '{"a": "b"}'
resp.form['status_code'] = '400'
resp.form['qs_data$element0key'] = 'foo'
resp.form['method'] = 'POST (JSON)'
resp.form['post_data$element0key'] = 'bar'
@ -1381,6 +1382,7 @@ def test_tests_webservice_response(pub):
assert response.name == 'Test response'
assert response.url == 'http://example.com/'
assert response.payload == '{"a": "b"}'
assert response.status_code == 400
assert response.qs_data == {'foo': ''}
assert response.method == 'POST'
assert response.post_data == {'bar': ''}

View File

@ -543,3 +543,53 @@ def test_workflow_tests_webservice(pub):
with pytest.raises(WorkflowTestError) as excinfo:
testdef.run(formdef)
assert str(excinfo.value) == 'Webservice response Fake response was used 0 times (instead of 1).'
def test_workflow_tests_webservice_status_jump(pub):
user = pub.user_class(name='test user')
user.store()
workflow = Workflow(name='Workflow One')
new_status = workflow.add_status(name='New status')
end_status = workflow.add_status(name='Error status')
wscall = new_status.add_action('webservice_call')
wscall.url = 'http://example.com/json'
wscall.varname = 'test_webservice'
wscall.action_on_4xx = end_status.id
workflow.store()
formdef = FormDef()
formdef.name = 'test title'
formdef.workflow_id = workflow.id
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
testdef = TestDef.create_from_formdata(formdef, formdata)
testdef.agent_id = user.id
testdef.store()
response = WebserviceResponse()
response.testdef_id = testdef.id
response.name = 'Fake response'
response.url = 'http://example.com/json'
response.payload = '{"foo": "foo"}'
response.store()
testdef.workflow_tests.actions = [
workflow_tests.AssertStatus(status_name='New status'),
workflow_tests.AssertWebserviceCall(webservice_response_id=response.id, call_count=1),
]
testdef.run(formdef)
response.status_code = 400
response.store()
testdef.workflow_tests.actions = [
workflow_tests.AssertStatus(status_name='End status'),
workflow_tests.AssertWebserviceCall(webservice_response_id=response.id, call_count=1),
]

View File

@ -1777,33 +1777,48 @@ class FormDefPage(Directory, TempfileDirectoryMixin):
)
context['deprecation_titles'] = deprecations.titles
temp_drafts = defaultdict(int)
temp_drafts = {}
total_drafts = 0
drafts = {}
for formdata in self.formdef.data_class().select(clause=[Equal('status', 'draft')]):
page_id = formdata.page_id if formdata.page_id is not None else '_unkown'
temp_drafts[page_id] += 1
for formdata in self.formdef.data_class().select_iterator(
clause=[Equal('status', 'draft')], itersize=200
):
page_id = formdata.page_id if formdata.page_id is not None else '_unknown'
if page_id not in temp_drafts:
temp_drafts[page_id] = {'total': 0, 'page_no': int(formdata.page_no)}
temp_drafts[page_id]['total'] += 1
total_drafts += 1
if total_drafts:
for key in ('_unkown', '_confirmation_page', '_first_page'):
special_page_page_no_mapping = {
'_first_page': -100, # first
'_unknown': 1000, # last
'_confirmation_page': 999, # second to last
}
for page_id, page_no in special_page_page_no_mapping.items():
try:
num_drafts = temp_drafts.pop(key)
draft_info = temp_drafts.pop(page_id)
except KeyError:
num_drafts = 0
drafts[key] = {'total': num_drafts, 'field': None}
for page_id, num_drafts in temp_drafts.items():
draft_info = {'total': 0}
drafts[page_id] = {'total': draft_info['total'], 'field': None, 'page_no': page_no}
for page_id, draft_info in temp_drafts.items():
for field in self.formdef.iter_fields(with_backoffice_fields=False):
if page_id == field.id and isinstance(field, PageField):
drafts[page_id] = {'total': num_drafts, 'field': field}
drafts[page_id] = {
'total': draft_info['total'],
'field': field,
'page_no': draft_info['page_no'],
}
break
else:
drafts['_unkown']['total'] += num_drafts
drafts['_unknown']['total'] += draft_info['total']
for draft_data in drafts.values():
draft_percent = 100 * draft_data['total'] / total_drafts
draft_data['percent'] = draft_percent
draft_data['percent_rounded'] = '%d' % draft_percent
context['drafts'] = sorted(drafts.items(), reverse=True, key=lambda x: x[1]['total'])
context['drafts'] = sorted(drafts.items(), key=lambda x: x[1]['page_no'])
context['drafts_total'] = total_drafts
context['is_carddef'] = isinstance(self.formdef, CardDef)
return template.QommonTemplateResponse(
templates=[self.inspect_template_name],

View File

@ -754,6 +754,16 @@ class WebserviceResponsePage(Directory):
validation_function=validate_json,
)
form.add(
RadiobuttonsWidget,
'status_code',
title=_('Response status code'),
required=True,
options=[200, 204, 400, 401, 403, 404, 500, 502, 503],
value=self.webservice_response.status_code,
extra_css_class='widget-inline-radio',
)
form.add(
WidgetDict,
'qs_data',
@ -820,6 +830,7 @@ class WebserviceResponsePage(Directory):
self.webservice_response.name = form.get_widget('name').parse()
self.webservice_response.payload = form.get_widget('payload').parse()
self.webservice_response.url = form.get_widget('url').parse()
self.webservice_response.status_code = form.get_widget('status_code').parse()
self.webservice_response.qs_data = form.get_widget('qs_data').parse()
self.webservice_response.method = form.get_widget('method').parse()
self.webservice_response.post_data = form.get_widget('post_data').parse()

View File

@ -4,8 +4,8 @@ msgid ""
msgstr ""
"Project-Id-Version: wcs 0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-04 13:46+0100\n"
"PO-Revision-Date: 2024-03-04 13:47+0100\n"
"POT-Creation-Date: 2024-03-04 17:49+0100\n"
"PO-Revision-Date: 2024-03-04 17:49+0100\n"
"Last-Translator: Thomas Noël <tnoel@entrouvert.com>\n"
"Language-Team: french\n"
"Language: fr\n"
@ -2628,6 +2628,10 @@ msgstr "JSON invalide : %s"
msgid "Response payload (JSON)"
msgstr "Contenu de la réponse (JSON)"
#: admin/tests.py
msgid "Response status code"
msgstr "Code de statut de la réponse"
#: admin/tests.py
msgid "Restrict to query string data"
msgstr "Limiter aux paramètres de lURL"
@ -11948,11 +11952,14 @@ msgstr "Les traces des erreurs seront envoyées à %s."
msgid ""
"Record errors in the central error screen, for management by administrators"
msgstr ""
" Enregistrer les erreurs dans lécran de centralisation des erreurs, pour traitement par léquipe dadministration"
" Enregistrer les erreurs dans lécran de centralisation des erreurs, pour "
"traitement par léquipe dadministration"
#: wf/wscall.py
msgid "Record errors in card/form history log, for agents"
msgstr "Enregistrer les erreurs dans lhistorique de la demande ou fiche, pour visualisation par les agents"
msgstr ""
"Enregistrer les erreurs dans lhistorique de la demande ou fiche, pour "
"visualisation par les agents"
#: wf/wscall.py
msgctxt "wscall-parameter"
@ -12044,10 +12051,25 @@ msgstr "Nom du statut"
msgid "Assert email is sent"
msgstr "Vérifier lenvoi dun courriel"
#: workflow_tests.py
#, python-format
msgid "Email to \"%s\""
msgstr "Courriel vers « %s »"
#: workflow_tests.py
msgid "No email was sent."
msgstr "Aucun courriel envoyé."
#: workflow_tests.py
#, python-format
msgid "Email addresses: %s"
msgstr "Adresses de courriel : %s"
#: workflow_tests.py
#, python-format
msgid "Email was not sent to address \"%s\"."
msgstr "Le courriel na pas été envoyé vers ladresse « %s »."
#: workflow_tests.py
#, python-format
msgid "Email subject: %s"
@ -12068,6 +12090,14 @@ msgstr "Corps du courriel : %s"
msgid "Email body does not contain \"%s\"."
msgstr "Le corps du courriel ne contient pas « %s »."
#: workflow_tests.py
msgid "Email addresses"
msgstr "Adresses de courriel"
#: workflow_tests.py
msgid "Add address"
msgstr "Ajouter une adresse"
#: workflow_tests.py
msgid "Subject must contain"
msgstr "Le sujet doit contenir"

View File

@ -1281,7 +1281,7 @@ def intcomma(value):
@register.filter
def get_preference(user, pref_name):
return user.get_preference(pref_name)
return user.get_preference(pref_name) if user else None
@register.simple_tag(takes_context=True)

View File

@ -11,7 +11,7 @@
<button role="tab" aria-selected="false" aria-controls="inspect-workflow" id="tab-workflow" tabindex="-1">{% trans "Workflow" %}</button>
<button role="tab" aria-selected="false" aria-controls="inspect-options" id="tab-options" tabindex="-1">{% trans "Options" %}</button>
<button role="tab" aria-selected="false" aria-controls="inspect-fields" id="tab-fields" tabindex="-1">{% trans "Fields" %}</button>
{% if not snapshots_diff %}
{% if not snapshots_diff and not is_carddef %}
<button role="tab" aria-selected="false" aria-controls="inspect-drafts" id="tab-drafts" tabindex="-1">{% trans "Drafts" %}</button>
{% endif %}
{% if custom_views %}
@ -93,16 +93,19 @@
<div id="inspect-drafts" role="tabpanel" tabindex="0" aria-labelledby="tab-drafts" hidden>
{% if drafts %}
<div class="infonotice">
<p>{% trans "Statistics on drafts by page." %}</p>
<p>{% trans "Lifespan of drafts (in days)" %}{% trans ":" %} {{ formdef.drafts_lifespan|default_if_none:_('default value') }}.</p>
</div>
<table class="stats">
<thead><tr><th colspan="4">{% trans "Page" %}</th></tr></thead>
<tbody>
{% for page_drafts in drafts %}
{% with page_id=page_drafts.0 draft_data=page_drafts.1 %}
{% if draft_data.total %}
<tr id="{{ page_id }}">
<td class="label">
{% if page_id == "_unkown" %}
{% trans "Unkown" %}
{% if page_id == "_unknown" %}
{% trans "Unknown" %}
{% elif page_id == "_first_page" %}
{% trans "Only page" %}
{% elif page_id == "_confirmation_page" %}
@ -111,7 +114,7 @@
{{ draft_data.field.ellipsized_label }}
{% endif %}
</td>
<td class="percent"> {{draft_data.percent}}&nbsp;%</td>
<td class="percent">{% blocktranslate with percent=draft_data.percent %}{{percent}}%{% endblocktranslate %}</td>
<td class="total">({{draft_data.total}}/{{drafts_total}})</td>
</tr>
<tr>
@ -125,7 +128,7 @@
</tbody>
</table>
{% else %}
<p>{% trans "No drafts found for this form" %}</p>
<p>{% trans "There are currently no drafts for this form." %}</p>
{% endif %}
</div>

View File

@ -640,7 +640,7 @@ class MockWebserviceResponseAdapter(requests.adapters.HTTPAdapter):
}
raw_response = HTTPResponse(
status=200,
status=response.status_code,
body=io.BytesIO(response.payload.encode()),
headers=headers,
original_response=self.make_original_response(headers),
@ -669,6 +669,7 @@ class WebserviceResponse(XmlStorableObject):
name = ''
payload = None
url = None
status_code = 200
qs_data = None
method = ''
post_data = None
@ -678,6 +679,7 @@ class WebserviceResponse(XmlStorableObject):
('name', 'str'),
('payload', 'str'),
('url', 'str'),
('status_code', 'int'),
('qs_data', 'kv_data'),
('method', 'str'),
('post_data', 'kv_data'),