Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"cSpell.language": "en-GB",
"cSpell.words": [
"dateutil",
"Dproject",
"Dsonar",
"endcall",
"endfor",
"endset",
Expand All @@ -20,8 +22,10 @@
"novalidate",
"psycopg",
"pyjwt",
"qualitygate",
"relativedelta",
"responseset",
"sonarsource",
"stylesheet",
"toplevel",
"unsubmitted",
Expand Down
2 changes: 2 additions & 0 deletions lung_cancer_screening/core/jinja2/layout.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

{% extends 'nhsuk/template.jinja' %}

{% from 'nhsuk/components/back-link/macro.jinja' import backLink %}

{% block head %}
<link rel="stylesheet" href="{{ static('css/main.css' )}}">
<script type="module" src="{{ static('js/bundle.js' )}}"></script>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django import forms

from ...nhsuk_forms.choice_field import ChoiceField
from ..models.relatives_age_when_diagnosed_response import RelativesAgeWhenDiagnosedResponse, RelativesAgeWhenDiagnosedValues


class RelativesAgeWhenDiagnosedForm(forms.ModelForm):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields["value"] = ChoiceField(
choices=RelativesAgeWhenDiagnosedValues.choices,
label="Were any of your relatives younger than 60 years old when they were diagnosed with lung cancer?",
widget=forms.RadioSelect,
error_messages={
'required': 'Select if your relatives were younger than 60 when they were diagnosed with lung cancer'
}
)

class Meta:
model = RelativesAgeWhenDiagnosedResponse
fields = ['value']
1 change: 0 additions & 1 deletion lung_cancer_screening/questions/jinja2/question_form.jinja
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{% extends 'layout.jinja' %}

{% from 'nhsuk/components/button/macro.jinja' import button %}
{% from 'nhsuk/components/back-link/macro.jinja' import backLink %}

{% if error %}
{% set error_message = { "text": error } %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% extends 'question_form.jinja' %}

{% block prelude %}

<h1 class="nhsuk-heading-l">If your relatives were under 60 when they were diagnosed</h1>

<p>We ask about their age when they were diagnosed with lung cancer because it may impact your chances of developing lung cancer.</p>

<p>If you do not know or cannot remember, select ‘I do not know’.</p>

{% endblock prelude %}
16 changes: 4 additions & 12 deletions lung_cancer_screening/questions/jinja2/responses.jinja
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
{% extends 'layout.jinja' %}
{% from 'nhsuk/components/button/macro.jinja' import button %}
{% from 'nhsuk/components/back-link/macro.jinja' import backLink %}

{% block beforeContent %}
<nav>
{{
backLink({
"href": url("questions:family_history_lung_cancer"),
"text": "Back"
})
}}
</nav>
{% endblock beforeContent %}
{% from 'nhsuk/components/button/macro.jinja' import button %}

{% block page_content %}
<div class="nhsuk-grid-row">
Expand All @@ -27,6 +16,9 @@
<li>What is your ethnic background? {{ response_set.ethnicity }}</li>
<li>Have you ever worked in a job where you might have been exposed to asbestos? {{ response_set.asbestos_exposure }}</li>
<li>Have you ever been diagnosed with any of the following respiratory conditions? {{ response_set.respiratory_conditions }}</li>
<li>Have you ever been diagnosed with cancer? {{ response_set.cancer_diagnosis }}</li>
<li>Have any of your parents, siblings or children ever been diagnosed with lung cancer {{ response_set.family_history_lung_cancer }}</li>
<li>Were any of your relatives younger than 60 years old when they were diagnosed with lung cancer? {{ response_set.relatives_age_when_diagnosed }}</li>
</ul>

<form action="{{ request.path }}" method="post">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 5.2.9 on 2026-01-05 12:20

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('questions', '0036_familyhistorylungcancerresponse'),
]

