Skip to content

PPHA-669-terms-of-use-pages#357

Open
jamiefalcus wants to merge 1 commit intomainfrom
PPHA-669-terms-of-use-pages
Open

PPHA-669-terms-of-use-pages#357
jamiefalcus wants to merge 1 commit intomainfrom
PPHA-669-terms-of-use-pages

Conversation

@jamiefalcus
Copy link
Contributor

What is the change?

Adding terms of use page and accept terms of use page

Why are we making this change?

So that a user can view and agree to terms of use

Copilot AI review requested due to automatic review settings March 12, 2026 11:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Terms of Use flow to the questionnaire so users can view Terms of Use and explicitly agree before continuing into the questions.

Changes:

  • Introduces /terms-of-use (static TemplateView) and /agree-terms-of-use (new question view + model/form) and routes users through it from Start.
  • Adds persistence for acceptance via TermsOfUseResponse (model + migration) and supporting factories/tests.
  • Extends NHSUK form rendering to support a checkbox-style template and updates global layout/footer to link to Terms of use.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
scripts/tests/unit.sh Adjusts unit test script to skip coverage report when running tagged tests.
lung_cancer_screening/questions/views/have_you_ever_smoked.py Updates back link to return to the new agree-terms step.
lung_cancer_screening/questions/views/agree_terms_of_use.py Adds the Agree Terms of Use view and a (currently unused) gating mixin.
lung_cancer_screening/questions/urls.py Registers agree-terms-of-use and terms-of-use routes.
lung_cancer_screening/questions/tests/unit/views/test_terms_of_use.py Adds view-level tests for agree-terms GET/POST behavior.
lung_cancer_screening/questions/tests/unit/models/test_agree_terms_of_use_response.py Adds model tests for TermsOfUseResponse.
lung_cancer_screening/questions/tests/unit/forms/test_agree_terms_of_use_form.py Adds form tests for the new terms form.
lung_cancer_screening/questions/tests/factories/terms_of_use_response_factory.py Adds factory for TermsOfUseResponse.
lung_cancer_screening/questions/models/terms_of_use_response.py Introduces TermsOfUseResponse model (1:1 with ResponseSet).
lung_cancer_screening/questions/models/init.py Exposes TermsOfUseResponse via package init import.
lung_cancer_screening/questions/migrations/0007_termsofuseresponse.py Creates DB table for terms-of-use acceptance response.
lung_cancer_screening/questions/jinja2/terms_of_use.jinja Adds the Terms of Use content page template.
lung_cancer_screening/questions/jinja2/start.jinja Routes “Continue” from Start to agree-terms step.
lung_cancer_screening/questions/jinja2/agree_terms_of_use.jinja Adds the agree-terms page prelude content and link to terms.
lung_cancer_screening/questions/forms/agree_terms_of_use_form.py Adds the model form used to capture acceptance.
lung_cancer_screening/nhsuk_forms/jinja2/checkbox.jinja Adds a new checkbox rendering template.
lung_cancer_screening/nhsuk_forms/choice_field.py Selects checkbox.jinja template for CheckboxInput.
lung_cancer_screening/jinja2_env.py Modifies Jinja environment/filter configuration (includes new debug filter code).
lung_cancer_screening/core/jinja2/layout.jinja Adds inline CSS for numbered terms page and footer link to Terms of use.
features/questionnaire.feature Updates end-to-end flow to include agreeing to terms before questions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +11 to +21
class EnsureAcceptedTermsEligible:
def dispatch(self, request, *args, **kwargs):
if (
not hasattr(request.response_set, "terms_of_use_response")
or not request.response_set.terms_of_use_response.has_accepted()
):
return redirect(reverse("questions:agree_terms_of_use"))
else:
return super().dispatch(request, *args, **kwargs)


Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnsureAcceptedTermsEligible is introduced but not used by any view (no references outside this file). If this mixin is meant to enforce terms acceptance across the questionnaire, it should be added to the relevant question views; otherwise consider removing it to avoid dead/unused code.

Suggested change
class EnsureAcceptedTermsEligible:
def dispatch(self, request, *args, **kwargs):
if (
not hasattr(request.response_set, "terms_of_use_response")
or not request.response_set.terms_of_use_response.has_accepted()
):
return redirect(reverse("questions:agree_terms_of_use"))
else:
return super().dispatch(request, *args, **kwargs)

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +32
self.fields["value"] = ChoiceField(
choices=[("True", 'I agree')],
widget=forms.CheckboxInput,
label="I agree",
error_messages={
'required': 'Agree to the terms of use to continue'
}
)

#self.fields["value"].initial = [("True", 'I agree')]

class Meta:
model = TermsOfUseResponse
fields = ['value']

def clean_value(self):
if self.cleaned_data.get("value") == "True":
return True

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ChoiceField with widget=forms.CheckboxInput is problematic because CheckboxInput.value_from_datadict effectively reduces the submission to a boolean based on key presence, so unexpected posted values (e.g. "something not in list") won’t be validated against choices the way the tests expect. Consider using the existing MultipleChoiceField (single option) + a clean_value that maps the selected list to a boolean, or use a dedicated boolean field implementation that validates the posted value explicitly.

Suggested change
self.fields["value"] = ChoiceField(
choices=[("True", 'I agree')],
widget=forms.CheckboxInput,
label="I agree",
error_messages={
'required': 'Agree to the terms of use to continue'
}
)
#self.fields["value"].initial = [("True", 'I agree')]
class Meta:
model = TermsOfUseResponse
fields = ['value']
def clean_value(self):
if self.cleaned_data.get("value") == "True":
return True
self.fields["value"] = MultipleChoiceField(
choices=[("True", "I agree")],
widget=forms.CheckboxSelectMultiple,
label="I agree",
error_messages={
"required": "Agree to the terms of use to continue"
},
)
# self.fields["value"].initial = [("True", "I agree")]
def clean_value(self):
values = self.cleaned_data.get("value") or []
return "True" in values
class Meta:
model = TermsOfUseResponse
fields = ["value"]

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +34
def clean_value(self):
if self.cleaned_data.get("value") == "True":
return True

def do_coerce(value) :
return value == "True"
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean_value is defined at module scope (not indented under TermsOfUseForm), so it will never run as a form field cleaner. do_coerce is also unused. If you need to coerce/validate the submitted value, move clean_value into the form class (or implement it via the field’s coerce/custom field) and remove unused helpers.

Suggested change
def clean_value(self):
if self.cleaned_data.get("value") == "True":
return True
def do_coerce(value) :
return value == "True"
def clean_value(self):
value = self.cleaned_data.get("value")
return value == "True"

Copilot uses AI. Check for mistakes.
@jamiefalcus jamiefalcus force-pushed the PPHA-669-terms-of-use-pages branch 3 times, most recently from 883f542 to 58279a5 Compare March 12, 2026 14:01
@jamiefalcus jamiefalcus force-pushed the PPHA-669-terms-of-use-pages branch from 58279a5 to 59e3c42 Compare March 12, 2026 14:03
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants