Skip to content

Commit dad2377

Browse files
authored
Merge pull request #152 from NHSDigital/dtos-9252-extract-appointment-status
[DTOS-9252] Extract appointment status to an append only log
2 parents 5e585cc + 54dea81 commit dad2377

14 files changed

Lines changed: 252 additions & 144 deletions

File tree

manage_breast_screening/clinics/tests/factories.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ClinicFactory(DjangoModelFactory):
3737
class Meta:
3838
model = models.Clinic
3939
django_get_or_create = ("starts_at", "ends_at")
40+
skip_postgeneration_save = True
4041

4142
type = FuzzyChoice(models.Clinic.TYPE_CHOICES)
4243
risk_type = FuzzyChoice(models.Clinic.RISK_TYPE_CHOICES)

manage_breast_screening/clinics/tests/system/test_user_views_clinic_show_page.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import re
2-
import pytest
3-
42
from datetime import datetime, timezone
3+
4+
import pytest
55
from django.urls import reverse
66
from playwright.sync_api import expect
77

8-
from manage_breast_screening.core.utils.date_formatting import format_time, format_date
8+
from manage_breast_screening.clinics.tests.factories import ClinicFactory
9+
from manage_breast_screening.core.system_test_setup import SystemTestCase
10+
from manage_breast_screening.core.utils.date_formatting import format_date, format_time
911
from manage_breast_screening.core.utils.string_formatting import (
1012
format_age,
1113
format_nhs_number,
1214
)
13-
from manage_breast_screening.participants.models import Appointment
14-
from manage_breast_screening.core.system_test_setup import SystemTestCase
15+
from manage_breast_screening.participants.models import AppointmentStatus
1516
from manage_breast_screening.participants.tests.factories import AppointmentFactory
16-
from manage_breast_screening.clinics.tests.factories import ClinicFactory
1717

1818

1919
class TestUserViewsClinicShowPage(SystemTestCase):
@@ -44,7 +44,7 @@ def given_there_are_appointments(self):
4444
self.confirmed_appointment = AppointmentFactory(
4545
clinic_slot__clinic=self.clinic,
4646
clinic_slot__starts_at=datetime.now(timezone.utc).replace(hour=9, minute=0),
47-
status=Appointment.Status.CONFIRMED,
47+
current_status=AppointmentStatus.CONFIRMED,
4848
screening_episode__participant__first_name="Janet",
4949
screening_episode__participant__last_name="Confirmed",
5050
)
@@ -53,14 +53,14 @@ def given_there_are_appointments(self):
5353
clinic_slot__starts_at=datetime.now(timezone.utc).replace(
5454
hour=9, minute=30
5555
),
56-
status=Appointment.Status.CHECKED_IN,
56+
current_status=AppointmentStatus.CHECKED_IN,
5757
)
5858
self.screened_appointment = AppointmentFactory(
5959
clinic_slot__clinic=self.clinic,
6060
clinic_slot__starts_at=datetime.now(timezone.utc).replace(
6161
hour=10, minute=45
6262
),
63-
status=Appointment.Status.SCREENED,
63+
current_status=AppointmentStatus.SCREENED,
6464
)
6565

6666
def given_i_am_on_the_clinic_list(self):
@@ -158,5 +158,5 @@ def _expect_rows_to_match_appointments(self, rows, appointments):
158158
format_age(appointment.screening_episode.participant.age())
159159
)
160160
expect(row.locator("td").nth(3)).to_contain_text(
161-
appointment.get_status_display()
161+
appointment.current_status.get_state_display()
162162
)

manage_breast_screening/clinics/views.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from django.shortcuts import render, get_object_or_404, redirect
1+
from django.shortcuts import get_object_or_404, redirect, render
22
from django.views.decorators.http import require_http_methods
33

