backoffice: add options to item field for plotting choices on a map (#47066)

This commit is contained in:
Frédéric Péters 2020-11-09 16:55:44 +01:00
parent 1daf66e50c
commit 0e9e91b2db
4 changed files with 107 additions and 38 deletions

View File

@ -1589,6 +1589,46 @@ def test_form_edit_item_field_data_source(pub):
resp = app.get('/backoffice/forms/1/')
def test_form_edit_item_field_geojson_data_source(pub, http_requests):
NamedDataSource.wipe()
create_superuser(pub)
create_role()
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {
'type': 'geojson',
'value': 'http://remote.example.net/geojson',
}
data_source.id_property = 'id'
data_source.label_template_property = '{{ text }}'
data_source.cache_duration = '5'
data_source.store()
FormDef.wipe()
formdef = FormDef()
formdef.name = 'form title'
formdef.fields = [fields.ItemField(id='1', label='1st field', type='item')]
formdef.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/1/fields/1/')
resp.form['display_mode'] = 'map'
assert resp.pyquery('option[value=foobar][data-type=geojson]')
resp.form['data_mode'] = 'data-source'
resp.form['data_source$type'] = 'foobar'
resp.form['min_zoom'] = 'Wide area'
resp.form['max_zoom'] = 'Small road'
resp = resp.form.submit('submit').follow()
formdef = FormDef.get(formdef.id)
assert formdef.fields[0].data_source == {'type': 'foobar'}
assert formdef.fields[0].min_zoom == '9'
resp = app.get('/backoffice/forms/1/fields/1/')
assert resp.form['min_zoom'].value == 'Wide area'
def test_form_edit_items_field(pub):
create_superuser(pub)
create_role()

View File

@ -68,7 +68,7 @@ class DataSourceSelectionWidget(CompositeWidget):
options.append(OptGroup(_('Cards')))
options.extend(cards_options)
nds_options = [(x.slug, x.name, x.slug) for x in NamedDataSource.select()]
nds_options = [(x.slug, x.name, x.slug, {'data-type': x.type}) for x in NamedDataSource.select()]
nds_options.sort(key=lambda x: misc.simplify(x[1]))
if nds_options:
options.append(OptGroup(_('Manually Configured Data Sources')))

View File

@ -1434,7 +1434,44 @@ def item_items_stats(field, values):
return r.getvalue()
class ItemField(WidgetField):
class MapOptionsMixin:
initial_zoom = None
min_zoom = None
max_zoom = None
def fill_zoom_admin_form(self, form, **kwargs):
# 0: whole world, 9: wide area, 11: area, 13: town, 16: small road
zoom_levels = [(None, '---'),
('0', _('Whole world')),
('6', _('Country')),
('9', _('Wide area')),
('11', _('Area')),
('13', _('Town')),
('16', _('Small road')),
('18', _('Neighbourhood')),
('19', _('Ant')),]
form.add(SingleSelectWidget, 'initial_zoom', title=_('Initial zoom level'),
value=self.initial_zoom or '13', options=zoom_levels, **kwargs)
form.add(SingleSelectWidget, 'min_zoom', title=_('Minimal zoom level'),
value=self.min_zoom, options=zoom_levels, required=False, **kwargs)
form.add(SingleSelectWidget, 'max_zoom', title=_('Maximal zoom level'),
value=self.max_zoom, options=zoom_levels, required=False, **kwargs)
def check_zoom_admin_form(self, form):
initial_zoom = form.get_widget('initial_zoom').parse()
min_zoom = form.get_widget('min_zoom').parse()
max_zoom = form.get_widget('max_zoom').parse()
if min_zoom and max_zoom:
if int(min_zoom) > int(max_zoom):
form.get_widget('min_zoom').set_error(
_('Minimal zoom level cannot be greater than maximal zoom level.'))
if (initial_zoom and min_zoom and int(initial_zoom) < int(min_zoom)) or (
(initial_zoom and max_zoom and int(initial_zoom) > int(max_zoom))):
form.get_widget('initial_zoom').set_error(
_('Initial zoom level must be between minimal and maximal zoom levels.'))
class ItemField(WidgetField, MapOptionsMixin):
key = 'item'
description = N_('List')
@ -1634,14 +1671,16 @@ class ItemField(WidgetField):
WidgetField.fill_admin_form(self, form)
form.add(CheckboxWidget, 'in_filters', title=_('Display in default filters'),
value=self.in_filters, advanced=True)
options = [('list', _('List')),
('radio', _('Radio buttons')),
('autocomplete', _('Autocomplete')),
options = [('list', _('List'), 'list'),
('radio', _('Radio buttons'), 'radio'),
('autocomplete', _('Autocomplete'), 'autocomplete'),
('map', _('Map (requires geographical data)'), 'map'),
]
form.add(RadiobuttonsWidget, 'display_mode',
title=_('Display Mode'),
options=options,
value=self.display_mode)
value=self.display_mode,
attrs={'data-dynamic-display-parent': 'true'})
real_data_source = data_sources.get_real(self.data_source)
form.add(RadiobuttonsWidget, 'data_mode',
title=_('Data'),
@ -1669,11 +1708,14 @@ class ItemField(WidgetField):
title=_('Display disabled items'),
value=self.display_disabled_items,
advanced=not(self.display_disabled_items))
self.fill_zoom_admin_form(form,
attrs={'data-dynamic-display-child-of': 'display_mode',
'data-dynamic-display-value': 'map'})
def get_admin_attributes(self):
return WidgetField.get_admin_attributes(self) + ['items',
'display_mode', 'data_source', 'in_filters', 'anonymise',
'display_disabled_items']
'display_disabled_items', 'initial_zoom', 'min_zoom', 'max_zoom']
def check_admin_form(self, form):
data_mode = form.get_widget('data_mode').parse()
@ -1690,6 +1732,8 @@ class ItemField(WidgetField):
data_source_type.set_value(None)
data_source_type.transfer_form_value(get_request())
self.check_zoom_admin_form(form)
def stats(self, values):
return item_items_stats(self, values)
@ -2420,13 +2464,10 @@ class TableRowsField(WidgetField):
register_field_class(TableRowsField)
class MapField(WidgetField):
class MapField(WidgetField, MapOptionsMixin):
key = 'map'
description = N_('Map')
initial_zoom = None
min_zoom = None
max_zoom = None
default_position = None
init_with_geoloc = False
@ -2436,22 +2477,7 @@ class MapField(WidgetField):
def fill_admin_form(self, form):
WidgetField.fill_admin_form(self, form)
# 0: whole world, 9: wide area, 11: area, 13: town, 16: small road
zoom_levels = [(None, '---'),
('0', _('Whole world')),
('6', _('Country')),
('9', _('Wide area')),
('11', _('Area')),
('13', _('Town')),
('16', _('Small road')),
('18', _('Neighbourhood')),
('19', _('Ant')),]
form.add(SingleSelectWidget, 'initial_zoom', title=_('Initial zoom level'),
value=self.initial_zoom or '13', options=zoom_levels)
form.add(SingleSelectWidget, 'min_zoom', title=_('Minimal zoom level'),
value=self.min_zoom, options=zoom_levels, required=False)
form.add(SingleSelectWidget, 'max_zoom', title=_('Maximal zoom level'),
value=self.max_zoom, options=zoom_levels, required=False)
self.fill_zoom_admin_form(form)
form.add(MapWidget, 'default_position', title=_('Initial Position'),
value=self.default_position, default_zoom='9', required=False)
form.add(CheckboxWidget, 'init_with_geoloc',
@ -2459,17 +2485,7 @@ class MapField(WidgetField):
value=self.init_with_geoloc, required=False)
def check_admin_form(self, form):
initial_zoom = form.get_widget('initial_zoom').parse()
min_zoom = form.get_widget('min_zoom').parse()
max_zoom = form.get_widget('max_zoom').parse()
if min_zoom and max_zoom:
if int(min_zoom) > int(max_zoom):
form.get_widget('min_zoom').set_error(
_('Minimal zoom level cannot be greater than maximal zoom level.'))
if (initial_zoom and min_zoom and int(initial_zoom) < int(min_zoom)) or (
(initial_zoom and max_zoom and int(initial_zoom) > int(max_zoom))):
form.get_widget('initial_zoom').set_error(
_('Initial zoom level must be between minimal and maximal zoom levels.'))
self.check_zoom_admin_form(form)
def get_admin_attributes(self):
return WidgetField.get_admin_attributes(self) + ['initial_zoom',

View File

@ -254,4 +254,17 @@ $(function() {
panned_svg.resize();
});
}
$('[type=radio][name=display_mode]').on('change', function() {
if ($(this).val() == 'map') {
$('input[name="data_mode"][value="data-source"]').click()
$('select[name="data_source$type"] option:not([data-type="geojson"])').hide();
if ($('select[name="data_source$type"] option:selected:visible').length == 0) {
$('select[name="data_source$type"] option:visible').first().prop('selected', true);
}
} else {
$('select[name="data_source$type"] option').show();
}
});
$('[type=radio][name=display_mode]').trigger('change');
});