operations = [
migrations.CreateModel(
name='FamilyHistoryAgeWhenDiagnosedResponse',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('value', models.CharField(choices=[('Y', 'Yes, they were younger than 60'), ('N', 'No, they were 60 or older'), ('U', 'I do not know')], max_length=1)),
('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='family_history_age_when_diagnosed', to='questions.responseset')),
],
options={
'abstract': False,
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 5.2.9 on 2026-01-05 15:23

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('questions', '0037_familyhistoryagewhendiagnosedresponse'),
]

operations = [
migrations.CreateModel(
name='RelativesAgeWhenDiagnosedResponse',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('value', models.CharField(choices=[('Y', 'Yes, they were younger than 60'), ('N', 'No, they were 60 or older'), ('U', 'I do not know')], max_length=1)),
('response_set', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='relatives_age_when_diagnosed', to='questions.responseset')),
],
options={
'abstract': False,
},
),
migrations.DeleteModel(
name='FamilyHistoryAgeWhenDiagnosedResponse',
),
]
1 change: 1 addition & 0 deletions lung_cancer_screening/questions/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
from .weight_response import WeightResponse # noqa: F401
from .cancer_diagnosis_response import CancerDiagnosisResponse # noqa: F401
from .family_history_lung_cancer_response import FamilyHistoryLungCancerResponse # noqa: F401
from .relatives_age_when_diagnosed_response import RelativesAgeWhenDiagnosedResponse # noqa: F401
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@


class FamilyHistoryLungCancerValues(models.TextChoices):
YES = "Y", 'Yes'
NO = "N", 'No'
YES = "Y", "Yes"
NO = "N", "No"
UNKNOWN = "U", "I do not know"

class FamilyHistoryLungCancerResponse(BaseModel):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.db import models

from .base import BaseModel
from .response_set import ResponseSet


class RelativesAgeWhenDiagnosedValues(models.TextChoices):
YES = "Y", "Yes, they were younger than 60"
NO = "N", "No, they were 60 or older"
UNKNOWN = "U", "I do not know"

class RelativesAgeWhenDiagnosedResponse(BaseModel):
response_set = models.OneToOneField(ResponseSet, on_delete=models.CASCADE, related_name='relatives_age_when_diagnosed')
value = models.CharField(max_length=1, choices=RelativesAgeWhenDiagnosedValues.choices)
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,17 @@ def cancer_diagnosis(self):

@property
def family_history_lung_cancer(self):
if not hasattr(self.response_set, 'family_history_lung_cancer_response'):
if not hasattr(self.response_set, 'family_history_lung_cancer'):
return None

return self.response_set.family_history_lung_cancer_response.get_value_display()
return self.response_set.family_history_lung_cancer.get_value_display()

@property
def relatives_age_when_diagnosed(self):
if not hasattr(self.response_set, 'relatives_age_when_diagnosed'):
return None

return self.response_set.relatives_age_when_diagnosed.get_value_display()