4-
from .presenters import ClinicsPresenter, ClinicPresenter
5-
from .presenters import AppointmentListPresenter
6-
4+
from ..participants.models import Appointment, AppointmentStatus
75
from .models import Clinic
8-
from ..participants.models import Appointment
6+
from .presenters import AppointmentListPresenter, ClinicPresenter, ClinicsPresenter
97

108

119
def clinic_list(request, filter="today"):
@@ -44,7 +42,6 @@ def clinic(request, id, filter="remaining"):
4442
@require_http_methods(["POST"])
4543
def check_in(_request, id, appointment_id):
4644
appointment = get_object_or_404(Appointment, pk=appointment_id)
47-
appointment.status = Appointment.Status.CHECKED_IN
48-
appointment.save()
45+
appointment.statuses.create(state=AppointmentStatus.CHECKED_IN)
4946

5047
return redirect("clinics:show", id=id)
Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,21 @@
11
{% from 'components/tag/macro.jinja' import tag %}
2-
3-
{% set show_check_in = appointment.status.is_confirmed %}
4-
5-
<div data-event-status-container="{{ appointment.id }}" {% if show_check_in %}data-module="app-check-in"{% endif %}>
2+
{% set show_check_in = appointment.current_status.is_confirmed %}
3+
<div data-event-status-container="{{ appointment.id }}"
4+
{% if show_check_in %}data-module="app-check-in"{% endif %}>
65
<span data-hide-on-submit>{{ tag({
7-
"text": appointment.status.text,
8-
"classes": appointment.status.classes
9-
})}}</span>
10-
6+
"text": appointment.current_status.text,
7+
"classes": appointment.current_status.classes
8+
}) }}</span>
119
{% if show_check_in %}
12-
<span data-show-on-submit hidden>{{ tag({
13-
"text": "Checked in",
14-
"classes": "app-nowrap"
15-
})}}</span>
10+
<span data-show-on-submit hidden>{{ tag({"text": "Checked in", "classes": "app-nowrap"}) }}</span>
1611
{% endif %}
17-
1812
{% if show_check_in %}
19-
{% set action_url = check_in_url or url('mammograms:check_in', kwargs={'id': appointment.id}) %}
20-
<form action="{{ action_url }}" method="post" novalidate>
13+
{% set action_url = check_in_url or url('mammograms:check_in', kwargs={'id': appointment.id}) %}
14+
<form action="{{ action_url }}" method="post" novalidate>
2115
<p class="nhsuk-u-margin-top-2 nhsuk-u-margin-bottom-0">
22-
<button class="app-button--link">
23-
Check in
24-
</button>
16+
<button class="app-button--link">Check in</button>
2517
</p>
2618
{{ csrf_input }}
27-
</form>
19+
</form>
2820
{% endif %}
2921
</div>

manage_breast_screening/mammograms/forms.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from django import forms
22

3+
from ..participants.models import AppointmentStatus
4+
35

