-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add feedback survey This follows the pattern set by GOV.UK but stores responses in the database. We are building our own because we're in private beta this is an internal service (not on GOV.UK) The form is linked from the MOJ internal header, rather than the phase banner where we would put it on an external service. * fix: make sure RadioSelect doesn't have a blank choice * fix: handle form errors in the template * feat: add command to export survey data * fix: add env var for CSRF_TRUSTED_ORIGINS
- Loading branch information
Showing
24 changed files
with
361 additions
and
1,578 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.contrib import admin | ||
|
||
# Register your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class FeedbackConfig(AppConfig): | ||
default_auto_field = "django.db.models.BigAutoField" | ||
name = "feedback" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from django.forms import ModelForm | ||
from django.forms.widgets import RadioSelect | ||
|
||
from .models import Feedback | ||
|
||
|
||
def formfield(field, **kwargs): | ||
""" | ||
Wrapper around the db model's formfield method that maps db fields to form fields. | ||
This is a workaround to prevent a blank choice being included in the ChoiceField. | ||
By default, if the model field has no default value (or blank is set to True), | ||
then it will include this empty value, even if we have customised the widget is customised. | ||
""" | ||
return field.formfield(initial=None, **kwargs) | ||
|
||
|
||
class FeedbackForm(ModelForm): | ||
class Meta: | ||
model = Feedback | ||
fields = ["satisfaction_rating", "how_can_we_improve"] | ||
widgets = { | ||
"satisfaction_rating": RadioSelect(attrs={"class": "govuk-radios__input"}) | ||
} | ||
formfield_callback = formfield |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import csv | ||
from sys import stdout | ||
|
||
from django.core.management.base import BaseCommand | ||
|
||
from feedback.models import Feedback | ||
|
||
|
||
class Command(BaseCommand): | ||
help = "Export feedback survey data" | ||
|
||
def handle(self, *args, **options): | ||
writer = csv.writer(stdout) | ||
writer.writerow(["satisfaction_rating", "how_can_we_improve"]) | ||
|
||
for feedback in Feedback.objects.all(): | ||
writer.writerow([feedback.satisfaction_rating, feedback.how_can_we_improve]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Generated by Django 5.0.7 on 2024-08-13 11:31 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="Feedback", | ||
fields=[ | ||
( | ||
"id", | ||
models.BigAutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
( | ||
"satisfaction_rating", | ||
models.IntegerField( | ||
choices=[ | ||
(5, "Very satisfied"), | ||
(4, "Satisfied"), | ||
(3, "Neither satisfied or dissatisfied"), | ||
(2, "Dissatisfied"), | ||
(1, "Very dissatisfied"), | ||
], | ||
verbose_name="Satisfaction survey", | ||
), | ||
), | ||
( | ||
"how_can_we_improve", | ||
models.TextField(verbose_name="How can we improve this service?"), | ||
), | ||
], | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from django.db import models | ||
from django.utils.translation import gettext as _ | ||
|
||
|
||
class Feedback(models.Model): | ||
SATISFACTION_RATINGS = [ | ||
(5, "Very satisfied"), | ||
(4, "Satisfied"), | ||
(3, "Neither satisfied or dissatisfied"), | ||
(2, "Dissatisfied"), | ||
(1, "Very dissatisfied"), | ||
] | ||
|
||
satisfaction_rating = models.IntegerField( | ||
choices=SATISFACTION_RATINGS, | ||
verbose_name=_("Satisfaction survey"), | ||
null=False, | ||
blank=False, | ||
) | ||
|
||
how_can_we_improve = models.TextField( | ||
verbose_name=_("How can we improve this service?"), null=False, blank=True | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
{% extends "base/base.html" %} | ||
{% load static %} | ||
{% load i18n %} | ||
|
||
{% block content %} | ||
<div class="govuk-grid-row"> | ||
<div class="govuk-grid-column-full"> | ||
<h1 class="govuk-heading-xl">{{h1_value}}</h1> | ||
</div> | ||
|
||
<div class="govuk-grid-column-two-thirds"> | ||
<form action="{% url 'feedback:feedback' %}" method="post" novalidate> | ||
|
||
{% if form.errors %} | ||
<div class="govuk-error-summary" data-module="govuk-error-summary"> | ||
<div role="alert"> | ||
<h2 class="govuk-error-summary__title"> | ||
{% translate "There is a problem" %} | ||
</h2> | ||
<div class="govuk-error-summary__body"> | ||
<p class="govuk-body">{% translate 'Make sure you have filled in all the fields.' %}</p> | ||
</div> | ||
</div> | ||
</div> | ||
{% endif %} | ||
|
||
<div class="govuk-form-group {% if form.satisfaction_rating.errors %}govuk-form-group-errors{% endif %}"> | ||
<fieldset class="govuk-fieldset"> | ||
<legend class="govuk-fieldset__legend govuk-fieldset__legend--l"> | ||
<h2 class="govuk-fieldset__heading"> | ||
{{form.satisfaction_rating.label}} | ||
</h2> | ||
</legend> | ||
{% for error in form.satisfaction_rating.errors %} | ||
<p id="passport-issued-error" class="govuk-error-message"> | ||
<span class="govuk-visually-hidden">Error:</span> {{error}} | ||
</p> | ||
{% endfor %} | ||
<div class="govuk-radios" data-module="govuk-radios"> | ||
{% for radio in form.satisfaction_rating %} | ||
<div class="govuk-radios__item"> | ||
{{radio.tag}} | ||
<label class="govuk-label govuk-radios__label" for="{{radio.id_for_label}}"> | ||
{{radio.choice_label}} | ||
</label> | ||
</div> | ||
{% endfor %} | ||
</div> | ||
</fieldset> | ||
</div> | ||
<div class="govuk-form-group"> | ||
<h2 class="govuk-label-wrapper"> | ||
<label class="govuk-label govuk-label--l" for="{{form.how_can_we_improve.id_for_label}}"> | ||
{{form.how_can_we_improve.label}} | ||
</label> | ||
</h2> | ||
<div id="more-detail-hint" class="govuk-hint"> | ||
{% translate 'Do not include personal or financial information, like your National Insurance number or credit card details.' %} | ||
</div> | ||
<textarea class="govuk-textarea" id="{{form.how_can_we_improve.id_for_label}}" name="{{form.how_can_we_improve.html_name}}" rows="5" aria-describedby="more-detail-hint"></textarea> | ||
</div> | ||
<button type="submit" class="govuk-button" data-module="govuk-button"> | ||
Send feedback | ||
</button> | ||
{% csrf_token %} | ||
</form> | ||
</div> | ||
</div> | ||
{% endblock content %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{% extends "base/base.html" %} | ||
{% load i18n %} | ||
{% load static %} | ||
|
||
{% block content %} | ||
<div class="govuk-grid-row"> | ||
<div class="govuk-grid-column-two-thirds"> | ||
<h1 class="govuk-heading-xl"> | ||
{% translate 'Thank you for your feedback' %} | ||
</h1> | ||
|
||
<p class="govuk-body">{% translate 'Your feedback will help us improve the service.' %}</p> | ||
</div> | ||
|
||
</div> | ||
{% endblock content %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.test import TestCase | ||
|
||
# Create your tests here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from django.urls import path | ||
|
||
from . import views | ||
|
||
app_name = "feedback" | ||
|
||
urlpatterns = [ | ||
path("", views.feedback_form_view, name="feedback"), | ||
path("thanks", views.thank_you_view, name="thanks"), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import logging | ||
|
||
from django.http import HttpResponse | ||
from django.shortcuts import redirect, render | ||
from django.utils.translation import gettext as _ | ||
|
||
from .forms import FeedbackForm | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
def feedback_form_view(request) -> HttpResponse: | ||
if request.method == "POST": | ||
form = FeedbackForm(request.POST) | ||
if form.is_valid(): | ||
form.save() | ||
return redirect("feedback:thanks") | ||
else: | ||
log.error(f"Unexpected invalid feedback form submission: {form.errors}") | ||
else: | ||
form = FeedbackForm() | ||
|
||
return render( | ||
request, | ||
"feedback.html", | ||
{ | ||
"h1_value": _("Give feedback on Find MOJ data"), | ||
"form": form, | ||
}, | ||
) | ||
|
||
|
||
def thank_you_view(request) -> HttpResponse: | ||
return render( | ||
request, | ||
"thanks.html", | ||
{"h1_value": _("Thank you for your feedback")}, | ||
) |
Oops, something went wrong.