From 29228641bfd7f044f3bb7662f9e28789da8704f6 Mon Sep 17 00:00:00 2001 From: Todd Dembrey <todd.dembrey@torchbox.com> Date: Thu, 23 Aug 2018 13:07:55 +0100 Subject: [PATCH] Make sure we correctly store and handle the score values and add tests --- opentech/apply/review/blocks.py | 2 +- opentech/apply/review/fields.py | 48 +++++-------------- opentech/apply/review/forms.py | 12 ++--- opentech/apply/review/tests/test_views.py | 17 +++++++ .../apply/stream_forms/testing/factories.py | 19 ++++++-- 5 files changed, 48 insertions(+), 50 deletions(-) diff --git a/opentech/apply/review/blocks.py b/opentech/apply/review/blocks.py index d271c864c..275f1601f 100644 --- a/opentech/apply/review/blocks.py +++ b/opentech/apply/review/blocks.py @@ -22,7 +22,7 @@ class ScoreFieldBlock(OptionalFormFieldBlock): template = 'review/render_scored_answer_field.html' def render(self, value, context=None): - comment, score = json.loads(context['data']) + comment, score = context['data'] context.update(**{ 'comment': comment, 'score': RATE_CHOICES_DICT.get(int(score), RATE_CHOICE_NA) diff --git a/opentech/apply/review/fields.py b/opentech/apply/review/fields.py index aeaeaa79e..8f1e8eee2 100644 --- a/opentech/apply/review/fields.py +++ b/opentech/apply/review/fields.py @@ -1,5 +1,3 @@ -import json - from django import forms from tinymce import TinyMCE @@ -19,44 +17,22 @@ class ScoredAnswerWidget(forms.MultiWidget): super().__init__(_widgets, attrs) def decompress(self, value): + # We should only hit this on initialisation where we set the default to a list of None if value: - return json.loads(value) + return value return [None, None] def render(self, name, value, attrs=None, renderer=None): - """ - Render the widget as an HTML string. - Required for the correct rendering of the TinyMCE widget. - """ - if self.is_localized: - for widget in self.widgets: - widget.is_localized = self.is_localized - # value is a list of values, each corresponding to a widget - # in self.widgets. - if not isinstance(value, list): - value = self.decompress(value) - + context = self.get_context(name, value, attrs) rendered = [] - final_attrs = self.build_attrs(attrs) - input_type = final_attrs.pop('type', None) - id_ = final_attrs.get('id') - for i, widget in enumerate(self.widgets): - if input_type is not None: - widget.input_type = input_type - widget_name = '%s_%s' % (name, i) - try: - widget_value = value[i] - except IndexError: - widget_value = None - if id_: - widget_attrs = final_attrs.copy() - widget_attrs['id'] = '%s_%s' % (id_, i) - else: - widget_attrs = final_attrs - - rendered.append(widget.render(widget_name, widget_value, widget_attrs, renderer)) - - return ''.join([mark_safe(item) for item in rendered]) + # We need to explicitly call the render method on the tinymce widget + # MultiValueWidget just passes all the context into the template + for kwargs, widget in zip(context['widget']['subwidgets'], self.widgets): + name = kwargs['name'] + value = kwargs['value'] + attrs = kwargs['attrs'] + rendered.append(widget.render(name, value, attrs, renderer)) + return mark_safe(''.join([widget for widget in rendered])) class ScoredAnswerField(forms.MultiValueField): @@ -71,4 +47,4 @@ class ScoredAnswerField(forms.MultiValueField): super().__init__(fields=fields, *args, **kwargs) def compress(self, data_list): - return json.dumps(data_list) + return [data_list[0], int(data_list[1])] diff --git a/opentech/apply/review/forms.py b/opentech/apply/review/forms.py index 6ff588cd2..28a206afe 100644 --- a/opentech/apply/review/forms.py +++ b/opentech/apply/review/forms.py @@ -78,15 +78,9 @@ class ReviewModelForm(StreamBaseForm, forms.ModelForm, metaclass=MixedMetaClass) scores = list() for field in self.instance.score_fields: - value = json.loads(data.get(field.id, '[null, null]')) - - try: - score = int(value[1]) - except TypeError: - pass - else: - if score != NA: - scores.append(score) + score = data.get(field.id)[1] + if score != NA: + scores.append(score) try: return sum(scores) / len(scores) diff --git a/opentech/apply/review/tests/test_views.py b/opentech/apply/review/tests/test_views.py index 9b21dcabf..2a4ded257 100644 --- a/opentech/apply/review/tests/test_views.py +++ b/opentech/apply/review/tests/test_views.py @@ -115,6 +115,23 @@ class StaffReviewFormTestCase(BaseViewTestCase): review = self.submission.reviews.first() self.assertEqual(review.score, score) + def test_average_score_calculated(self): + form = ReviewFormFactory(form_fields__multiple__score=2) + review_form = self.submission.round.review_forms.first() + review_form.form = form + review_form.save() + + score_1, score_2 = 1, 5 + + data = ReviewFormFieldsFactory.form_response(form.form_fields, { + field.id: {'score': score} + for field, score in zip(form.score_fields, [score_1, score_2]) + }) + + self.post_page(self.submission, data, 'form') + review = self.submission.reviews.first() + self.assertEqual(review.score, (score_1 + score_2) / 2) + def test_no_score_is_NA(self): form = ReviewFormFactory(form_fields__exclude__score=True) review_form = self.submission.round.review_forms.first() diff --git a/opentech/apply/stream_forms/testing/factories.py b/opentech/apply/stream_forms/testing/factories.py index 398a54607..f742d6c50 100644 --- a/opentech/apply/stream_forms/testing/factories.py +++ b/opentech/apply/stream_forms/testing/factories.py @@ -177,29 +177,40 @@ class StreamFieldUUIDFactory(wagtail_factories.StreamFieldFactory): def build_form(self, data): extras = defaultdict(dict) exclusions = [] + multiples = dict() for field, value in data.items(): # we dont care about position name, attr = field.split('__') if name == 'exclude': exclusions.append(attr) + elif name == 'multiple': + multiples[attr] = value else: extras[name] = {attr: value} + defined_both = set(exclusions) & set(multiples) + if defined_both: + raise ValueError( + 'Cant exclude and provide multiple at the same time: {}'.format(', '.join(defined_both)) + ) form_fields = {} - for i, field in enumerate(self.factories): + field_count = 0 + for field in self.factories: if field == 'text_markup' or field in exclusions: pass else: - form_fields[f'{i}__{field}__'] = '' + for _ in range(multiples.get(field, 1)): + form_fields[f'{field_count}__{field}__'] = '' + field_count += 1 for attr, value in extras[field].items(): - form_fields[f'{i}__{field}__{attr}'] = value + form_fields[f'{field_count}__{field}__{attr}'] = value return form_fields def form_response(self, fields, field_values=dict()): data = { - field.id: self.factories[field.block.name].make_form_answer(field_values.get(field, {})) + field.id: self.factories[field.block.name].make_form_answer(field_values.get(field.id, {})) for field in fields if hasattr(self.factories[field.block.name], 'make_form_answer') } -- GitLab