From 18f03eb5c63760692ca72b9a7d9d8abed5e31b6b Mon Sep 17 00:00:00 2001
From: sks444 <krishnasingh.ss30@gmail.com>
Date: Mon, 20 Jul 2020 16:25:51 +0530
Subject: [PATCH] Implement score field without text box block

---
 hypha/apply/review/blocks.py                  | 36 +++++++++++++++++++
 hypha/apply/review/forms.py                   |  9 +++++
 .../0023_add_score_without_text_block.py      | 26 ++++++++++++++
 hypha/apply/review/models.py                  |  5 +++
 hypha/apply/review/options.py                 | 12 +++++++
 5 files changed, 88 insertions(+)
 create mode 100644 hypha/apply/review/migrations/0023_add_score_without_text_block.py

diff --git a/hypha/apply/review/blocks.py b/hypha/apply/review/blocks.py
index 35a4cb38e..8598e5d61 100644
--- a/hypha/apply/review/blocks.py
+++ b/hypha/apply/review/blocks.py
@@ -12,6 +12,7 @@ from hypha.apply.review.options import (
     RATE_CHOICE_NA,
     RATE_CHOICES_DICT,
     RECOMMENDATION_CHOICES,
+    SCORE_CHOICES,
     VISIBILILTY_HELP_TEXT,
     VISIBILITY,
 )
@@ -53,6 +54,40 @@ class ScoreFieldBlock(OptionalFormFieldBlock):
         return super().render(value, context)
 
 
+class ScoreFieldWithoutTextBlock(OptionalFormFieldBlock):
+    """
+    There are two ways score could be accepted on reviews.
+
+    One is to use ScoreFieldBlock, where you need to put text answer along with
+    giving score on the review.
+
+    Second is to use this block to just select a reasonable score with adding
+    any text as answer.
+
+    This block uses SCORE_CHOICES instead of RATE_CHOICES, only difference is to
+    have empty string('') in place of NA for text value `n/a - choose not to answer`
+    as it helps to render this value as default to the forms and also when this field is
+    required it automatically handles validation on empty string.
+    """
+    name = 'score without text'
+    field_class = forms.ChoiceField
+
+    class Meta:
+        icon = 'order'
+
+    def get_field_kwargs(self, struct_value):
+        kwargs = super().get_field_kwargs(struct_value)
+        kwargs['choices'] = SCORE_CHOICES
+        return kwargs
+
+    def render(self, value, context=None):
+        data = int(context['data'])
+        choices = dict(SCORE_CHOICES)
+        context['data'] = choices[data]
+
+        return super().render(value, context)
+
+
 class ReviewMustIncludeFieldBlock(MustIncludeFieldBlock):
     pass
 
@@ -117,6 +152,7 @@ class ReviewCustomFormFieldsBlock(CustomFormFieldsBlock):
     text = TextFieldBlock(group=_('Fields'))
     text_markup = RichTextBlock(group=_('Fields'), label=_('Paragraph'))
     score = ScoreFieldBlock(group=_('Fields'))
+    score_without_text = ScoreFieldWithoutTextBlock(group=_('Fields'))
     checkbox = CheckboxFieldBlock(group=_('Fields'))
     dropdown = DropdownFieldBlock(group=_('Fields'))
 
diff --git a/hypha/apply/review/forms.py b/hypha/apply/review/forms.py
index 76b636818..cf9641f0e 100644
--- a/hypha/apply/review/forms.py
+++ b/hypha/apply/review/forms.py
@@ -89,6 +89,15 @@ class ReviewModelForm(StreamBaseForm, forms.ModelForm, metaclass=MixedMetaClass)
                 score = 0
             scores.append(score)
 
+        # Check if there are score_fields_without_text and also
+        # append scores from them.
+        for field in self.instance.score_fields_without_text:
+            score = int(data.get(field.id))
+            # Include '' answers as 0.
+            if score == '':
+                score = 0
+            scores.append(score)
+
         try:
             return sum(scores) / len(scores)
         except ZeroDivisionError:
diff --git a/hypha/apply/review/migrations/0023_add_score_without_text_block.py b/hypha/apply/review/migrations/0023_add_score_without_text_block.py
new file mode 100644
index 000000000..d6dccfb47
--- /dev/null
+++ b/hypha/apply/review/migrations/0023_add_score_without_text_block.py
@@ -0,0 +1,26 @@
+# Generated by Django 2.2.13 on 2020-07-20 10:55
+
+from django.db import migrations
+import wagtail.core.blocks
+import wagtail.core.blocks.static_block
+import wagtail.core.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('review', '0022_add_word_limit_to_text_blocks'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='review',
+            name='form_fields',
+            field=wagtail.core.fields.StreamField([('rich_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False)), ('word_limit', wagtail.core.blocks.IntegerBlock(default=1000, label='Word limit'))], group='Fields')), ('markdown_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False)), ('word_limit', wagtail.core.blocks.IntegerBlock(default=1000, label='Word limit'))], group='Fields')), ('char', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('format', wagtail.core.blocks.ChoiceBlock(choices=[('email', 'Email'), ('url', 'URL')], label='Format', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False)), ('word_limit', wagtail.core.blocks.IntegerBlock(default=1000, label='Word limit'))], group='Fields')), ('text_markup', wagtail.core.blocks.RichTextBlock(group='Fields', label='Paragraph')), ('score', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('score_without_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('checkbox', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.BooleanBlock(required=False))], group='Fields')), ('dropdown', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('recommendation', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('comments', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('visibility', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required'))]),
+        ),
+        migrations.AlterField(
+            model_name='reviewform',
+            name='form_fields',
+            field=wagtail.core.fields.StreamField([('rich_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False)), ('word_limit', wagtail.core.blocks.IntegerBlock(default=1000, label='Word limit'))], group='Fields')), ('markdown_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False)), ('word_limit', wagtail.core.blocks.IntegerBlock(default=1000, label='Word limit'))], group='Fields')), ('char', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('format', wagtail.core.blocks.ChoiceBlock(choices=[('email', 'Email'), ('url', 'URL')], label='Format', required=False)), ('default_value', wagtail.core.blocks.CharBlock(label='Default value', required=False))], group='Fields')), ('text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.TextBlock(label='Default value', required=False)), ('word_limit', wagtail.core.blocks.IntegerBlock(default=1000, label='Word limit'))], group='Fields')), ('text_markup', wagtail.core.blocks.RichTextBlock(group='Fields', label='Paragraph')), ('score', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('score_without_text', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False))], group='Fields')), ('checkbox', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('default_value', wagtail.core.blocks.BooleanBlock(required=False))], group='Fields')), ('dropdown', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('required', wagtail.core.blocks.BooleanBlock(label='Required', required=False)), ('choices', wagtail.core.blocks.ListBlock(wagtail.core.blocks.CharBlock(label='Choice')))], group='Fields')), ('recommendation', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('comments', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required')), ('visibility', wagtail.core.blocks.StructBlock([('field_label', wagtail.core.blocks.CharBlock(label='Label')), ('help_text', wagtail.core.blocks.TextBlock(label='Help text', required=False)), ('help_link', wagtail.core.blocks.URLBlock(label='Help link', required=False)), ('info', wagtail.core.blocks.static_block.StaticBlock())], group=' Required'))]),
+        ),
+    ]
diff --git a/hypha/apply/review/models.py b/hypha/apply/review/models.py
index de821de34..528db5b54 100644
--- a/hypha/apply/review/models.py
+++ b/hypha/apply/review/models.py
@@ -20,6 +20,7 @@ from .blocks import (
     RecommendationCommentsBlock,
     ReviewCustomFormFieldsBlock,
     ScoreFieldBlock,
+    ScoreFieldWithoutTextBlock,
     VisibilityBlock,
 )
 from .options import (
@@ -46,6 +47,10 @@ class ReviewFormFieldsMixin(models.Model):
     def score_fields(self):
         return self._get_field_type(ScoreFieldBlock, many=True)
 
+    @property
+    def score_fields_without_text(self):
+        return self._get_field_type(ScoreFieldWithoutTextBlock, many=True)
+
     @property
     def recommendation_field(self):
         return self._get_field_type(RecommendationBlock)
diff --git a/hypha/apply/review/options.py b/hypha/apply/review/options.py
index 3bcdde018..7bb125260 100644
--- a/hypha/apply/review/options.py
+++ b/hypha/apply/review/options.py
@@ -9,6 +9,18 @@ RATE_CHOICES = (
     (5, '5. Excellent'),
     (NA, 'n/a - choose not to answer'),
 )
+
+SCORE_CHOICES = (
+    (0, '0. Need more info'),
+    (1, '1. Poor'),
+    (2, '2. Not so good'),
+    (3, '3. Is o.k.'),
+    (4, '4. Good'),
+    (5, '5. Excellent'),
+    ('', 'n/a - choose not to answer'),
+)
+
+
 RATE_CHOICES_DICT = dict(RATE_CHOICES)
 RATE_CHOICE_NA = RATE_CHOICES_DICT[NA]
 
-- 
GitLab