backoffice: add option for a "user visible status" column (#38167) #1007

Merged
fpeters merged 1 commits from wip/38167-user-visible-status-column into main 2024-01-12 15:06:26 +01:00
4 changed files with 101 additions and 28 deletions

View File

@ -2812,7 +2812,7 @@ def test_api_geojson_formdata(pub, local_user):
resp = get_app(pub).get(sign_uri('/api/forms/test/geojson?full=on', user=local_user))
assert len(resp.json['features']) == 10
display_fields = resp.json['features'][0]['properties']['display_fields']
assert len(display_fields) == 9
assert len(display_fields) == 10
field_varnames = [f['varname'] for f in display_fields]
assert 'foobar' in field_varnames

View File

@ -11,6 +11,7 @@ from wcs.carddef import CardDef
from wcs.formdef import FormDef
from wcs.qommon.http_request import HTTPRequest
from wcs.qommon.upload_storage import PicklableUpload
from wcs.workflows import Workflow
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
from .test_all import create_superuser
@ -531,6 +532,7 @@ def test_backoffice_block_columns(pub):
'Block',
'Block / Test',
'Block / card field',
'Status (for user)',
'Anonymised',
]
# enable columns for subfields
@ -566,6 +568,7 @@ def test_backoffice_block_columns(pub):
'Block',
'Block / Test',
'Block / card field',
'Status (for user)',
'Anonymised',
]
resp.forms['listing-settings']['8-123'].checked = True
@ -633,6 +636,7 @@ def test_backoffice_block_email_column(pub):
'Channel',
'Block',
'Block / Test',
'Status (for user)',
'Anonymised',
]
resp.forms['listing-settings']['8-123'].checked = True
@ -698,6 +702,7 @@ def test_backoffice_block_bool_column(pub):
'Channel',
'Block',
'Block / Test',
'Status (for user)',
'Anonymised',
]
resp.forms['listing-settings']['8-123'].checked = True
@ -759,6 +764,7 @@ def test_backoffice_block_date_column(pub):
'Channel',
'Block',
'Block / Test',
'Status (for user)',
'Anonymised',
]
resp.forms['listing-settings']['8-123'].checked = True
@ -825,6 +831,7 @@ def test_backoffice_block_file_column(pub):
'Channel',
'Block',
'Block / Test',
'Status (for user)',
'Anonymised',
]
resp.forms['listing-settings']['8-123'].checked = True
@ -887,6 +894,7 @@ def test_backoffice_block_text_column(pub):
'Channel',
'Block',
'Block / Test',
'Status (for user)',
'Anonymised',
]
resp.forms['listing-settings']['8-123'].checked = True
@ -952,6 +960,7 @@ def test_backoffice_block_column_position(pub):
'Block',
'Block / Test',
'Block / Bar',
'Status (for user)',
'Anonymised',
]
resp.forms['listing-settings']['time'].checked = False
@ -1103,6 +1112,7 @@ def test_backoffice_digest_column(pub):
'Digest',
'Channel',
'field',
'Status (for user)',
'Anonymised',
]
assert {x.text for x in resp.pyquery('.cell-status + td')} == {'form foo', 'form bar'}
@ -1142,3 +1152,63 @@ def test_backoffice_unknown_status_column(pub):
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/?filter=all')
assert resp.pyquery('tbody td.cell-status').text() == 'Unknown'
def test_backoffice_user_visible_status_column(pub):
pub.user_class.wipe()
create_superuser(pub)
pub.role_class.wipe()
role = pub.role_class(name='test')
role.store()
Workflow.wipe()
workflow = Workflow(name='test user visible column')
st1 = workflow.add_status('st1')
st2 = workflow.add_status('st2')
st2.visibility = ['_receiver']
jump = st1.add_action('jump')
jump.status = str(st2.id)
workflow.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form-title'
formdef.fields = [
fields.StringField(
id='1',
label='field',
varname='foo',
)
]
formdef.workflow_roles = {'_receiver': role.id}
formdef.workflow = workflow
formdef.store()
data_class = formdef.data_class()
data_class.wipe()
formdata = data_class()
formdata.data = {'1': 'foo'}
formdata.just_created()
formdata.store()
formdata.perform_workflow()
formdata.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = app.get('/backoffice/management/form-title/')
resp.forms['listing-settings']['filter'] = 'all'
resp.forms['listing-settings']['user-visible-status'].checked = True
resp = resp.forms['listing-settings'].submit()
assert [x.text_content() for x in resp.pyquery('#columns-filter label')] == [
'Number',
'Created',
'Last Modified',
'User Label',
'Status',
'Status (for user)',
'Channel',
'field',
'Anonymised',
]
assert '<td class="cell-status">st2</td><td>st1</td>' in resp.text

