Skip to content

Commit 453f90c

Browse files
Merge pull request #328 from RSE-Sheffield/feat/survey-read-only
Prevent survey changes after responses received
2 parents d18cce6 + 9bbd777 commit 453f90c

File tree

5 files changed

+47
-3
lines changed

5 files changed

+47
-3
lines changed

home/templates/projects/project.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ <h2 class="text-2xl font-bold d-inline-block">Surveys</h2>
5555
<div class="card-body">
5656
<h3 class="card-title" title="Survey identifier {{ survey.pk }}">{{ survey.name }}</h3>
5757
<div class="card-text">
58-
<span>Responses: {{ survey.survey_response.count | default:0 }}</span>
58+
<span>Responses: {{ survey.responses_count }}</span>
5959
</div>
6060
<div class="mt-2 w-100 d-flex flex-wrap justify-content-end gap-2">
6161
{% if can_edit|get_item:survey.id %}

survey/models.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,20 @@ def _generate_random_field_value(cls, field_config):
134134
def accept_response(self, answers: list):
135135
return SurveyResponse.objects.create(survey=self, answers=answers)
136136

137+
@property
138+
def responses_count(self) -> int:
139+
"""
140+
The number of submitted questionnaires.
141+
"""
142+
return self.survey_response.count()
143+
144+
@property
145+
def has_responses(self) -> bool:
146+
"""
147+
Does this survey have any responses?
148+
"""
149+
return self.survey_response.exists()
150+
137151

138152
class SurveyEvidenceSection(models.Model):
139153
"""

survey/services/survey.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ def update_consent_demography_config(
9797
demography_config,
9898
survey_body_path,
9999
) -> Survey:
100+
"""
101+
Modify demographics fields on this survey
102+
"""
103+
104+
if survey.has_responses:
105+
raise PermissionDenied("Cannot modify survey with responses")
106+
100107
survey.consent_config = consent_config
101108
survey.demography_config = demography_config
102109
survey.survey_body_path = survey_body_path

survey/templates/survey/survey.html

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ <h1 title="Reference number {{ survey.reference_number }}">{{ survey.name }}</h1
5151
</div>
5252
<div class="d-flex justify-content-end mt-3">
5353
<div>
54-
<a href="{% url 'survey_duplicate_config' survey.pk %}" class="btn btn-primary"><i class='bx bxs-copy' ></i> Create a new survey using current configurations.</a>
54+
<a href="{% url 'survey_duplicate_config' survey.pk %}" class="btn btn-primary"
55+
title="Create a new survey using current configurations.">
56+
<i class='bx bxs-copy' ></i> Duplicate</a>
5557
</div>
5658
</div>
5759
</div>
@@ -68,10 +70,24 @@ <h2>1. Configure Your Survey</h2>
6870
</div>
6971
<div class="card-body">
7072
<div class="mb-3 mt-3">
73+
{% if survey.has_responses %}
74+
<div class="alert alert-warning">
75+
<p>
76+
⚠️ The survey cannot be modified after responses have been collected.
77+
Consider duplicating the survey instead.
78+
</p>
79+
</div>
80+
{% endif %}
7181
<p>First, configure you survey here. You are able to add and customise demographic questions asked
7282
in the survey.</p>
83+
<p>After the survey receives any responses you will no longer be able to modify the questions.</p>
84+
{% if survey.has_responses %}
85+
<button class="btn btn-secondary" title="Surveys that have been responded to cannot be modified">
86+
<i class="bx bxs-cog"></i> Configure the survey</button>
87+
{% else %}
7388
<a href="{% url 'survey_configure' survey.id %}" class="btn btn-primary"><i class="bx bxs-cog"></i>
7489
Configure the survey</a>
90+
{% endif %}
7591
</div>
7692
</div>
7793
</div>

survey/views.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ def get_success_url(self):
134134

135135

136136
class SurveyConfigureView(LoginRequiredMixin, View):
137+
"""
138+
Modify the questions in a survey.
139+
"""
137140

138141
def get(self, request: HttpRequest, pk: int):
139142
return self.render_survey_config_view(request, pk, is_post=False)
@@ -142,9 +145,13 @@ def post(self, request: HttpRequest, pk: int):
142145
return self.render_survey_config_view(request, pk, is_post=True)
143146

144147
def render_survey_config_view(self, request: HttpRequest, pk: int, is_post: bool):
145-
context = {}
148+
context = dict()
146149
# TODO: Error handling when object not found
147150
survey = get_object_or_404(Survey, pk=pk)
151+
if survey.has_responses:
152+
raise PermissionDenied(
153+
"Surveys cannot be modified after receiving responses."
154+
)
148155
context["survey"] = survey
149156
context["csrf"] = str(csrf(self.request)["csrf_token"])
150157
context["can_edit"] = {survey.id: survey_service.can_edit(request.user, survey)}

0 commit comments

Comments
 (0)