Skip to content

Commit f4658ff

Browse files
authored
Merge pull request #4936 from nhsuk/refactor-important-notices
Refactor important notices
2 parents 2ad41c4 + 9efa49d commit f4658ff

22 files changed

Lines changed: 800 additions & 317 deletions

app/components/app_imports_navigation_component.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def call
2020
selected: active == :issues
2121
)
2222

23-
if policy(:notices).index?
23+
if policy(ImportantNotice).index?
2424
nav.with_item(
2525
href: imports_notices_path,
2626
text: notices_text,
@@ -42,7 +42,7 @@ def issues_text
4242
end
4343

4444
def notices_text
45-
count = ImportantNotices.call(patient_scope: policy_scope(Patient)).length
45+
count = policy_scope(ImportantNotice).count
4646

4747
text_with_count("Important notices", count)
4848
end

app/components/app_notices_table_component.html.erb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@
1919
<% body.with_row do |row| %>
2020
<% row.with_cell do %>
2121
<span class="nhsuk-table-responsive__heading">Date</span>
22-
<%= notice[:date_time].to_date.to_fs(:long) %>
22+
<%= notice.recorded_at.to_date.to_fs(:long) %>
2323
<% end %>
2424
<% row.with_cell do %>
2525
<span class="nhsuk-table-responsive__heading">Child</span>
26-
<%= link_to notice[:patient].full_name, patient_path(notice[:patient]) %>
26+
<%= link_to notice.patient.full_name, patient_path(notice.patient) %>
2727
<% end %>
2828
<% row.with_cell do %>
2929
<span class="nhsuk-table-responsive__heading">Notice</span>
30-
<%= notice[:message] %>
30+
<%= notice.message %>
3131
<% end %>
3232
<% end %>
3333
<% end %>

app/components/app_patient_card_component.rb

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class AppPatientCardComponent < ViewComponent::Base
66
<% card.with_heading(level: heading_level) { "Child’s details" } %>
77
88
<% important_notices.each do |notice| %>
9-
<%= render AppStatusComponent.new(text: notice[:message]) %>
9+
<%= render AppStatusComponent.new(text: notice) %>
1010
<% end %>
1111
1212
<%= render AppChildSummaryComponent.new(
@@ -46,5 +46,40 @@ def initialize(
4646

4747
def show_school_and_year_group = patient.show_year_group?(team: current_team)
4848

49-
def important_notices = ImportantNotices.call(patient:)
49+
def important_notices
50+
notices = patient.important_notices.where(team_id: current_team.id)
51+
52+
[
53+
(
54+
if patient.restricted?
55+
notices.restricted.order(recorded_at: :desc).first&.message
56+
end
57+
),
58+
(
59+
if patient.invalidated?
60+
notices.invalidated.order(recorded_at: :desc).first&.message
61+
end
62+
),
63+
*notices.deceased.first&.message,
64+
*gillick_no_notify_notices
65+
].compact
66+
end
67+
68+
def gillick_no_notify_notices
69+
no_notify_vaccination_records =
70+
patient.vaccination_records.select do
71+
it.notify_parents == false && it.team == current_team
72+
end
73+
74+
if no_notify_vaccination_records.any?
75+
vaccinations_sentence =
76+
"#{no_notify_vaccination_records.map(&:programme).uniq.map(&:name).to_sentence} " \
77+
"#{"vaccination".pluralize(no_notify_vaccination_records.length)}"
78+
79+
"Child gave consent for #{vaccinations_sentence} under Gillick competence and " \
80+
"does not want their parents to be notified. " \
81+
"These records will not be automatically synced with GP records. " \
82+
"Your team must let the child's GP know they were vaccinated."
83+
end
84+
end
5085
end

app/controllers/dashboard_controller.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ class DashboardController < ApplicationController
77

88
def index
99
@notices_count =
10-
if policy(:notices).index?
11-
ImportantNotices.call(patient_scope: policy_scope(Patient)).length
12-
end
10+
(policy_scope(ImportantNotice).count if policy(ImportantNotice).index?)
1311
end
1412
end

app/controllers/imports/notices_controller.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ class Imports::NoticesController < ApplicationController
44
layout "full"
55

66
def index
7-
authorize :notices
7+
authorize ImportantNotice
88

9-
@notices = ImportantNotices.call(patient_scope: policy_scope(Patient))
9+
@notices =
10+
policy_scope(ImportantNotice).includes(
11+
vaccination_record: :programme
12+
).order(recorded_at: :desc)
1013
end
1114
end
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# frozen_string_literal: true
2+
3+
class ImportantNoticeGeneratorJob < ApplicationJob
4+
queue_as :cache
5+
6+
BATCH_SIZE = 1000
7+
8+
def perform(patient_id = nil)
9+
if patient_id.present?
10+
process_batch(
11+
[
12+
Patient.includes(
13+
:teams,
14+
vaccination_records: %i[programme team]
15+
).find(patient_id)
16+
]
17+
)
18+
else
19+
Patient
20+
.includes(:teams, vaccination_records: %i[programme team])
21+
.find_in_batches(batch_size: BATCH_SIZE) do |patients_batch|
22+
process_batch(patients_batch)
23+
end
24+
end
25+
end
26+
27+
private
28+
29+
def process_batch(patients)
30+
notices_to_create = []
31+
notice_ids_to_dismiss = []
32+
33+
patient_ids = patients.map(&:id)
34+
35+
existing_notices =
36+
ImportantNotice
37+
.where(patient_id: patient_ids)
38+
.index_by { |n| notice_key(n) }
39+
40+
patient_team_ids =
41+
patients.each_with_object({}) do |patient, hash|
42+
hash[patient.id] = patient.teams.map(&:id)
43+
end
44+
45+
patients.each do |patient|
46+
collect_notices_for_patient(
47+
patient,
48+
notices_to_create,
49+
notice_ids_to_dismiss,
50+
existing_notices,
51+
patient_team_ids[patient.id]
52+
)
53+
end
54+
55+
if notices_to_create.any?
56+
ImportantNotice.import(notices_to_create, validate: false)
57+
end
58+
59+
if notice_ids_to_dismiss.any?
60+
ImportantNotice.where(id: notice_ids_to_dismiss).update_all(
61+
dismissed_at: Time.current
62+
)
63+
end
64+
end
65+
66+
def collect_notices_for_patient(
67+
patient,
68+
notices_to_create,
69+
notice_ids_to_dismiss,
70+
existing_notices,
71+
team_ids
72+
)
73+
return if team_ids.empty?
74+
75+
team_ids.each do |team_id|
76+
if patient.deceased? &&
77+
!notice_exists?(existing_notices, patient.id, :deceased, team_id)
78+
notices_to_create << ImportantNotice.new(
79+
patient:,
80+
team_id: team_id,
81+
type: :deceased,
82+
recorded_at: patient.date_of_death_recorded_at
83+
)
84+
end
85+
86+
if patient.invalidated? &&
87+
!notice_exists?(existing_notices, patient.id, :invalidated, team_id)
88+
notices_to_create << ImportantNotice.new(
89+
patient:,
90+
team_id: team_id,
91+
type: :invalidated,
92+
recorded_at: patient.invalidated_at
93+
)
94+
end
95+
96+
if patient.restricted? &&
97+
!restricted_notice_exists?(existing_notices, patient)
98+
notices_to_create << ImportantNotice.new(
99+
patient:,
100+
team_id: team_id,
101+
type: :restricted,
102+
recorded_at: patient.restricted_at
103+
)
104+
end
105+
106+
collect_gillick_no_notify_notices(
107+
patient,
108+
team_id,
109+
notices_to_create,
110+
existing_notices
111+
)
112+
113+
unless patient.invalidated?
114+
existing_notices.each_value do |notice|
115+
unless notice.patient_id == patient.id && notice.team_id == team_id &&
116+
notice.type == "invalidated" && notice.dismissed_at.nil?
117+
next
118+
end
119+
notice_ids_to_dismiss << notice.id
120+
end
121+
end
122+
123+
unless patient.restricted?
124+
existing_notices.each_value do |notice|
125+
unless notice.patient_id == patient.id && notice.team_id == team_id &&
126+
notice.type == "restricted" && notice.dismissed_at.nil?
127+
next
128+
end
129+
notice_ids_to_dismiss << notice.id
130+
end
131+
end
132+
133+
next if patient.deceased?
134+
existing_notices.each_value do |notice|
135+
unless notice.patient_id == patient.id && notice.team_id == team_id &&
136+
notice.type == "deceased" && notice.dismissed_at.nil?
137+
next
138+
end
139+
notice_ids_to_dismiss << notice.id
140+
end
141+
end
142+
end
143+
144+
def collect_gillick_no_notify_notices(
145+
patient,
146+
team_id,
147+
notices_to_create,
148+
existing_notices
149+
)
150+
no_notify_vaccination_records =
151+
patient.vaccination_records.select do |record|
152+
record.team&.id == team_id && record.notify_parents == false
153+
end
154+
155+
return if no_notify_vaccination_records.empty?
156+
157+
no_notify_vaccination_records.each do |record|
158+
if notice_exists?(
159+
existing_notices,
160+
patient.id,
161+
:gillick_no_notify,
162+
team_id,
163+
record.id
164+
)
165+
next
166+
end
167+
168+
notices_to_create << ImportantNotice.new(
169+
patient:,
170+
team_id: team_id,
171+
vaccination_record_id: record.id,
172+
type: :gillick_no_notify,
173+
recorded_at: record.performed_at
174+
)
175+
end
176+
end
177+
178+
def notice_key(notice)
179+
[
180+
notice.patient_id,
181+
notice.type,
182+
notice.team_id,
183+
notice.vaccination_record_id
184+
]
185+
end
186+
187+
def notice_exists?(
188+
existing_notices,
189+
patient_id,
190+
type,
191+
team_id,
192+
vaccination_record_id = nil
193+
)
194+
key = [patient_id, type.to_s, team_id, vaccination_record_id]
195+
existing_notices.key?(key)
196+
end
197+
198+
def restricted_notice_exists?(existing_notices, patient)
199+
existing_notices.values.any? do |notice|
200+
notice.patient_id == patient.id && notice.type == "restricted" &&
201+
(
202+
notice.dismissed_at.nil? ||
203+
notice.dismissed_at >= patient.restricted_at
204+
)
205+
end
206+
end
207+
end

0 commit comments

Comments
 (0)