Skip to content

Comments

Add hormone replacement therapy page#1016

Merged
swebberuk merged 1 commit intomainfrom
DTOSS-12223-hrt
Feb 18, 2026
Merged

Add hormone replacement therapy page#1016
swebberuk merged 1 commit intomainfrom
DTOSS-12223-hrt

Conversation

@swebberuk
Copy link
Contributor

@swebberuk swebberuk commented Feb 11, 2026

Added page to record details of hormone replacement therapy.

Page can be accessed from the 'record medical information' page.

Description

image image image

Jira link

https://nhsd-jira.digital.nhs.uk/browse/DTOSS-12223

Review notes

Review checklist

  • Check database queries are correctly scoped to current_provider

@swebberuk swebberuk force-pushed the DTOSS-12223-hrt branch 3 times, most recently from 6b67fbd to e68c11b Compare February 11, 2026 15:09
choices=HormoneReplacementTherapy.Status,
label=f"Is {participant.full_name} currently taking HRT?",
label_classes="nhsuk-fieldset__legend--l",
page_heading=True,
Copy link
Contributor Author

@swebberuk swebberuk Feb 11, 2026

Choose a reason for hiding this comment

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

Set page_heading=True as want the label to be <h1>. Required as pages must have a level-one heading and, unlike other pages, this page does not have a separate title.

visually_hidden_label_suffix=None,
classes=None,
choice_hints=None,
page_heading=False,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added page_heading so can be used by label.jinja to determine if label should be wrapped in <h1>.

NO_BUT_STOPPED_RECENTLY = "NO_BUT_STOPPED_RECENTLY", "No, but stopped recently"
NO = "NO", "No"

appointment = models.OneToOneField(Appointment, on_delete=models.PROTECT)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have defined relationship with appointment as one-to-one.

This is consistent with what we've done for Study.

This is different to what we've done for CystHistoryItem, even though we also restrict that to one per appointment.

appointment = models.ForeignKey(
  Appointment,
  on_delete=models.PROTECT,
  related_name="cyst_history_items",
)

"MastectomyOrLumpectomyHistoryItem", # Temporarily allowed
"OtherProcedureHistoryItem", # Temporarily allowed
"ParticipantReportedMammogram", # Temporarily allowed
"HormoneReplacementTherapy", # Temporarily allowed
Copy link
Contributor Author

@swebberuk swebberuk Feb 11, 2026

Choose a reason for hiding this comment

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

Added this to avoid:

Disallowed references detected:
- manage_breast_screening/mammograms/views/other_relevant_information/hormone_replacement_therapy_views.py:64 -> model.objects
    return HormoneReplacementTherapy.objects.get(
make: *** [Makefile:110: test-lint] Error 1

Have followed same approach as used in other views, e.g. UpdateOtherProcedureHistoryView.

Don't know what the preferred solution for this is?

When we agree on a solution then should also fix for the other items in this list.

Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't be adding to this list, the linter is here to avoid us unintentionally exposing data from other providers.

Instead of HormoneReplacmentTherapy.objects.get you can access it via the Appointment instance, and this will ensure that access is scoped to the user's allowed providers (as the appointment is already provider scoped).

You could add a filter to the .objects.get call to accomplish the same thing, but the linter is not smart enough to detect that.

{% macro label_html(field) %}
{% set unbound_field = field.field %}
{% if unbound_field.page_heading -%}
<h1 class="nhsuk-fieldset__heading">
Copy link
Contributor Author

@swebberuk swebberuk Feb 11, 2026

Choose a reason for hiding this comment

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

The HRT page is different than other pages. It contains a single choice field without a title. For accessibility requirements, want the label of the choice field to be a level-one heading. Altered label.jinja to support that.

Image

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused by this - that text is the legend, not a label.

I'd expect isPageHeading or similar to be set on a legend property. @MatMoore does our radio component support isPageHeading?

Copy link
Contributor

Choose a reason for hiding this comment

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

This macro name is a bit misleading as it can be used with both labels and legends. The naming follows the Django form field naming, and the form field abstraction doesn't care about the tags used in the html.

We are calling it like this in nhsuk_forms/jinja2/forms/radios.jinja:

    "legend": {
      "html": label_html(field),
      "classes": unbound_field.label_classes
    }

The jinja component does support isPageHeading, but the above macro that converts between Django form field and component params doesn't pass it through.

It looks like this diff is producing the correct HTML, but we could equivalently add "isPageHeading": unbound_field.page_heading to the legend params above, and then we wouldn't need the extra conditional here (correct me if I'm wrong.)