View File

@ -1851,6 +1851,13 @@ class FormPage(Directory, TempfileDirectoryMixin):
yield RelatedField(carddef, card_field, field)
yield FakeField('status', 'status', _('Status'), include_in_statistics=True)
if any(x.get_visibility_mode() != 'all' for x in self.formdef.workflow.possible_status):
yield FakeField(
'user-visible-status',
'user-visible-status',
_('Status (for user)'),
geojson_label=_('Status'),
)
yield FakeField('anonymised', 'anonymised', _('Anonymised'))
def get_default_columns(self):
@ -4098,7 +4105,7 @@ class FormBackOfficeStatusPage(FormStatusPage):
class FakeField:
can_include_in_listing = True
def __init__(self, id, type_key, label, addable=True, include_in_statistics=False):
def __init__(self, id, type_key, label, addable=True, include_in_statistics=False, geojson_label=None):
self.id = id
self.contextual_id = self.id
self.key = type_key
@ -4110,6 +4117,7 @@ class FakeField:
self.store_structured_value = None
self.addable = addable
self.include_in_statistics = include_in_statistics
self.geojson_label = force_str(geojson_label or self.label)
def get_view_value(self, value):
# just here to quack like a duck

View File

@ -925,6 +925,25 @@ class FormData(StorableObject):
return None
def get_field_view_value(self, field, max_length=None):
class StatusFieldValue:
def __init__(self, status):
self.status = status
def get_ods_style_name(self):
return 'StatusStyle-%s' % misc.simplify(self.status.name) if self.status else None
def get_ods_colour(self, colour):
return {'black': '#000000', 'white': '#ffffff'}.get(colour, colour)
def get_ods_style_bg_colour(self):
return self.get_ods_colour(self.status.colour) if self.status else 'transparent'
def get_ods_style_fg_colour(self):
return self.get_ods_colour(self.status.get_contrast_color()) if self.status else '#000000'
def __str__(self):
return str(get_publisher().translate(self.status.name) if self.status else _('Unknown'))
def get_value(field, data, **kwargs):
# return the value of the given field, with special handling for "fake"
# field types that are shortcuts to internal properties.
@ -939,33 +958,9 @@ class FormData(StorableObject):
if field.key == 'user-label':
return self.get_user_label() or '-'
if field.key == 'status':
class StatusFieldValue:
def __init__(self, status):
self.status = status
def get_ods_style_name(self):
return 'StatusStyle-%s' % misc.simplify(self.status.name) if self.status else None
def get_ods_colour(self, colour):
return {'black': '#000000', 'white': '#ffffff'}.get(colour, colour)
def get_ods_style_bg_colour(self):
return self.get_ods_colour(self.status.colour) if self.status else 'transparent'
def get_ods_style_fg_colour(self):
return (
self.get_ods_colour(self.status.get_contrast_color())
if self.status
else '#000000'
)
def __str__(self):
return str(
get_publisher().translate(self.status.name) if self.status else _('Unknown')
)
return StatusFieldValue(self.get_status())
if field.key == 'user-visible-status':
return StatusFieldValue(self.get_visible_status(user=None))
if field.key == 'submission_channel':
return self.get_submission_channel_label()
if field.key == 'submission_agent':