Skip to content

Commit d18cce6

Browse files
Merge pull request #315 from RSE-Sheffield/chore/mock-responses
Refactor the generation of mock survey responses
2 parents ee950b3 + 75956e5 commit d18cce6

File tree

2 files changed

+108
-98
lines changed

2 files changed

+108
-98
lines changed

survey/models.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import random
23
import secrets
34

45
from django.db import models
@@ -56,6 +57,83 @@ def reference_number(self) -> str:
5657
"""
5758
return f"{self.__class__.__name__.upper()}-{str(self.pk).zfill(6)}"
5859

60+
def generate_mock_responses(self, num_responses: int = 10):
61+
for _ in range(num_responses):
62+
self.accept_response(self._generate_mock_response())
63+
64+
def _generate_mock_response(self):
65+
"""
66+
Generate a dummy survey submission based on the questions in this survey.
67+
"""
68+
output_data = list()
69+
70+
for section in self.survey_config["sections"]:
71+
section_data_output = list()
72+
for field in section["fields"]:
73+
section_data_output.append(self._generate_random_field_value(field))
74+
75+
output_data.append(section_data_output)
76+
77+
return output_data
78+
79+
@classmethod
80+
def _generate_random_field_value(cls, field_config):
81+
field_type = field_config["type"]
82+
if field_type == "radio" or field_type == "select":
83+
# Pick one option
84+
num_options = len(field_config["options"])
85+
option_index = random.randint(0, num_options - 1)
86+
return str(field_config["options"][option_index])
87+
elif field_type == "checkbox":
88+
# Pick one random option
89+
num_options = len(field_config["options"])
90+
option_index = random.randint(0, num_options - 1)
91+
return [str(field_config["options"][option_index])]
92+
elif field_type == "likert":
93+
likert_output = list()
94+
# Pick something
95+
for _ in field_config["sublabels"]:
96+
num_options = len(field_config["options"])
97+
option_index = random.randint(0, num_options - 1)
98+
likert_output.append(str(field_config["options"][option_index]))
99+
return likert_output
100+
101+
elif field_type == "text":
102+
if "textType" in field_config:
103+
if field_config["textType"] == "INTEGER_TEXT":
104+
min_value = (
105+
field_config["minNumValue"]
106+
if "minNumValue" in field_config
107+
else 0
108+
)
109+
max_value = (
110+
field_config["maxNumValue"]
111+
if "maxNumValue" in field_config
112+
else 100
113+
)
114+
return str(random.randint(min_value, max_value))
115+
elif field_config["textType"] == "DECIMALS_TEXT":
116+
min_value = (
117+
field_config["minNumValue"]
118+
if "minNumValue" in field_config
119+
else 0
120+
)
121+
max_value = (
122+
field_config["maxNumValue"]
123+
if "maxNumValue" in field_config
124+
else 100
125+
)
126+
return str(random.uniform(min_value, max_value))
127+
elif field_config["textType"] == "EMAIL_TEXT":
128+
129+
else:
130+
return "Test plaintext field"
131+
else:
132+
return f"Test string for textarea field {field_config['label']}"
133+
134+
def accept_response(self, answers: list):
135+
return SurveyResponse.objects.create(survey=self, answers=answers)
136+
59137

60138
class SurveyEvidenceSection(models.Model):
61139
"""

survey/services/survey.py

Lines changed: 30 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def get_user_role(self, user: User, survey: Survey) -> Optional[str]:
3939
try:
4040
return survey.project.organisation.get_user_role(user)
4141
except (
42-
AttributeError
42+
AttributeError
4343
): # In case user is AnonymousUser or organisation method fails
4444
return None
4545

@@ -90,12 +90,12 @@ def initialise_survey(self, user: User, project: Project, survey: Survey):
9090

9191
@requires_permission("edit", obj_param="survey")
9292
def update_consent_demography_config(
93-
self,
94-
user: User,
95-
survey: Survey,
96-
consent_config,
97-
demography_config,
98-
survey_body_path,
93+
self,
94+
user: User,
95+
survey: Survey,
96+
consent_config,
97+
demography_config,
98+
survey_body_path,
9999
) -> Survey:
100100
survey.consent_config = consent_config
101101
survey.demography_config = demography_config
@@ -108,9 +108,9 @@ def update_consent_demography_config(
108108
with open(settings.SURVEY_TEMPLATE_DIR / body_path) as f:
109109
sort_config = json.load(f)
110110
merged_sections = (
111-
survey.consent_config["sections"]
112-
+ sort_config["sections"]
113-
+ survey.demography_config["sections"]
111+
survey.consent_config["sections"]
112+
+ sort_config["sections"]
113+
+ survey.demography_config["sections"]
114114
)
115115
survey.survey_config = {"sections": merged_sections}
116116

@@ -139,7 +139,7 @@ def duplicate_survey(self, user: User, survey: Survey):
139139
return new_survey
140140

141141
def _create_survey_evidence_sections(
142-
self, survey: Survey, clear_existing_sections: bool = True
142+
self, survey: Survey, clear_existing_sections: bool = True
143143
):
144144
if not survey.survey_config["sections"]:
145145
raise ValueError("No sections available")
@@ -156,11 +156,11 @@ def _create_survey_evidence_sections(
156156
survey_evidence_section.save()
157157

158158
def _create_survey_improvement_sections(
159-
self, survey: Survey, clear_existing_sections: bool = True
159+
self, survey: Survey, clear_existing_sections: bool = True
160160
):
161161
if clear_existing_sections:
162162
for improve_section in SurveyImprovementPlanSection.objects.filter(
163-
survey=survey
163+
survey=survey
164164
):
165165
improve_section.delete() # Delete all previous section first
166166

@@ -175,18 +175,18 @@ def _create_survey_improvement_sections(
175175

176176
@requires_permission("edit", obj_param="survey")
177177
def update_evidence_section(
178-
self, user: User, survey: Survey, evidence_section: SurveyEvidenceSection, text
178+
self, user: User, survey: Survey, evidence_section: SurveyEvidenceSection, text
179179
):
180180
evidence_section.text = text
181181
evidence_section.save()
182182

183183
@requires_permission("edit", obj_param="survey")
184184
def update_improvement_section(
185-
self,
186-
user: User,
187-
survey: Survey,
188-
improve_section: SurveyImprovementPlanSection,
189-
text,
185+
self,
186+
user: User,
187+
survey: Survey,
188+
improve_section: SurveyImprovementPlanSection,
189+
text,
190190
):
191191
improve_section.plan = text
192192
improve_section.save()
@@ -280,7 +280,7 @@ def _is_extension_supported(self, file_name: str) -> bool:
280280

281281
@requires_permission("edit", obj_param="survey")
282282
def add_uploaded_files(
283-
self, user: User, survey: Survey, files: Dict[str, UploadedFile]
283+
self, user: User, survey: Survey, files: Dict[str, UploadedFile]
284284
):
285285

286286
for field_name, uploaded_file in files.items():
@@ -297,11 +297,11 @@ def add_uploaded_files(
297297

298298
@requires_permission("edit", obj_param="survey")
299299
def add_uploaded_files_to_evidence_section(
300-
self,
301-
user: User,
302-
survey: Survey,
303-
evidence_section: SurveyEvidenceSection,
304-
files: Dict[str, UploadedFile],
300+
self,
301+
user: User,
302+
survey: Survey,
303+
evidence_section: SurveyEvidenceSection,
304+
files: Dict[str, UploadedFile],
305305
):
306306

307307
for field_name, uploaded_file in files.items():
@@ -324,84 +324,16 @@ def remove_file(self, user: User, survey: Survey, file: SurveyFile):
324324

325325
@requires_permission("edit", obj_param="survey")
326326
def remove_evidence_file(
327-
self, user: User, survey: Survey, file: SurveyEvidenceFile
327+
self, user: User, survey: Survey, file: SurveyEvidenceFile
328328
):
329329
file.delete()
330330

331-
def generate_mock_responses(self, user: User, survey: Survey, num_responses):
331+
@staticmethod
332+
def generate_mock_responses(user: User, survey: Survey, num_responses: int = 10):
332333
"""
333334
Generate a number of mock responses
334335
"""
335336
if not user.is_superuser:
336-
return PermissionDenied("Must be superuser to use this feature")
337+
raise PermissionDenied("Must be superuser to use this feature")
337338

338-
for i in range(num_responses):
339-
self.accept_response(
340-
survey, responseValues=self.generate_mock_response(survey.survey_config)
341-
)
342-
343-
def generate_mock_response(self, survey_config):
344-
output_data = []
345-
346-
for section in survey_config["sections"]:
347-
section_data_output = []
348-
for field in section["fields"]:
349-
section_data_output.append(self.generate_random_field_value(field))
350-
351-
output_data.append(section_data_output)
352-
353-
return output_data
354-
355-
def generate_random_field_value(self, field_config):
356-
type = field_config["type"]
357-
if type == "radio" or type == "select":
358-
# Pick one option
359-
num_options = len(field_config["options"])
360-
option_index = random.randint(0, num_options - 1)
361-
return str(field_config["options"][option_index])
362-
elif type == "checkbox":
363-
# Pick one random option
364-
num_options = len(field_config["options"])
365-
option_index = random.randint(0, num_options - 1)
366-
return [str(field_config["options"][option_index])]
367-
elif type == "likert":
368-
likert_output = []
369-
# Pick something
370-
for sublabel in field_config["sublabels"]:
371-
num_options = len(field_config["options"])
372-
option_index = random.randint(0, num_options - 1)
373-
likert_output.append(str(field_config["options"][option_index]))
374-
return likert_output
375-
376-
elif type == "text":
377-
if "textType" in field_config:
378-
if field_config["textType"] == "INTEGER_TEXT":
379-
min_value = (
380-
field_config["minNumValue"]
381-
if "minNumValue" in field_config
382-
else 0
383-
)
384-
max_value = (
385-
field_config["maxNumValue"]
386-
if "maxNumValue" in field_config
387-
else 100
388-
)
389-
return str(random.randint(min_value, max_value))
390-
elif field_config["textType"] == "DECIMALS_TEXT":
391-
min_value = (
392-
field_config["minNumValue"]
393-
if "minNumValue" in field_config
394-
else 0
395-
)
396-
max_value = (
397-
field_config["maxNumValue"]
398-
if "maxNumValue" in field_config
399-
else 100
400-
)
401-
return str(random.uniform(min_value, max_value))
402-
elif field_config["textType"] == "EMAIL_TEXT":
403-
404-
else:
405-
return "Test plaintext field"
406-
else:
407-
return f"Test string for textarea field {field_config['label']}"
339+
survey.generate_mock_responses(num_responses=num_responses)

0 commit comments

Comments
 (0)