46
class ScreeningAppointmentForm(forms.Form):
57
decision = forms.ChoiceField(
@@ -115,7 +117,7 @@ def save(self):
115117
reasons_json[field_name] = value
116118
self.instance.stopped_reasons = reasons_json
117119
self.instance.reinvite = self.cleaned_data["decision"]
118-
self.instance.status = self.instance.Status.ATTENDED_NOT_SCREENED
119120
self.instance.save()
121+
self.instance.statuses.create(state=AppointmentStatus.ATTENDED_NOT_SCREENED)
120122

121123
return self.instance

manage_breast_screening/mammograms/presenters.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
from django.urls import reverse
22

33
from ..core.utils.date_formatting import format_date, format_relative_date, format_time
4-
from ..participants.models import Appointment
4+
from ..participants.models import AppointmentStatus
55
from ..participants.presenters import ParticipantPresenter
66

7-
Status = Appointment.Status
8-
97

108
def status_colour(status):
119
"""
1210
Color to render the status tag
1311
"""
1412
match status:
15-
case Status.CHECKED_IN:
13+
case AppointmentStatus.CHECKED_IN:
1614
return "" # no colour will get solid dark blue
17-
case Status.SCREENED:
15+
case AppointmentStatus.SCREENED:
1816
return "green"
19-
case Status.DID_NOT_ATTEND | Status.CANCELLED:
17+
case AppointmentStatus.DID_NOT_ATTEND | AppointmentStatus.CANCELLED:
2018
return "red"
21-
case Status.ATTENDED_NOT_SCREENED | Status.PARTIALLY_SCREENED:
19+
case (
20+
AppointmentStatus.ATTENDED_NOT_SCREENED
21+
| AppointmentStatus.PARTIALLY_SCREENED
22+
):
2223
return "orange"
2324
case _:
2425
return "blue" # default blue
@@ -45,7 +46,7 @@ def __init__(self, appointment):
4546
self._appointment = appointment
4647
self._last_known_screening = appointment.screening_episode.previous()
4748

48-
self.allStatuses = Status
49+
self.allStatuses = AppointmentStatus
4950
self.id = appointment.id
5051
self.clinic_slot = ClinicSlotPresenter(appointment.clinic_slot)
5152
self.participant = ParticipantPresenter(
@@ -61,14 +62,15 @@ def start_time(self):
6162
return self.clinic_slot.starts_at
6263

6364
@property
64-
def status(self):
65-
colour = status_colour(self._appointment.status)
65+
def current_status(self):
66+
current_status = self._appointment.current_status
67+
colour = status_colour(current_status.state)
6668

6769
return {
6870
"classes": f"nhsuk-tag--{colour} app-nowrap" if colour else "app-nowrap",
69-
"text": self._appointment.get_status_display(),
70-
"key": self._appointment.status,
71-
"is_confirmed": self._appointment.status == Status.CONFIRMED,
71+
"text": current_status.get_state_display(),
72+
"key": current_status.state,
73+
"is_confirmed": current_status.state == AppointmentStatus.CONFIRMED,
7274
}
7375

7476
@property

manage_breast_screening/mammograms/tests/system/test_user_submits_cannot_go_ahead_form.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from playwright.sync_api import expect
66

77
from manage_breast_screening.core.system_test_setup import SystemTestCase
8-
from manage_breast_screening.participants.models import Appointment
8+
from manage_breast_screening.participants.models import AppointmentStatus
99
from manage_breast_screening.participants.tests.factories import (
1010
AppointmentFactory,
1111
ParticipantFactory,
@@ -93,7 +93,8 @@ def then_i_see_the_clinics_page(self):
9393
def and_the_appointment_is_updated(self):
9494
self.appointment.refresh_from_db()
9595
self.assertEqual(
96-
self.appointment.status, Appointment.Status.ATTENDED_NOT_SCREENED
96+
self.appointment.current_status.state,
97+
AppointmentStatus.ATTENDED_NOT_SCREENED,
9798
)
9899
self.assertEqual(self.appointment.reinvite, True)
99100
self.assertEqual(

manage_breast_screening/mammograms/tests/test_presenters.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
import time_machine
88

99
from manage_breast_screening.clinics.models import ClinicSlot
10-
from manage_breast_screening.participants.models import Appointment, ScreeningEpisode
10+
from manage_breast_screening.participants.models import (
11+
Appointment,
12+
AppointmentStatus,
13+
ScreeningEpisode,
14+
)
1115

1216
from ..presenters import AppointmentPresenter, ClinicSlotPresenter
1317

@@ -24,21 +28,21 @@ def mock_appointment(self):
2428
"status, expected_classes, expected_text, expected_key, expected_is_confirmed",
2529
[
2630
(
27-
Appointment.Status.CONFIRMED,
31+
AppointmentStatus.CONFIRMED,
2832
"nhsuk-tag--blue app-nowrap",
2933
"Confirmed",
3034
"CONFIRMED",
3135
True,
3236
),
3337
(
34-
Appointment.Status.CHECKED_IN,
38+
AppointmentStatus.CHECKED_IN,
3539
"app-nowrap",
3640
"Checked in",
3741
"CHECKED_IN",
3842
False,
3943
),
4044
(
41-
Appointment.Status.ATTENDED_NOT_SCREENED,
45+
AppointmentStatus.ATTENDED_NOT_SCREENED,
4246
"nhsuk-tag--orange app-nowrap",
4347
"Attended not screened",
4448
"ATTENDED_NOT_SCREENED",
@@ -55,12 +59,9 @@ def test_status(
5559
expected_key,
5660
expected_is_confirmed,
5761
):
58-
mock_appointment.status = status
59-
mock_appointment.get_status_display.return_value = Appointment.STATUS_CHOICES[
60-
status
61-
]
62+
mock_appointment.current_status = AppointmentStatus(state=status)
6263

63-
result = AppointmentPresenter(mock_appointment).status
64+
result = AppointmentPresenter(mock_appointment).current_status
6465

6566
assert result["classes"] == expected_classes
6667
assert result["text"] == expected_text

manage_breast_screening/mammograms/views.py

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
from django.views.decorators.http import require_http_methods
66
from django.views.generic import FormView
77

8-
from manage_breast_screening.participants.models import Appointment, Participant
9-
8+
from ..participants.models import Appointment, AppointmentStatus, Participant
109
from .forms import (
1110
AppointmentCannotGoAheadForm,
1211
AskForMedicalInformationForm,
@@ -15,30 +14,11 @@
1514
)
1615
from .presenters import AppointmentPresenter, present_secondary_nav
1716

18-
Status = Appointment.Status
19-
2017
APPOINTMENT_CANNOT_PROCEED = "Appointment cannot proceed"
2118

2219
logger = logging.getLogger(__name__)
2320

2421

25-
def status_color(status):
26-
"""
27-
Color to render the status tag
28-
"""
29-
match status:
30-
case Status.CHECKED_IN:
31-
return "" # no colour will get solid dark blue
32-
case Status.SCREENED:
33-
return "green"
34-
case (Status.DID_NOT_ATTEND, Status.CANCELLED):
35-
return "red"
36-
case (Status.ATTENDED_NOT_SCREENED, Status.PARTIALLY_SCREENED):
37-
return "orange"
38-
case _:
39-
return "blue" # default blue
40-
41-
4222
class BaseAppointmentForm(FormView):
4323
@property
4424
def appointment_id(self):
@@ -75,9 +55,9 @@ def get_context_data(self, **kwargs):
7555
}
7656
)
7757

78-
if appointment.status in [
79-
appointment.Status.SCREENED,
80-
appointment.Status.PARTIALLY_SCREENED,
58+
if AppointmentStatus in [
59+
AppointmentStatus.SCREENED,
60+
AppointmentStatus.PARTIALLY_SCREENED,
8161
]:
8262
context["secondary_nav_items"] = present_secondary_nav(appointment.pk)
8363

@@ -200,9 +180,8 @@ def awaiting_images(request, id):
200180

201181

202182
@require_http_methods(["POST"])
203-
def check_in(request, id):
183+
def check_in(_request, id):
204184
appointment = get_object_or_404(Appointment, pk=id)
205-
appointment.status = Appointment.Status.CHECKED_IN
206-
appointment.save()
185+
appointment.statuses.create(state=AppointmentStatus.CHECKED_IN)
207186

208187
return redirect("mammograms:start_screening", id=id)

manage_breast_screening/participants/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class AppointmentAdmin(admin.ModelAdmin):
1616
"name",
1717
"clinic_slot__starts_at",
1818
"clinic_slot__duration_in_minutes",
19-
"status",
19+
"statuses__state",
2020
]
2121

2222
@admin.display()

0 commit comments

Comments
 (0)