diff --git a/SORT/settings.py b/SORT/settings.py index d7575c2e..367028cf 100644 --- a/SORT/settings.py +++ b/SORT/settings.py @@ -259,12 +259,19 @@ def cast_to_boolean(obj: Any) -> bool: }, } +# Survey content configuration files SURVEY_TEMPLATE_DIR = BASE_DIR / "data/survey_config" SURVEY_TEMPLATES = { "Nurses": "sort_only_config_nurses.json", "Midwives": "sort_only_config_midwives.json", "NMAHPs": "sort_only_config_nmahps.json", } +DEMOGRAPHY_TEMPLATES = { + "Nurses": "demography_only_config_nurses.json", + "Midwives": "demography_only_config_midwives.json", + "NMAHPs": "demography_only_config_nmahps.json", +} +CONSENT_TEMPLATE = "consent_only_config.json" # Crispy enables Bootstrap styling on Django forms # https://django-crispy-forms.readthedocs.io/en/latest/install.html diff --git a/SORT/test/test_case/view.py b/SORT/test/test_case/view.py index f8f38059..bded35ab 100644 --- a/SORT/test/test_case/view.py +++ b/SORT/test/test_case/view.py @@ -42,11 +42,11 @@ def login_superuser(self): ) def get( - self, - view_name: str, - expected_status_code: int = HTTPStatus.OK, - login: bool = True, - **kwargs + self, + view_name: str, + expected_status_code: int = HTTPStatus.OK, + login: bool = True, + **kwargs ): """ Helper method to make a GET request to one of the views in this app. @@ -63,12 +63,12 @@ def get( return response def post( - self, - view_name: str, - expected_status_code: int = HTTPStatus.OK, - login: bool = True, - data: dict = None, - **kwargs + self, + view_name: str, + expected_status_code: int = HTTPStatus.OK, + login: bool = True, + data: dict = None, + **kwargs ): """ Helper method to make a POST request to one of the views in this app. @@ -83,7 +83,8 @@ def post( if login: self.login() response = self.client.post( - django.urls.reverse(view_name, kwargs=kwargs), data=data + path=django.urls.reverse(view_name, kwargs=kwargs), + data=data, ) self.assertEqual(response.status_code, expected_status_code) return response diff --git a/data/survey_config/demography_only_config_midwives.json b/data/survey_config/demography_only_config_midwives.json new file mode 100644 index 00000000..90689285 --- /dev/null +++ b/data/survey_config/demography_only_config_midwives.json @@ -0,0 +1,170 @@ +{ + "sections": [ + { + "title": "Demographic", + "type": "demographic", + "description": "Please fill in your demographic information", + "fields": [ + { + "type": "text", + "label": "What is your age?", + "required": true, + "sublabels": [], + "options": [], + "enforceValueConstraints": false, + "maxNumChar": 500, + "minNumValue": 0, + "maxNumValue": 100, + "textType": "INTEGER_TEXT", + "disabled": false, + "readOnly": true + }, + { + "type": "radio", + "label": "What is your gender?", + "description": "", + "required": true, + "sublabels": [], + "options": [ + "Male", + "Female", + "Non-binary", + "Prefer not to say" + ], + "disabled": false, + "readOnly": true + }, + { + "type": "text", + "label": "How long have you been working in your current organisation? (Number of years)", + "required": true, + "sublabels": [], + "options": [], + "enforceValueConstraints": false, + "maxNumChar": 500, + "minNumValue": 0, + "maxNumValue": 100, + "textType": "INTEGER_TEXT", + "disabled": false, + "readOnly": true + }, + { + "type": "radio", + "label": "What is your current pay band?", + "required": true, + "sublabels": [], + "options": [ + "Band 2", + "Band 3", + "Band 4", + "Band 5", + "Band 6", + "Band 7", + "Band 8a", + "Band 8b", + "Band 8c", + "Band 8d", + "Band 9" + ], + "disabled": false, + "readOnly": true + }, + { + "type": "radio", + "label": "What is your profession?", + "required": true, + "sublabels": [], + "options": [ + "Registered Midwife", + "Community Midwife", + "Hospital Midwife", + "Specialist Midwife", + "Senior Midwife", + "Lead/Manager Midwife", + "Consultant Midwife", + "Research Midwife", + "Maternity Support Worker", + "Midwifery Educator", + "Other" + ], + "disabled": false, + "readOnly": true + }, + { + "description": "", + "disabled": false, + "enforceValueConstraints": false, + "label": "What is your job title?", + "maxNumChar": 500, + "maxNumValue": 100, + "minNumValue": 0, + "options": [ + ], + "required": true, + "sublabels": [ + ], + "textType": "PLAIN_TEXT", + "type": "text", + "readOnly": true + }, + { + "type": "radio", + "label": "What is your highest level of qualification?", + "required": true, + "sublabels": [], + "options": [ + "No qualification", + "Diploma", + "Degree", + "Masters", + "PhD/Doctorate", + "Other" + ], + "disabled": false, + "readOnly": true + }, + { + "description": "", + "disabled": false, + "enforceValueConstraints": true, + "label": "How many years have you been qualified?", + "maxNumChar": 500, + "maxNumValue": 100, + "minNumValue": 0, + "options": [], + "required": true, + "sublabels": [], + "textType": "INTEGER_TEXT", + "type": "text", + "readOnly": true + }, + { + "type": "radio", + "label": "What is your ethnicity?", + "required": true, + "sublabels": [], + "options": [ + "Asian British - Bangladeshi", + "Asian British - Indian", + "Asian British - Pakistani", + "Asian - Chinese", + "Asian - other", + "Black British - African", + "Black British - Caribbean", + "Black British - other", + "Mixed - Black African and White", + "Mixed - Black Asian and White", + "Mixed - Caribbean and White", + "Mixed - other", + "White - British", + "White - Irish", + "White - Romany", + "Other" + ], + "disabled": false, + "readOnly": true + } + ] + } + ] +} diff --git a/data/survey_config/demography_only_config.json b/data/survey_config/demography_only_config_nmahps.json similarity index 87% rename from data/survey_config/demography_only_config.json rename to data/survey_config/demography_only_config_nmahps.json index 5adb88f1..eabc5295 100644 --- a/data/survey_config/demography_only_config.json +++ b/data/survey_config/demography_only_config_nmahps.json @@ -7,7 +7,7 @@ "fields": [ { "type": "text", - "label": "What is your age", + "label": "What is your age?", "required": true, "sublabels": [], "options": [], @@ -21,7 +21,7 @@ }, { "type": "radio", - "label": "Your Gender", + "label": "What is your gender?", "description": "", "required": true, "sublabels": [], @@ -50,15 +50,20 @@ }, { "type": "radio", - "label": "What is your current Band/Grade", + "label": "What is your current pay band?", "required": true, "sublabels": [], "options": [ + "Band 2", + "Band 3", "Band 4", "Band 5", "Band 6", "Band 7", - "Band 8", + "Band 8a", + "Band 8b", + "Band 8c", + "Band 8d", "Band 9" ], "disabled": false, @@ -66,7 +71,7 @@ }, { "type": "radio", - "label": "Please describe your current role", + "label": "What is your profession?", "required": true, "sublabels": [], "options": [ @@ -95,7 +100,7 @@ "description": "", "disabled": false, "enforceValueConstraints": false, - "label": "Please provide your job title", + "label": "What is your job title?", "maxNumChar": 500, "maxNumValue": 100, "minNumValue": 0, @@ -110,14 +115,16 @@ }, { "type": "radio", - "label": "Please indicate your highest qualification", + "label": "What is your highest level of qualification?", "required": true, "sublabels": [], "options": [ + "No qualification", "Diploma", "Degree", "Masters", - "PhD/Doctorate" + "PhD/Doctorate", + "Other" ], "disabled": false, "readOnly": true @@ -126,7 +133,7 @@ "description": "", "disabled": false, "enforceValueConstraints": true, - "label": "How many years have you been qualified", + "label": "How many years have you been qualified?", "maxNumChar": 500, "maxNumValue": 100, "minNumValue": 0, diff --git a/data/survey_config/demography_only_config_nurses.json b/data/survey_config/demography_only_config_nurses.json new file mode 100644 index 00000000..b94d30e3 --- /dev/null +++ b/data/survey_config/demography_only_config_nurses.json @@ -0,0 +1,165 @@ +{ + "sections": [ + { + "title": "Demographic", + "type": "demographic", + "description": "Please fill in your demographic information", + "fields": [ + { + "type": "text", + "label": "What is your age?", + "required": true, + "sublabels": [], + "options": [], + "enforceValueConstraints": false, + "maxNumChar": 500, + "minNumValue": 0, + "maxNumValue": 100, + "textType": "INTEGER_TEXT", + "disabled": false, + "readOnly": true + }, + { + "type": "radio", + "label": "What is your gender?", + "description": "", + "required": true, + "sublabels": [], + "options": [ + "Male", + "Female", + "Non-binary", + "Prefer not to say" + ], + "disabled": false, + "readOnly": true + }, + { + "type": "text", + "label": "How long have you been working in your current organisation? (Number of years)", + "required": true, + "sublabels": [], + "options": [], + "enforceValueConstraints": false, + "maxNumChar": 500, + "minNumValue": 0, + "maxNumValue": 100, + "textType": "INTEGER_TEXT", + "disabled": false, + "readOnly": true + }, + { + "type": "radio", + "label": "What is your current pay band?", + "required": true, + "sublabels": [], + "options": [ + "Band 2", + "Band 3", + "Band 4", + "Band 5", + "Band 6", + "Band 7", + "Band 8a", + "Band 8b", + "Band 8c", + "Band 8d", + "Band 9" + ], + "disabled": false, + "readOnly": true + }, + { + "type": "radio", + "label": "What is your profession?", + "required": true, + "sublabels": [], + "options": [ + "Registered Staff Nurse", + "Senior Nurse", + "Specialist Nurse", + "Advanced Nurse Practitioner", + "Consultant Nurse", + "Other" + ], + "disabled": false, + "readOnly": true + }, + { + "description": "", + "disabled": false, + "enforceValueConstraints": false, + "label": "What is your job title?", + "maxNumChar": 500, + "maxNumValue": 100, + "minNumValue": 0, + "options": [ + ], + "required": true, + "sublabels": [ + ], + "textType": "PLAIN_TEXT", + "type": "text", + "readOnly": true + }, + { + "type": "radio", + "label": "What is your highest level of qualification?", + "required": true, + "sublabels": [], + "options": [ + "No qualification", + "Diploma", + "Degree", + "Masters", + "PhD/Doctorate", + "Other" + ], + "disabled": false, + "readOnly": true + }, + { + "description": "", + "disabled": false, + "enforceValueConstraints": true, + "label": "How many years have you been qualified?", + "maxNumChar": 500, + "maxNumValue": 100, + "minNumValue": 0, + "options": [], + "required": true, + "sublabels": [], + "textType": "INTEGER_TEXT", + "type": "text", + "readOnly": true + }, + { + "type": "radio", + "label": "What is your ethnicity?", + "required": true, + "sublabels": [], + "options": [ + "Asian British - Bangladeshi", + "Asian British - Indian", + "Asian British - Pakistani", + "Asian - Chinese", + "Asian - other", + "Black British - African", + "Black British - Caribbean", + "Black British - other", + "Mixed - Black African and White", + "Mixed - Black Asian and White", + "Mixed - Caribbean and White", + "Mixed - other", + "White - British", + "White - Irish", + "White - Romany", + "Other" + ], + "disabled": false, + "readOnly": true + } + ] + } + ] +} diff --git a/scripts/test.bat b/scripts/test.bat new file mode 100644 index 00000000..9875e4db --- /dev/null +++ b/scripts/test.bat @@ -0,0 +1,3 @@ +flake8 +python manage.py test home/tests --parallel=auto --failfast +python manage.py test survey/tests --parallel=auto --failfast diff --git a/survey/checks/survey_config.py b/survey/checks/survey_config.py index 22706c84..f9f4985c 100644 --- a/survey/checks/survey_config.py +++ b/survey/checks/survey_config.py @@ -1,14 +1,14 @@ +import itertools from pathlib import Path from django.core.checks import Tags, Error -import django.conf +from django.conf import settings import django.contrib.staticfiles.finders -settings = django.conf.settings - -SURVEY_CONFIG_FILE_PATHS = { - "data/survey_config/consent_only_config.json", - "data/survey_config/demography_only_config.json" -} +FILENAMES = itertools.chain( + settings.SURVEY_TEMPLATES.values(), + settings.DEMOGRAPHY_TEMPLATES.values(), + (settings.CONSENT_TEMPLATE,) +) @django.core.checks.register(Tags.staticfiles) @@ -18,8 +18,9 @@ def check_survey_config(*args, **kwargs) -> list[Error]: """ errors = list() - for path in SURVEY_CONFIG_FILE_PATHS: - path = Path(path).absolute() + # Iterate over survey config files + for filename in FILENAMES: + path = Path(settings.SURVEY_TEMPLATE_DIR).joinpath(filename).absolute() if not path.exists(): errors.append( Error(f"File not found: {path}", hint="Make sure the survey config file is present.") diff --git a/survey/forms.py b/survey/forms.py index e264f3bf..8ce65250 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -4,6 +4,8 @@ from .validators.email_list_validator import EmailListValidator +from .models import Survey, Profession + class InvitationForm(forms.Form): email = forms.CharField( @@ -75,3 +77,20 @@ def add_fields(self, form, index): ) return formset_factory(BlankDynamicForm, BaseTestFormSet, min_num=1, max_num=1) + + +class SurveyCreateForm(forms.ModelForm): + survey_body_path = forms.ChoiceField( + label="Target audience", + choices=Profession, + help_text="Respondent profession", + widget=forms.Select(attrs={'class': 'form-control'}) # Optional: add CSS classes + ) + + class Meta: + model = Survey + fields = ["name", "description", "survey_body_path"] + widgets = { + 'name': forms.TextInput(attrs={'class': 'form-control'}), + 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}), + } diff --git a/survey/migrations/0020_alter_survey_project_alter_survey_survey_body_path.py b/survey/migrations/0020_alter_survey_project_alter_survey_survey_body_path.py new file mode 100644 index 00000000..f483a685 --- /dev/null +++ b/survey/migrations/0020_alter_survey_project_alter_survey_survey_body_path.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1.4 on 2025-09-15 15:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("home", "0011_rename_created_on_project_created_at"), + ("survey", "0019_survey_is_active"), + ] + + operations = [ + migrations.AlterField( + model_name="survey", + name="survey_body_path", + field=models.TextField( + choices=[ + ("NMAHPS", "NMAHPs"), + ("NURSES", "Nurses"), + ("WIDMIVES", "Midwives"), + ], + default="NMAHPS", + help_text="Respondent profession", + ), + ), + ] diff --git a/survey/models.py b/survey/models.py index da639e11..4b650f4c 100644 --- a/survey/models.py +++ b/survey/models.py @@ -23,6 +23,15 @@ logger = logging.getLogger(__name__) +class Profession(models.TextChoices): + """ + Respondent job category for the target audience that will complete the survey. + """ + NMAHPS = "NMAHPs", "Nurses, Midwives and Allied Health Professionals (NMAHPs)" + NURSES = "Nurses", "Nurses" + WIDMIVES = "Midwives", "Widwives" + + class Survey(models.Model): """ Represents a survey that will be sent out to a participant @@ -31,11 +40,13 @@ class Survey(models.Model): name = models.CharField(max_length=200) description = models.TextField(blank=True, null=True) survey_config = models.JSONField(null=True) - consent_config = models.JSONField(null=True) - demography_config = models.JSONField(null=True) project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True, related_name="survey") created_at = models.DateTimeField(auto_now_add=True) - survey_body_path = models.TextField(blank=True, null=True) + survey_body_path = models.TextField( + blank=False, null=False, default=Profession.NMAHPS, + help_text="Respondent profession", + choices=Profession + ) is_active = models.BooleanField( default=True, help_text="Are responses being collected?", @@ -49,21 +60,46 @@ def __str__(self): def organisation(self): return self.project.organisation - def initialise(self): + @property + def consent_config_path(self) -> Path: + """The location of the consent configuration file.""" + return Path(settings.SURVEY_TEMPLATE_DIR).joinpath(settings.CONSENT_TEMPLATE) + + @property + def demography_config_filename(self) -> str: """ - Load an "empty" survey configuration + The filename of the demographics questions configuration file for this profession. """ + return settings.DEMOGRAPHY_TEMPLATES[self.survey_body_path] - # Consent questions - with open("data/survey_config/consent_only_config.json") as file: - self.consent_config = json.load(file) + @property + def demography_config_path(self) -> Path: + """ + The location of the demographics questions configuration file for this profession + """ + return Path(settings.SURVEY_TEMPLATE_DIR) / self.demography_config_filename + + @property + def consent_config_default(self) -> dict: + """ + Survey consent question configuration + """ + with self.consent_config_path.open() as file: + return json.load(file) - # Demographics questions - with open("data/survey_config/demography_only_config.json") as file: - self.demography_config = json.load(file) + @property + def demography_config_default(self) -> dict: + """ + The default demographics questions configuration + """ + with self.demography_config_path.open() as file: + return json.load(file) - self.survey_body_path = "Nurses" - self.merge_sections() + def initialise(self): + """ + Set up a new survey, populating the question sections. + """ + self.reset() def get_absolute_url(self): return reverse("survey", kwargs={"pk": self.pk}) @@ -278,27 +314,45 @@ def to_excel(self) -> bytes: @property def template_filename(self) -> str: + """ + The filename of the SORT questions config file for this profession e.g. "sort_only_config_midwives.json" + """ return settings.SURVEY_TEMPLATES[self.survey_body_path] @property def template_path(self) -> Path: + """ + The location of the SORT questions configuration for this profession. + """ return settings.SURVEY_TEMPLATE_DIR.joinpath(self.template_filename) @property - def sort_config(self): + def sort_config(self) -> dict: + """ + The SORT section configuration for this profession. + """ with self.template_path.open() as file: return json.load(file) - def merge_sections(self, body_path: str = None): + def reset(self): """ - Merge all the survey question configuration into the survey_config field. + Reset all the questionnaire sections to their default values. """ - merged_sections = ( - self.consent_config["sections"] - + self.sort_config["sections"] - + self.demography_config["sections"] + self.update( + consent_config=self.consent_config_default, + demography_config=self.demography_config_default, ) - self.survey_config = {"sections": merged_sections} + + def update(self, consent_config: dict, demography_config: dict): + """ + Update the survey question configuration into the survey_config field. + + The consent and demographics fields may be overridden by the user, while the SORT questions are hard-coded. + """ + self.survey_config = { + # Merge sections by concatenating all questions + "sections": consent_config["sections"] + self.sort_config["sections"] + demography_config["sections"] + } class SurveyEvidenceSection(models.Model): diff --git a/survey/services/survey.py b/survey/services/survey.py index 8ddcaf32..810078b2 100644 --- a/survey/services/survey.py +++ b/survey/services/survey.py @@ -92,7 +92,7 @@ def update_consent_demography_config( survey.demography_config = demography_config survey.survey_body_path = survey_body_path - survey.merge_sections() + survey.update(consent_config=consent_config, demography_config=demography_config) survey.save() @@ -111,8 +111,8 @@ def duplicate_survey(self, user: User, survey: Survey): self.update_consent_demography_config( user, new_survey, - consent_config=survey.consent_config, - demography_config=survey.demography_config, + consent_config=survey.consent_config_default, + demography_config=survey.demography_config_default, survey_body_path=survey.survey_body_path, ) diff --git a/survey/templates/survey/create.html b/survey/templates/survey/create.html index 0db8e9b8..a69c7f1d 100644 --- a/survey/templates/survey/create.html +++ b/survey/templates/survey/create.html @@ -1,36 +1,39 @@ {% extends "base_manager.html" %} {% block content %} -
-
-