@property
def respiratory_conditions(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import factory

from .response_set_factory import ResponseSetFactory
from ...models.relatives_age_when_diagnosed_response import RelativesAgeWhenDiagnosedResponse, RelativesAgeWhenDiagnosedValues


class RelativesAgeWhenDiagnosedResponseFactory(factory.django.DjangoModelFactory):
class Meta:
model = RelativesAgeWhenDiagnosedResponse

response_set = factory.SubFactory(ResponseSetFactory)
value = factory.Iterator(RelativesAgeWhenDiagnosedValues)
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from django.test import TestCase, tag

from ...factories.response_set_factory import ResponseSetFactory
from ....models.relatives_age_when_diagnosed_response import RelativesAgeWhenDiagnosedResponse, RelativesAgeWhenDiagnosedValues
from ....forms.relatives_age_when_diagnosed_form import RelativesAgeWhenDiagnosedForm

@tag("FamilyHistoryLungCancer")
class TestRelativesLungCancerForm(TestCase):
def setUp(self):
self.response_set = ResponseSetFactory()
self.response = RelativesAgeWhenDiagnosedResponse.objects.create(
response_set=self.response_set,
value=RelativesAgeWhenDiagnosedValues.NO
)


def test_is_valid_with_a_valid_value(self):
form = RelativesAgeWhenDiagnosedForm(
instance=self.response,
data={
"value": RelativesAgeWhenDiagnosedValues.NO
}
)
self.assertTrue(form.is_valid())
self.assertEqual(
form.cleaned_data["value"],
RelativesAgeWhenDiagnosedValues.NO
)

def test_is_invalid_with_an_invalid_value(self):
form = RelativesAgeWhenDiagnosedForm(
instance=self.response,
data={
"value": "invalid"
}
)
self.assertFalse(form.is_valid())
self.assertEqual(
form.errors["value"],
["Select a valid choice. invalid is not one of the available choices."]
)

def test_is_invalid_when_no_option_is_selected(self):
form = RelativesAgeWhenDiagnosedForm(
instance=self.response,
data={
"value": None
}
)
self.assertFalse(form.is_valid())
self.assertEqual(
form.errors["value"],
["Select if your relatives were younger than 60 when they were diagnosed with lung cancer"]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from django.test import TestCase, tag

from ...factories.response_set_factory import ResponseSetFactory
from ...factories.relatives_age_when_diagnosed_response_factory import RelativesAgeWhenDiagnosedResponseFactory, RelativesAgeWhenDiagnosedValues

from ....models.relatives_age_when_diagnosed_response import RelativesAgeWhenDiagnosedResponse

@tag("FamilyHistoryLungCancer")
class TestRelativesAgeWhenDiagnosedResponse(TestCase):
def setUp(self):
self.response_set = ResponseSetFactory()

def test_has_a_valid_factory(self):
model = RelativesAgeWhenDiagnosedResponseFactory.build(response_set=self.response_set)
model.full_clean()


def test_has_response_set_as_foreign_key(self):
response_set = ResponseSetFactory()
response = RelativesAgeWhenDiagnosedResponse.objects.create(
response_set=response_set,
value=RelativesAgeWhenDiagnosedValues.NO
)

self.assertEqual(response.response_set, response_set)

def test_has_value_as_string(self):
response_set = ResponseSetFactory()
response = RelativesAgeWhenDiagnosedResponse.objects.create(
response_set=response_set,
value=RelativesAgeWhenDiagnosedValues.NO
)

self.assertIsInstance(response.value, str)
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@
from ...factories.asbestos_exposure_response_factory import AsbestosExposureResponseFactory
from ...factories.respiratory_conditions_response_factory import RespiratoryConditionsResponseFactory
from ...factories.cancer_diagnosis_response_factory import CancerDiagnosisResponseFactory
from ...factories.family_history_lung_cancer_response_factory import FamilyHistoryLungCancerResponseFactory
from ...factories.relatives_age_when_diagnosed_response_factory import RelativesAgeWhenDiagnosedResponseFactory

from ....models.have_you_ever_smoked_response import HaveYouEverSmokedValues
from ....models.sex_at_birth_response import SexAtBirthValues
from ....models.gender_response import GenderValues
from ....models.ethnicity_response import EthnicityValues
from ....models.family_history_lung_cancer_response import FamilyHistoryLungCancerValues
from ....models.relatives_age_when_diagnosed_response import RelativesAgeWhenDiagnosedValues


from ....models.respiratory_conditions_response import RespiratoryConditionValues

from ....presenters.response_set_presenter import ResponseSetPresenter
Expand Down Expand Up @@ -178,3 +184,30 @@ def test_cancer_diagnosis_with_value(self):
)
presenter = ResponseSetPresenter(self.response_set)
self.assertEqual(presenter.cancer_diagnosis, "Yes")

def test_family_history_lung_cancer_with_no_value(self):
presenter = ResponseSetPresenter(self.response_set)
self.assertEqual(presenter.family_history_lung_cancer, None)

def test_family_history_lung_cancer_with_value(self):
FamilyHistoryLungCancerResponseFactory(
response_set=self.response_set,
value=FamilyHistoryLungCancerValues.YES
)

presenter = ResponseSetPresenter(self.response_set)

self.assertEqual(presenter.family_history_lung_cancer, FamilyHistoryLungCancerValues.YES.label)

def test_relative_age_when_diagnosed_with_no_value(self):
presenter = ResponseSetPresenter(self.response_set)
self.assertEqual(presenter.relatives_age_when_diagnosed, None)


def test_relative_age_when_diagnosed_with_value(self):
RelativesAgeWhenDiagnosedResponseFactory(
response_set=self.response_set,
value=RelativesAgeWhenDiagnosedValues.YES
)
presenter = ResponseSetPresenter(self.response_set)
self.assertEqual(presenter.relatives_age_when_diagnosed, RelativesAgeWhenDiagnosedValues.YES.label)
Loading