dataviz: refactor building of choice list (#71885)

This commit is contained in:
Valentin Deniaud 2023-04-20 11:11:28 +02:00
parent af473d684e
commit e8bd91b44e
1 changed files with 52 additions and 45 deletions

View File

@ -14,8 +14,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import dataclasses
import datetime
from collections import OrderedDict
from collections import OrderedDict, defaultdict
from django import forms
from django.conf import settings
@ -33,6 +34,21 @@ from .fields import StaticField
from .models import ChartCell, ChartFiltersCell, ChartNgCell
@dataclasses.dataclass
class Choice:
id: str
label: str
group: str = None
@staticmethod
def get_field_choices(choices):
choices_by_group = defaultdict(list)
for choice in choices:
choices_by_group[choice.group].append((choice.id, choice.label))
return list(choices_by_group.items())
class ChartForm(forms.ModelForm):
class Meta:
model = ChartCell
@ -60,10 +76,10 @@ def trigger_statistics_list_refresh():
class ChartFiltersMixin:
time_intervals = (
('week', _('Week')),
('month', _('Month')),
('year', _('Year')),
('weekday', _('Week day')),
Choice('week', _('Week')),
Choice('month', _('Month')),
Choice('year', _('Year')),
Choice('weekday', _('Week day')),
)
def get_filter_fields(self, cell):
@ -95,16 +111,15 @@ class ChartFiltersMixin:
def build_choice_field(self, cell, filter_, initial):
filter_id = filter_['id']
has_option_groups = isinstance(filter_['options'][0], list)
if filter_['options'] and has_option_groups:
choices = {
group: [(opt['id'], opt['label']) for opt in options] for group, options in filter_['options']
}
choices_to_complete = choices[None] = choices.get(None, [])
choices = list(choices.items())
else:
choices = [(option['id'], option['label']) for option in filter_['options']]
choices_to_complete = choices
filter_options = filter_['options']
if not isinstance(filter_options[0], list):
# no option groups, add empty one for consistency
filter_options = [(None, filter_options)]
choices = [
Choice(id=opt['id'], label=opt['label'], group=group)
for group, options in filter_options
for opt in options
]
if filter_id == 'time_interval':
self.extend_time_interval_choices(choices)
@ -112,37 +127,39 @@ class ChartFiltersMixin:
required = filter_.get('required', False)
multiple = filter_.get('multiple')
if not required:
choices_to_complete.insert(0, BLANK_CHOICE_DASH[0])
choices.insert(0, Choice(*BLANK_CHOICE_DASH[0]))
extra_variables = cell.page.get_extra_variables_keys()
variable_choices = [('variable:' + key, key) for key in extra_variables]
variable_choices = [
Choice(id='variable:' + key, label=key, group=_('Page variables')) for key in extra_variables
]
if has_option_groups:
possible_choices = {choice[0] for _, group_choices in choices for choice in group_choices}
else:
possible_choices = {choice[0] for choice in choices}
for choice in initial if isinstance(initial, list) else [initial]:
if not choice:
continue
if choice.startswith('variable:'):
variable = choice.replace('variable:', '')
if not variable in extra_variables:
variable_choices.append((choice, _('%s (unavailable)') % variable))
elif choice not in possible_choices:
choices_to_complete.append((choice, _('%s (unavailable)') % choice))
variable_choices.append(
Choice(id=choice, label=_('%s (unavailable)') % variable, group=_('Page variables'))
)
elif not any(x.id == choice for x in choices):
choices.append(Choice(id=choice, label=_('%s (unavailable)') % choice))
if variable_choices and not multiple and filter_id != 'time_interval':
choices.append((_('Page variables'), variable_choices))
choices.extend(variable_choices)
field_class = forms.MultipleChoiceField if multiple else forms.ChoiceField
widget_class = MultiSelectWidget if multiple else forms.Select
return field_class(
field = field_class(
label=filter_['label'],
choices=choices,
choices=Choice.get_field_choices(choices),
required=required,
initial=initial,
widget=widget_class,
)
field.dataviz_choices = choices
return field
def build_boolean_field(self, cell, filter_, initial):
return forms.BooleanField(
@ -152,10 +169,9 @@ class ChartFiltersMixin:
)
def extend_time_interval_choices(self, choices):
choice_ids = {choice_id for choice_id, _ in choices}
if 'day' in choice_ids:
if any(choice.id == 'day' for choice in choices):
for choice in self.time_intervals:
if choice[0] not in choice_ids:
if choice not in choices:
choices.append(choice)
def update_time_range_choices(self, statistic, exclude_template_choice=False):
@ -399,21 +415,12 @@ class ChartFiltersForm(ChartFiltersMixin, forms.ModelForm):
if field_name not in dynamic_fields or isinstance(field, forms.BooleanField):
continue
has_option_groups = isinstance(dynamic_fields[field_name].choices[0][1], list)
if has_option_groups and isinstance(field.choices[0][1], list):
new_choices = []
field_choices = {group_label: choices for group_label, choices in field.choices}
for group_label, choices in dynamic_fields[field_name].choices:
if group_label not in field_choices:
continue
new_choices.append(
(group_label, [x for x in choices if x in field_choices[group_label]])
)
dynamic_fields[field_name].choices = new_choices
else:
dynamic_fields[field_name].choices = [
x for x in dynamic_fields[field_name].choices if x in field.choices
]
dynamic_fields[field_name].dataviz_choices = [
x for x in dynamic_fields[field_name].dataviz_choices if x in field.dataviz_choices
]
dynamic_fields[field_name].choices = Choice.get_field_choices(
dynamic_fields[field_name].dataviz_choices
)
if dynamic_fields[field_name].choices == []:
del dynamic_fields[field_name]