Create Survey

-

Use the form below to create a new survey.

-
-
-
-
- {% csrf_token %} - {% for field in form %} -
- - - {% if field.help_text %} -
{{ field.help_text }}
- {% endif %} -
+
+
+

Create Survey

+

Use the form below to create a new survey.

+
+ {% if messages %} +
    + {% for message in messages %} + {{ message }} {% endfor %} -
    - -
    - +
+ {% endif %} +
+
+
+ {% csrf_token %} + {% for field in form %} +
+
+ + {{ field }} +
+ {% if field.help_text %} +
{{ field.help_text }}
+ {% endif %} +
+ {% endfor %} +
+ +
+
+
-
{% endblock %} diff --git a/survey/templates/survey/survey.html b/survey/templates/survey/survey.html index 5128c803..2bbabe21 100644 --- a/survey/templates/survey/survey.html +++ b/survey/templates/survey/survey.html @@ -35,7 +35,8 @@

{{ survey.name }}

-

Survey description:

+

Respondent profession: {{ survey.survey_body_path }}

+

Description:

{{ survey.description }}

Created on: {{ survey.created_at }}

diff --git a/survey/templates/survey/survey_configure.html b/survey/templates/survey/survey_configure.html index 4b38e43b..f4b9ee7a 100644 --- a/survey/templates/survey/survey_configure.html +++ b/survey/templates/survey/survey_configure.html @@ -35,8 +35,8 @@

