Skip to content

Commit 82ef38e

Browse files
committed
Add ability to show PII related information on the timeline
A show_pii flag allows optional exclusion of personall-identifiable information from the event details and audited changes relating to a patient. This has been set to true by default in the controller, but can be changed to false trivially in case we decide not to display PII in the future.
1 parent e94e884 commit 82ef38e

File tree

4 files changed

+176
-30
lines changed

4 files changed

+176
-30
lines changed

app/controllers/inspect/timeline/patients_controller.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class PatientsController < ApplicationController
99

1010
layout "full"
1111

12+
SHOW_PII = true
13+
1214
DEFAULT_EVENT_NAMES = %w[
1315
consents
1416
school_moves
@@ -22,6 +24,8 @@ class PatientsController < ApplicationController
2224
].freeze
2325

2426
def show
27+
@show_pii = SHOW_PII
28+
2529
params.reverse_merge!(event_names: DEFAULT_EVENT_NAMES)
2630
params[:audit_config] ||= {}
2731

@@ -46,7 +50,8 @@ def show
4650
TimelineRecords.new(
4751
@patient,
4852
detail_config: build_details_config,
49-
audit_config: audit_config
53+
audit_config: audit_config,
54+
show_pii: @show_pii
5055
).load_grouped_events(event_names)
5156

5257
@no_events_message = true if @patient_timeline.empty?

app/views/inspect/timeline/patients/show.html.erb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
<div class="nhsuk-grid-row nhsuk-u-margin-top-4">
1818

19-
<div class="nhsuk-grid-column-one-third app-grid-column--sticky">
19+
<div class="nhsuk-grid-column-one-half app-grid-column--sticky">
2020
<%= render AppTimelineFilterComponent.new(
2121
url: inspect_timeline_patient_path,
2222
patient: @patient,
2323
event_options: TimelineRecords::DEFAULT_DETAILS_CONFIG,
24-
timeline_fields: TimelineRecords::AVAILABLE_DETAILS_CONFIG,
24+
timeline_fields: @show_pii ? TimelineRecords::AVAILABLE_DETAILS_CONFIG_WITH_PII : TimelineRecords::AVAILABLE_DETAILS_CONFIG,
2525
class_imports: @patient_events[:class_imports],
2626
cohort_imports: @patient_events[:cohort_imports],
2727
sessions: @patient_events[:sessions],
@@ -34,7 +34,7 @@
3434
) %>
3535
</div>
3636

37-
<div class="nhsuk-grid-column-two-thirds">
37+
<div class="nhsuk-grid-column-one-half">
3838
<% if @no_events_message %>
3939
<%= render AppWarningCalloutComponent.new(
4040
heading: "No events found",

lib/timeline_records.rb

Lines changed: 141 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,108 @@
22

33
class TimelineRecords
44
DEFAULT_DETAILS_CONFIG = {
5-
cohort_imports: [],
6-
class_imports: [],
7-
patient_sessions: %i[session_id],
8-
school_moves: %i[school_id source],
9-
school_move_log_entries: %i[school_id user_id],
105
consents: %i[response route],
6+
sessions: %i[],
7+
session_attendances: %i[],
118
triages: %i[status performed_by_user_id],
12-
vaccination_records: %i[outcome session_id]
9+
vaccination_records: %i[outcome session_id],
10+
organisation: %i[],
11+
cohort_imports: %i[],
12+
class_imports: %i[],
13+
parents: %i[],
14+
patient_sessions: %i[session_id],
15+
gillick_assessments: %i[],
16+
parent_relationships: %i[],
17+
school_moves: %i[school_id source],
18+
school_move_log_entries: %i[school_id user_id]
1319
}.freeze
1420

1521
AVAILABLE_DETAILS_CONFIG = {
16-
cohort_imports: %i[rows_count status uploaded_by_user_id],
17-
class_imports: %i[rows_count year_groups uploaded_by_user_id session_id],
18-
patient_sessions: %i[session_id],
19-
school_moves: %i[source school_id home_educated],
20-
school_move_log_entries: %i[user_id school_id home_educated],
21-
consents: %i[
22-
programme_id
23-
response
24-
route
25-
parent_id
26-
withdrawn_at
27-
invalidated_at
22+
consents: %i[response route updated_at withdrawn_at invalidated_at],
23+
sessions: %i[slug academic_year],
24+
session_attendances: %i[attending updated_at],
25+
triages: %i[status updated_at invalidated_at performed_by_user_id],
26+
vaccination_records: %i[
27+
outcome
28+
performed_at
29+
updated_at
30+
discarded_at
31+
uuid
32+
session_id
33+
],
34+
organisation: %i[name ods_code],
35+
cohort_imports: %i[
36+
csv_filename
37+
processed_at
38+
status
39+
rows_count
40+
new_record_count
41+
exact_duplicate_record_count
42+
changed_record_count
2843
],
29-
triages: %i[status performed_by_user_id programme_id invalidated_at],
44+
class_imports: %i[
45+
csv_filename
46+
processed_at
47+
status
48+
rows_count
49+
new_record_count
50+
exact_duplicate_record_count
51+
changed_record_count
52+
year_groups
53+
],
54+
parents: %i[],
55+
patient_sessions: %i[],
56+
gillick_assessments: %i[],
57+
parent_relationships: %i[],
58+
school_moves: %i[school_id source],
59+
school_move_log_entries: %i[school_id user_id]
60+
}.freeze
61+
62+
AVAILABLE_DETAILS_CONFIG_WITH_PII = {
63+
consents: %i[response route updated_at withdrawn_at invalidated_at],
64+
sessions: %i[slug academic_year],
65+
session_attendances: %i[attending updated_at],
66+
triages: %i[status updated_at invalidated_at performed_by_user_id],
3067
vaccination_records: %i[
3168
outcome
32-
performed_by_user_id
33-
programme_id
69+
performed_at
70+
updated_at
71+
discarded_at
72+
uuid
3473
session_id
35-
vaccine_id
36-
]
74+
],
75+
organisation: %i[name ods_code],
76+
cohort_imports: %i[
77+
csv_filename
78+
processed_at
79+
status
80+
rows_count
81+
new_record_count
82+
exact_duplicate_record_count
83+
changed_record_count
84+
],
85+
class_imports: %i[
86+
csv_filename
87+
processed_at
88+
status
89+
rows_count
90+
new_record_count
91+
exact_duplicate_record_count
92+
changed_record_count
93+
year_groups
94+
],
95+
parents: %i[full_name email phone],
96+
patient_sessions: %i[session_id],
97+
gillick_assessments: %i[
98+
knows_vaccination
99+
knows_disease
100+
knows_consequences
101+
knows_delivery
102+
knows_side_effects
103+
],
104+
parent_relationships: %i[type other_name],
105+
school_moves: %i[school_id source],
106+
school_move_log_entries: %i[school_id user_id]
37107
}.freeze
38108

39109
DEFAULT_AUDITS_CONFIG = {
@@ -67,14 +137,57 @@ class TimelineRecords
67137
source
68138
].freeze
69139

70-
def initialize(patient, detail_config: {}, audit_config: {})
140+
ALLOWED_AUDITED_CHANGES_WITH_PII = %i[
141+
full_name
142+
email
143+
phone
144+
nhs_number
145+
given_name
146+
family_name
147+
date_of_birth
148+
address_line_1
149+
address_line_2
150+
address_town
151+
address_postcode
152+
home_educated
153+
updated_from_pds_at
154+
restricted_at
155+
date_of_death
156+
pending_changes
157+
patient_id
158+
session_id
159+
programme_id
160+
vaccine_id
161+
organisation_id
162+
school_id
163+
gp_practice_id
164+
uploaded_by_user_id
165+
performed_by_user_id
166+
user_id
167+
parent_id
168+
status
169+
outcome
170+
response
171+
route
172+
date_of_death_recorded_at
173+
restricted_at
174+
invalidated_at
175+
withdrawn_at
176+
rows_count
177+
year_groups
178+
home_educated
179+
source
180+
].freeze
181+
182+
def initialize(patient, detail_config: {}, audit_config: {}, show_pii: false)
71183
@patient = patient
72184
@patient_id = @patient.id
73185
@patient_events = patient_events(@patient)
74186
@additional_events = additional_events(@patient)
75187
@detail_config = extract_detail_config(detail_config)
76188
@events = []
77189
@audit_config = audit_config
190+
@show_pii = show_pii
78191
end
79192

80193
def render_timeline(*event_names, truncate_columns: false)
@@ -213,12 +326,15 @@ def audits_events
213326
end
214327
).reject { it.audited_changes.keys == ["updated_from_pds_at"] }
215328

329+
allowed_changes =
330+
@show_pii ? ALLOWED_AUDITED_CHANGES_WITH_PII : ALLOWED_AUDITED_CHANGES
331+
216332
audit_events.map do |audit|
217333
filtered_changes =
218334
audit
219335
.audited_changes
220336
.each_with_object({}) do |(key, value), hash|
221-
if ALLOWED_AUDITED_CHANGES.include?(key.to_sym)
337+
if allowed_changes.include?(key.to_sym)
222338
hash[key] = value
223339
elsif audits[:include_filtered_audit_changes]
224340
hash[key] = "[FILTERED]"

spec/lib/timeline_records_spec.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22

33
describe TimelineRecords do
44
subject(:timeline) do
5-
described_class.new(patient, detail_config: detail_config)
5+
described_class.new(
6+
patient,
7+
detail_config: detail_config,
8+
show_pii: show_pii
9+
)
610
end
711

812
let(:programme) { create(:programme, :hpv) }
913
let(:detail_config) { {} }
14+
let(:show_pii) { false }
1015
let(:organisation) { create(:organisation, programmes: [programme]) }
1116
let(:session) { create(:session, organisation:, programmes: [programme]) }
1217
let(:class_import) { create(:class_import, session:) }
@@ -410,6 +415,26 @@
410415
end
411416
end
412417

418+
context "with show_pii: true" do
419+
let(:show_pii) { true }
420+
421+
it "includes PII-based audited changes" do
422+
patient.audits.create!(
423+
audited_changes: {
424+
given_name: %w[Alessia Alice],
425+
organisation_id: [nil, 1]
426+
}
427+
)
428+
events = timeline.load_events(["audits"])
429+
expect(events.first[:details][:audited_changes]).to have_key(
430+
:given_name
431+
)
432+
expect(events.first[:details][:audited_changes]).to have_key(
433+
:organisation_id
434+
)
435+
end
436+
end
437+
413438
context "with include_associated_audits: false" do
414439
let(:timeline) do
415440
described_class.new(

0 commit comments

Comments
 (0)