Skip to content

Commit a09b13d

Browse files
authored
Reviews rate options configurable on score fields (#5024)
By default nothing changes. Setting the new optional `REVIEW_RATE_CHOICES` implementors can change labels and more importantly disable rate options. You could change the labels before via translations but there was no way to disable options. By setting `REVIEW_RATE_CHOICES = {99: None}` in locale.py or setting REVIEW_RATE_CHOICES='{"99": null}' in .env will disable the NA option.
1 parent 00e111f commit a09b13d

5 files changed

Lines changed: 70 additions & 11 deletions

File tree

‎docs/setup/administrators/configuration.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ Show the total (sum) of a review's scored answers instead of the average of them
213213

214214
----
215215

216+
Change the labels of, or disable, review score choices. A JSON object keyed by score value (0-5 and 99 for "n/a - choose not to answer"). A string sets a new label, `null`, `false` or `""` disables the choice. If the n/a choice is disabled, score fields start out empty ("---------") instead of at n/a, so in required score fields reviewers have to pick a score. An optional score field left empty still counts as n/a. Existing reviews that used a disabled choice still show its label. Labels set here are not translated.
217+
218+
Example: `REVIEW_RATE_CHOICES='{"0": null, "3": "3. Okay", "99": "n/a"}'`
219+
220+
REVIEW_RATE_CHOICES = env.json('REVIEW_RATE_CHOICES', {})
221+
222+
----
223+
216224
If automatic e-mails should be sent out to reviewers when submissions are ready for review.
217225

218226
SEND_READY_FOR_REVIEW = env.bool('SEND_READY_FOR_REVIEW', True)

‎hypha/apply/review/blocks.py‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from django import forms
44
from django.conf import settings
5+
from django.db.models import BLANK_CHOICE_DASH
56
from django.utils.safestring import mark_safe
67
from django.utils.translation import gettext_lazy as _
78
from wagtail.blocks import RichTextBlock
@@ -69,7 +70,8 @@ class ScoreFieldWithoutTextBlock(OptionalFormFieldBlock):
6970
This block modifies RATE_CHOICES to have empty string('') in place of NA
7071
for text value `n/a - choose not to answer` as it helps to render this value
7172
as default to the forms and also when this field is
72-
required it automatically handles validation on empty string.
73+
required it automatically handles validation on empty string. If NA is
74+
disabled an empty choice is added first instead.
7375
"""
7476

7577
name = "score without text"
@@ -87,8 +89,7 @@ def get_field_kwargs(self, struct_value):
8789
def render(self, value, context=None):
8890
data = context.get("data", None)
8991
if data:
90-
choices = dict(self.get_choices(RATE_CHOICES))
91-
context["data"] = choices[int(data)]
92+
context["data"] = RATE_CHOICES_DICT.get(int(data), RATE_CHOICE_NA)
9293
else:
9394
context["data"] = ""
9495

@@ -98,9 +99,11 @@ def get_choices(self, choices):
9899
"""
99100
Replace 'NA' option with an empty string choice.
100101
"""
101-
rate_choices = list(choices)
102-
rate_choices.pop(-1)
103-
rate_choices.append(("", _("n/a - choose not to answer")))
102+
rate_choices = [choice for choice in choices if choice[0] != NA]
103+
if len(rate_choices) < len(choices):
104+
rate_choices.append(("", RATE_CHOICE_NA))
105+
else:
106+
rate_choices = BLANK_CHOICE_DASH + rate_choices
104107
return tuple(rate_choices)
105108

106109

‎hypha/apply/review/fields.py‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django import forms
2+
from django.db.models import BLANK_CHOICE_DASH
23
from django.forms import widgets
34
from django.utils.safestring import mark_safe
45
from tinymce.widgets import TinyMCE
@@ -7,11 +8,23 @@
78
from hypha.apply.utils.options import MCE_ATTRIBUTES_SHORT
89

910

11+
def get_score_choices():
12+
"""
13+
The NA choice is the default. If it is disabled, add an empty choice
14+
in its place so no score is preselected.
15+
"""
16+
if NA in dict(RATE_CHOICES):
17+
return RATE_CHOICES
18+
return tuple(BLANK_CHOICE_DASH) + RATE_CHOICES
19+
20+
1021
class ScoredAnswerWidget(forms.MultiWidget):
1122
def __init__(self, attrs=None):
1223
_widgets = (
1324
TinyMCE(attrs=attrs, mce_attrs=MCE_ATTRIBUTES_SHORT),
14-
widgets.Select(attrs={"data-score-field": "true"}, choices=RATE_CHOICES),
25+
widgets.Select(
26+
attrs={"data-score-field": "true"}, choices=get_score_choices()
27+
),
1528
)
1629
super().__init__(_widgets, attrs)
1730

@@ -42,13 +55,15 @@ class ScoredAnswerField(forms.MultiValueField):
4255
def __init__(self, *args, **kwargs):
4356
fields = (
4457
forms.CharField(),
45-
forms.ChoiceField(choices=RATE_CHOICES),
58+
forms.ChoiceField(choices=get_score_choices()),
4659
)
4760

4861
super().__init__(*args, **kwargs, fields=fields)
4962

5063
def compress(self, data_list):
5164
if data_list:
52-
return [data_list[0], int(data_list[1])]
65+
# An empty score is only possible when NA is disabled.
66+
score = data_list[1]
67+
return [data_list[0], int(score) if score != "" else NA]
5368
else:
5469
return ["", NA]

‎hypha/apply/review/options.py‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
from django.conf import settings
12
from django.utils.translation import gettext_lazy as _
23

34
NA = 99
45

5-
RATE_CHOICES = (
6+
DEFAULT_RATE_CHOICES = (
67
(0, _("0. Need more info")),
78
(1, _("1. Poor")),
89
(2, _("2. Not so good")),
@@ -12,7 +13,33 @@
1213
(NA, _("n/a - choose not to answer")),
1314
)
1415

15-
RATE_CHOICES_DICT = dict(RATE_CHOICES)
16+
17+
def get_rate_choices():
18+
"""
19+
Apply the REVIEW_RATE_CHOICES setting to DEFAULT_RATE_CHOICES.
20+
21+
The setting maps a score value (int or str) to a new label, or to a falsy
22+
value to disable that choice.
23+
24+
Returns ``(choices, labels)`` where ``labels`` also includes disabled
25+
choices, so existing reviews using them can still be displayed.
26+
"""
27+
overrides = {
28+
str(key): value
29+
for key, value in getattr(settings, "REVIEW_RATE_CHOICES", {}).items()
30+
}
31+
choices = []
32+
labels = {}
33+
for value, default_label in DEFAULT_RATE_CHOICES:
34+
override = overrides.get(str(value), default_label)
35+
label = override if isinstance(override, str) and override else default_label
36+
labels[value] = label
37+
if override:
38+
choices.append((value, label))
39+
return tuple(choices), labels
40+
41+
42+
RATE_CHOICES, RATE_CHOICES_DICT = get_rate_choices()
1643
RATE_CHOICE_NA = RATE_CHOICES_DICT[NA]
1744

1845
NO = 0

‎hypha/settings/base.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@
220220
# them, wherever a review score is shown.
221221
REVIEW_SCORE_SHOW_TOTAL = env.bool("REVIEW_SCORE_SHOW_TOTAL", False)
222222

223+
# Override labels of, or disable, review score choices. JSON object keyed by
224+
# score value; a string sets a new label, null/false/"" disables the choice.
225+
# Disabling the n/a choice (99) makes reviewers pick a score in required fields.
226+
# Example: {"0": null, "3": "3. Okay", "99": "n/a"}
227+
REVIEW_RATE_CHOICES = env.json("REVIEW_RATE_CHOICES", {})
228+
223229
# Require an applicant to view their rendered application before submitting
224230
SUBMISSION_PREVIEW_REQUIRED = env.bool("SUBMISSION_PREVIEW_REQUIRED", True)
225231

0 commit comments

Comments
 (0)