Configure {{ survey.name }}

>
{{ csrf | json_script:"csrf" }} - {{ survey.consent_config | json_script:"consentConfig" }} - {{ survey.demography_config | json_script:"demographyConfig" }} + {{ survey.consent_config_default | json_script:"consentConfig" }} + {{ survey.demography_config_default | json_script:"demographyConfig" }} {{ survey.survey_config | json_script:"configData" }} {% vite_client %} {% vite_asset "src/main.ts" %} diff --git a/survey/tests/test_invitation_views.py b/survey/tests/test_invitation_views.py index 4d666de2..508bda9e 100644 --- a/survey/tests/test_invitation_views.py +++ b/survey/tests/test_invitation_views.py @@ -19,8 +19,8 @@ def setUp(self): self.service.update_consent_demography_config( user=self.user, survey=self.survey, - consent_config=self.survey.consent_config, - demography_config=self.survey.demography_config, + consent_config=self.survey.consent_config_default, + demography_config=self.survey.demography_config_default, survey_body_path="Nurses", ) @@ -32,6 +32,5 @@ def test_invitation_view_post(self): view_name="invite", pk=self.survey.pk, data=dict(email="test@test.com", message="My message"), - # Expect redirection on success expected_status_code=HTTPStatus.FOUND, ) diff --git a/survey/tests/test_models.py b/survey/tests/test_models.py index 71695fd3..eb829ffa 100644 --- a/survey/tests/test_models.py +++ b/survey/tests/test_models.py @@ -11,6 +11,11 @@ def setUp(self): self.survey.initialise() self.survey.save() + def test_survey_config(self): + self.assertIsNotNone(self.survey.survey_config) + self.assertIsInstance(self.survey.survey_config, dict) + self.assertIsInstance(self.survey.survey_config["sections"], list) + def test_unique_survey_and_section_index(self): """ Each Survey cannot have more than one section with the same index diff --git a/survey/tests/test_survey_service.py b/survey/tests/test_survey_service.py index dbb03665..8ef53946 100644 --- a/survey/tests/test_survey_service.py +++ b/survey/tests/test_survey_service.py @@ -85,7 +85,7 @@ def test_initialise_survey(self): ) self.assertIsInstance(self.survey.survey_config, dict) - self.assertIsInstance(self.survey.consent_config, dict) + self.assertIsInstance(self.survey.consent_config_default, dict) self.assertIsInstance(self.survey.survey_config, dict) # Check that a survey was created with some sections @@ -100,25 +100,12 @@ def test_update_consent_demography_config(self): survey_body_path="Nurses", ) - self.assertIsInstance(self.survey.survey_config, dict) - self.assertIsInstance(self.survey.consent_config, dict) self.assertIsInstance(self.survey.survey_config, dict) # There should be some SORT survey sections self.assertGreater( len(self.survey.survey_config["sections"]), 0, "No survey sections" ) - # There shouldn't be any consent or demography since we updated with empty values above - self.assertEqual( - len(self.survey.consent_config["sections"]), - 0, - "Unexpected consent sections", - ) - self.assertEqual( - len(self.survey.demography_config["sections"]), - 0, - "Unexpected demography sections", - ) def test_duplicate_survey(self): # Properly initialise the survey @@ -126,9 +113,9 @@ def test_duplicate_survey(self): self.service.update_consent_demography_config( self.admin, self.survey, - demography_config=self.survey.demography_config, + demography_config=self.survey.demography_config_default, survey_body_path=self.survey.survey_body_path, - consent_config=self.survey.consent_config, + consent_config=self.survey.consent_config_default, ) duplicated_survey = self.service.duplicate_survey( @@ -138,10 +125,10 @@ def test_duplicate_survey(self): self.assertEqual(duplicated_survey.project, self.survey.project) self.assertDictEqual(duplicated_survey.survey_config, self.survey.survey_config) self.assertDictEqual( - duplicated_survey.consent_config, self.survey.consent_config + duplicated_survey.consent_config_default, self.survey.consent_config_default ) self.assertDictEqual( - duplicated_survey.demography_config, self.survey.demography_config + duplicated_survey.demography_config_default, self.survey.demography_config_default ) self.assertTrue( SurveyEvidenceSection.objects.filter(survey=duplicated_survey).count() > 1 @@ -198,8 +185,8 @@ def test_create_survey_evidence_sections(self): self.service.update_consent_demography_config( user=self.admin, survey=self.survey, - consent_config=self.survey.consent_config, - demography_config=self.survey.demography_config, + consent_config=self.survey.consent_config_default, + demography_config=self.survey.demography_config_default, survey_body_path="Nurses", ) # from survey.models import SurveyEvidenceSection diff --git a/survey/tests/test_survey_views.py b/survey/tests/test_survey_views.py index c25e8af2..57a481c7 100644 --- a/survey/tests/test_survey_views.py +++ b/survey/tests/test_survey_views.py @@ -1,3 +1,4 @@ +import json from http import HTTPStatus import SORT.test.model_factory @@ -14,16 +15,8 @@ def setUp(self): self.project = self.survey.project self.organisation = self.project.organisation self.user = self.organisation.members.first() - self.service.initialise_survey( - user=self.user, project=self.project, survey=self.survey - ) - self.service.update_consent_demography_config( - user=self.user, - survey=self.survey, - consent_config=self.survey.consent_config, - demography_config=self.survey.demography_config, - survey_body_path="Nurses", - ) + self.survey.initialise() + self.survey.save() def test_survey_get(self): self.get("survey", pk=self.survey.pk) @@ -59,7 +52,15 @@ def test_survey_configure_get(self): self.get("survey_configure", pk=self.survey.pk) def test_survey_configure_post(self): - self.post("survey_configure", pk=self.survey.pk) + self.post( + view_name="survey_configure", + pk=self.survey.pk, + data=dict( + consent_config=json.dumps(self.survey.consent_config_default), + demography_config=json.dumps(self.survey.demography_config_default), + ), + expected_status_code=HTTPStatus.FOUND, + ) def test_survey_export(self): self.get("survey_export", pk=self.survey.pk) diff --git a/survey/views.py b/survey/views.py index 2cac6f7c..5f23a473 100644 --- a/survey/views.py +++ b/survey/views.py @@ -26,7 +26,7 @@ from home.models import Project from survey.services import survey_service -from .forms import InvitationForm +from .forms import InvitationForm, SurveyCreateForm from .models import ( Survey, SurveyEvidenceFile, @@ -80,7 +80,7 @@ def render_survey_page(self, request: HttpRequest, pk: int, is_post=False): class SurveyCreateView(LoginRequiredMixin, CreateView): model = Survey template_name = "survey/create.html" - fields = ["name", "description"] + form_class = SurveyCreateForm def get_success_url(self): return self.object.get_absolute_url() @@ -139,12 +139,12 @@ class SurveyConfigureView(LoginRequiredMixin, View): """ def get(self, request: HttpRequest, pk: int): - return self.render_survey_config_view(request, pk, is_post=False) + return self.render_survey_config_view(request, pk) def post(self, request: HttpRequest, pk: int): - return self.render_survey_config_view(request, pk, is_post=True) + return self.render_survey_config_view(request, pk) - def render_survey_config_view(self, request: HttpRequest, pk: int, is_post: bool): + def render_survey_config_view(self, request: HttpRequest, pk: int): context = dict() # TODO: Error handling when object not found survey = get_object_or_404(Survey, pk=pk) @@ -156,26 +156,19 @@ def render_survey_config_view(self, request: HttpRequest, pk: int, is_post: bool context["csrf"] = str(csrf(self.request)["csrf_token"]) context["can_edit"] = {survey.id: survey_service.can_edit(request.user, survey)} - if is_post: - if ( - "survey_body_path" in request.POST - and "consent_config" in request.POST - and "demography_config" in request.POST - ): - consent_config = json.loads(request.POST.get("consent_config", None)) - demography_config = json.loads( - request.POST.get("demography_config", None) - ) - survey_body_path = request.POST.get("survey_body_path", None) - survey_service.update_consent_demography_config( - request.user, - survey, - consent_config=consent_config, - demography_config=demography_config, - survey_body_path=survey_body_path, - ) - messages.info(request, "Survey configuration saved") - return redirect("survey", pk=survey.pk) + if request.method == "POST": + consent_config = json.loads(request.POST["consent_config"]) + demography_config = json.loads(request.POST["demography_config"]) + survey_body_path = request.POST.get("survey_body_path", survey.survey_body_path) + survey_service.update_consent_demography_config( + request.user, + survey, + consent_config=consent_config, + demography_config=demography_config, + survey_body_path=survey_body_path, + ) + messages.info(request, "Survey configuration saved") + return redirect("survey", pk=survey.pk) return render( request=request, diff --git a/ui_components/src/SurveyConfigConsentDemographyApp.svelte b/ui_components/src/SurveyConfigConsentDemographyApp.svelte index e53ede5b..852cf309 100644 --- a/ui_components/src/SurveyConfigConsentDemographyApp.svelte +++ b/ui_components/src/SurveyConfigConsentDemographyApp.svelte @@ -14,7 +14,6 @@ -

Configure your welcome and consent page

@@ -32,19 +31,6 @@
-
-
-

SORT Survey Questions

- - -
-

Configure your demographic page

@@ -56,7 +42,8 @@ The in-built demographics fields may not be modified and are labelled "Read only".