If that's the case, then I think using that param would be preferable. The more we depend on nhsuk-frontend-jinja for our templating, the easier it will be to stay in sync with the design system.

@swebberuk swebberuk changed the title [wip] Add hormone replacement therapy page Add hormone replacement therapy page Feb 11, 2026
@swebberuk swebberuk marked this pull request as ready for review February 11, 2026 15:36
@swebberuk swebberuk force-pushed the DTOSS-12223-hrt branch 2 times, most recently from c58ae3a to a526e0f Compare February 12, 2026 11:39
@edwardhorsford
Copy link
Contributor

Mentioned via slack, but we've slightly tweaked the hint text for these fields to be more specific.

self.fields["status"] = ChoiceField(
choices=HormoneReplacementTherapy.Status,
label=f"Is {participant.full_name} currently taking HRT?",
label_classes="nhsuk-label--l",
Copy link
Contributor

Choose a reason for hiding this comment

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

As a sidenote, something I noticed recently is that at the component level, you can pass a size param (l/m/s) instead of specifying classes. So at some point it may be worth switching all the Field classes to that approach, to simplify the forms a bit further.

Copy link
Contributor

Choose a reason for hiding this comment

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

We only added the feature recently - but yes probably good to adopt.

"value" : {
"html": hormone_replacement_therapy_details(presenter)
},
"actions": {
Copy link
Contributor

Choose a reason for hiding this comment

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

We could maybe make this whole actions object an attribute of the presenter, since there's no html involved here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have made this change.

appointment.hormone_replacement_therapy
if hasattr(appointment, "hormone_replacement_therapy")
else None
)
Copy link
Contributor

Choose a reason for hiding this comment

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

can this be shortened to self.hormone_replacement_therapy = getattr(appointment, "hormone_replacement_therapy", None)?

)

status = models.CharField(choices=Status)
start_date = models.CharField(blank=True, null=False, default="")
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we should name this approx_start_date or something like that so it's clearer this is a free text value rather than an exact date? Maybe have a look at what we've done on the forms for previous mammograms and symptoms as they are similar I think.

Copy link
Contributor

Choose a reason for hiding this comment

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

Strong agree for explicit naming where these are approximate things

status = models.CharField(choices=Status)
start_date = models.CharField(blank=True, null=False, default="")
end_date = models.CharField(blank=True, null=False, default="")
previous_duration = models.CharField(blank=True, null=False, default="")
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, duration makes it sound more precise than it is.

"MastectomyOrLumpectomyHistoryItem", # Temporarily allowed
"OtherProcedureHistoryItem", # Temporarily allowed
"ParticipantReportedMammogram", # Temporarily allowed
"HormoneReplacementTherapy", # Temporarily allowed
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't be adding to this list, the linter is here to avoid us unintentionally exposing data from other providers.

Instead of HormoneReplacmentTherapy.objects.get you can access it via the Appointment instance, and this will ensure that access is scoped to the user's allowed providers (as the appointment is already provider scoped).

You could add a filter to the .objects.get call to accomplish the same thing, but the linter is not smart enough to detect that.

@github-actions
Copy link

github-actions bot commented Feb 17, 2026

The review app at this URL has been deleted:
https://pr-1016.manage-breast-screening.non-live.screening.nhs.uk

@swebberuk
Copy link
Contributor Author

swebberuk commented Feb 17, 2026

  1. Replaced HormoneReplacmentTherapy.objects.get with:
appointment = self.appointment
return appointment.hormone_replacement_therapy
  1. Renamed start_date, end_date and previous_duration to approx_start_date, approx_end_date and approx_previous_duration.

  2. Shortened:

appointment.hormone_replacement_therapy
if hasattr(appointment, "hormone_replacement_therapy")
  else None
 )

to getattr(appointment, "hormone_replacement_therapy", None)

  1. Reverted change in labels.jinja and added "isPageHeading": unbound_field.page_heading to radios.jinja.

@swebberuk swebberuk requested a review from MatMoore February 17, 2026 16:10
@sonarqubecloud
Copy link

@swebberuk swebberuk merged commit 9f5ccc0 into main Feb 18, 2026
15 checks passed
@swebberuk swebberuk deleted the DTOSS-12223-hrt branch February 18, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants