Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SystmOne exporter #2871

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
188 changes: 188 additions & 0 deletions app/lib/reports/systm_one_exporter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# frozen_string_literal: true

class Reports::SystmOneExporter
VACCINE_DOSE_MAPPINGS = {
"Gardasil 9" => {
"1" => "Y19a4",
"2" => "Y19a5",
"3" => "Y19a6"
}
}.freeze

DELIVERY_SITE_MAPPINGS = {
left_arm_upper_position: "Left deltoid",
left_arm_lower_position: "Left anterior forearm",
left_thigh: "Left lateral thigh",
right_arm_upper_position: "Right deltoid",
right_arm_lower_position: "Right anterior forearm",
right_thigh: "Right lateral thigh"
}.with_indifferent_access.freeze

def initialize(organisation:, programme:, start_date:, end_date:)
@organisation = organisation
@programme = programme
@start_date = start_date
@end_date = end_date
end

def call
CSV.generate(headers:, write_headers: true) do |csv|
vaccination_records.each do |vaccination_record|
csv << row(vaccination_record:)
end
end
end

def self.call(*args, **kwargs)
new(*args, **kwargs).call
end

private_class_method :new

private

attr_reader :organisation, :programme, :start_date, :end_date

def headers
[
"Practice code",
"NHS number",
"Surname",
"Middle name",
"Forename",
"Gender",
"Date of Birth",
"House name",
"House number and road",
"Town",
"Postcode",
"Vaccination",
"Part",
"Admin date",
"Batch number",
"Expiry date",
"Dose",
"Reason",
"Site",
"Method",
"Notes"
]
end

# TODO: Move this to a separate module to be included?
def vaccination_records
scope =
programme
.vaccination_records
.joins(:organisation)
.where(organisations: { id: organisation.id })
.merge(VaccinationRecord.administered)
.includes(
:batch,
:location,
:performed_by_user,
:programme,
:vaccine,
patient: %i[gp_practice school]
)

if start_date.present?
scope =
scope.where(
"vaccination_records.created_at >= ?",
start_date.beginning_of_day
).or(
scope.where(
"vaccination_records.updated_at >= ?",
start_date.beginning_of_day
)
)
end

if end_date.present?
scope =
scope.where(
"vaccination_records.created_at <= ?",
end_date.end_of_day
).or(
scope.where(
"vaccination_records.updated_at <= ?",
end_date.end_of_day
)
)
end

scope
end

def row(vaccination_record:)
patient = vaccination_record.patient

[
practice_code(vaccination_record), # Practice code
patient.nhs_number, # NHS number
patient.family_name, # Surname
"", # Middle name (not stored)
patient.given_name, # Forename
gender_code(patient.gender_code), # Gender
patient.date_of_birth.to_fs(:uk_short),
patient.address_line_2, # House name
patient.address_line_1, # House number and road
patient.address_town, # Town
patient.address_postcode, # Postcode
vaccination(vaccination_record), # Vaccination
"", # Part
vaccination_record.performed_at.to_date.to_fs(:uk_short), # Admin date
vaccination_record.batch&.name, # Batch number
vaccination_record.batch&.expiry&.to_fs(:uk_short), # Expiry date
vaccination_record.dose_volume_ml, # Dose
reason(vaccination_record), # Reason (not specified)
site(vaccination_record), # Site
vaccination_record.delivery_method, # Method
vaccination_record.notes # Notes
]
end

# TODO: Needs support for community and generic clinics.
def practice_code(vaccination_record)
location = vaccination_record.session.location

location.school? ? location.urn : location.ods_code
end

def gender_code(code)
{ male: "M", female: "F", not_specified: "U", not_known: "U" }[code.to_sym]
end

# TODO: These mappings are valid for Hertsforshire, but may not be correct for
# other SAIS teams. We'll need to check these are correct with new SAIS
# teams.
def vaccination(vaccination_record)
return if vaccination_record.not_administered?

VACCINE_DOSE_MAPPINGS.dig(
vaccination_record.vaccine.brand,
vaccination_record.dose_sequence.to_s
) ||
"#{vaccination_record.vaccine.brand} " \
"Part #{vaccination_record.dose_sequence}"
end

def reason(vaccination_record)
case vaccination_record.dose_sequence
when 1, nil
"Routine"
else
"At Risk"
end
end

# TODO: These mappings are valid for Hertsforshire, but may not be correct for
# other SAIS teams. We'll need to check these are correct with new SAIS
# teams.
def site(vaccination_record)
return if vaccination_record.not_administered?

DELIVERY_SITE_MAPPINGS.fetch(vaccination_record.delivery_site)
end
end
16 changes: 12 additions & 4 deletions app/models/vaccination_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ class VaccinationReport
include RequestSessionPersistable
include WizardStepConcern

FILE_FORMATS = %w[careplus mavis].freeze

def self.request_session_key
"vaccination_report"
end

def self.file_formats
%w[careplus mavis].tap do
it << "systm_one" if Flipper.enabled?(:systm_one_exporter)
end
end

attribute :date_from, :date
attribute :date_to, :date
attribute :file_format, :string
Expand All @@ -20,7 +24,10 @@ def wizard_steps
end

on_wizard_step :file_format, exact: true do
validates :file_format, inclusion: { in: FILE_FORMATS }
validates :file_format,
inclusion: {
in: -> { VaccinationReport.file_formats }
}
end

def programme
Expand Down Expand Up @@ -57,7 +64,8 @@ def csv_filename
def exporter_class
{
careplus: Reports::CareplusExporter,
mavis: Reports::ProgrammeVaccinationsExporter
mavis: Reports::ProgrammeVaccinationsExporter,
systm_one: Reports::SystmOneExporter
}.fetch(file_format.to_sym)
end

Expand Down
2 changes: 1 addition & 1 deletion app/views/vaccination_reports/file_format.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<%= form_with model: @vaccination_report, url: wizard_path, method: :put do |f| %>
<%= f.govuk_error_summary %>

<%= f.govuk_collection_radio_buttons :file_format, VaccinationReport::FILE_FORMATS, :itself, legend: { text: title, size: "l", tag: "h1" }, caption: { text: @programme.name } %>
<%= f.govuk_collection_radio_buttons :file_format, VaccinationReport.file_formats, :itself, legend: { text: title, size: "l", tag: "h1" }, caption: { text: @programme.name } %>

<%= f.govuk_submit %>
<% end %>
1 change: 1 addition & 0 deletions config/locales/helpers.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ en:
file_format_options:
careplus: CarePlus
mavis: CSV
systm_one: SystmOne
2 changes: 1 addition & 1 deletion db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
Faker::Config.locale = "en-GB"

def set_feature_flags
%i[dev_tools mesh_jobs cis2].each do |feature_flag|
%i[dev_tools mesh_jobs cis2 systm_one_export].each do |feature_flag|
Flipper.add(feature_flag) unless Flipper.exist?(feature_flag)
end
end
Expand Down
51 changes: 51 additions & 0 deletions spec/features/download_vaccination_reports_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,36 @@
then_i_download_a_mavis_file
end

scenario "Download in SystmOne format" do
given_an_hpv_programme_is_underway
and_an_administered_vaccination_record_exists
and_systm_one_export_is_enabled

when_i_go_to_the_programme
and_i_click_on_download_vaccination_report
then_i_see_the_dates_page

when_i_enter_some_dates
then_i_see_the_file_format_page

when_i_choose_systm_one
then_i_download_a_systm_one_file
end

scenario "SystmOne disabled" do
given_an_hpv_programme_is_underway
and_an_administered_vaccination_record_exists
# and_systm_one_export_is_enabled

when_i_go_to_the_programme
and_i_click_on_download_vaccination_report
then_i_see_the_dates_page

when_i_enter_some_dates
then_i_see_the_file_format_page
and_systm_one_export_is_disabled
end

def given_an_hpv_programme_is_underway
@organisation = create(:organisation, :with_one_nurse)
@programme = create(:programme, :hpv, organisations: [@organisation])
Expand Down Expand Up @@ -66,6 +96,10 @@ def and_an_administered_vaccination_record_exists
)
end

def and_systm_one_export_is_enabled
Flipper.enable(:systm_one_exporter)
end

def when_i_go_to_the_programme
sign_in @organisation.users.first
visit programme_path(@programme)
Expand Down Expand Up @@ -109,6 +143,11 @@ def when_i_choose_mavis
click_on "Continue"
end

def when_i_choose_systm_one
choose "SystmOne"
click_on "Continue"
end

def then_i_download_a_careplus_file
expect(page.status_code).to eq(200)

Expand All @@ -124,4 +163,16 @@ def then_i_download_a_mavis_file
"ORGANISATION_CODE,SCHOOL_URN,SCHOOL_NAME,CARE_SETTING,CLINIC_NAME,PERSON_FORENAME,PERSON_SURNAME"
)
end

def then_i_download_a_systm_one_file
expect(page.status_code).to eq(200)

expect(page).to have_content(
"Practice code,NHS number,Surname,Middle name,Forename,Gender,Date of Birth,House name,House number and road,Town"
)
end

def and_systm_one_export_is_disabled
expect(page).not_to have_selector("SystmOne")
end
end
Loading
Loading