Skip to content
Open
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
5 changes: 2 additions & 3 deletions app/lib/mavis_cli/reports/send_to_careplus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,8 @@ def resolve_credentials(ods_code:, workgroup:)

team = teams.sole

unless team.careplus_username.present? &&
team.careplus_password.present?
warn "Team '#{team.name}' does not have CarePlus credentials configured."
unless team.has_careplus_credentials?
warn "Team '#{team.name}' needs the CarePlus username, password, and namespace configured to send reports."
return nil, nil, nil
end

Expand Down
46 changes: 38 additions & 8 deletions app/lib/reports/automated_careplus_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,44 @@ def self.vaccination_records_scope(
start_date:,
end_date:
)
Reports::CareplusExporter.vaccination_records_scope(
team:,
programmes: team.programmes,
academic_year:,
start_date:,
end_date:,
include_missing_nhs_number: false
)
base_scope =
Reports::CareplusExporter.vaccination_records_scope(
team:,
programmes: team.programmes,
academic_year:,
start_date: nil,
end_date: nil,
include_missing_nhs_number: false
)
date_range_scope =
base_scope.created_or_updated_between(start_date, end_date)

return date_range_scope if team.careplus_automated_reports_enabled_at.blank?

nhs_number_first_added_scope =
base_scope
.created_or_updated_on_or_after(
team.careplus_automated_reports_enabled_at
)
.where.not(patients: { nhs_number_first_added_at: nil })

if start_date.present?
nhs_number_first_added_scope =
nhs_number_first_added_scope.where(
"patients.nhs_number_first_added_at >= ?",
start_date.beginning_of_day
)
end

if end_date.present?
nhs_number_first_added_scope =
nhs_number_first_added_scope.where(
"patients.nhs_number_first_added_at <= ?",
end_date.end_of_day
)
end

date_range_scope.or(nhs_number_first_added_scope).distinct
end

def self.shared_args(team:, academic_year:)
Expand Down
4 changes: 3 additions & 1 deletion app/lib/reports/careplus_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,13 @@ def gender_row_value(patient)
end

def consents
patient_ids = vaccination_records.unscope(:order).reselect(:patient_id)

@consents ||=
Consent
.select("DISTINCT ON (patient_id) consents.*")
.for_programmes(programmes)
.where(patient: vaccination_records.select(:patient_id), academic_year:)
.where(patient: patient_ids, academic_year:)
.not_invalidated
.response_given
.order(:patient_id, created_at: :desc)
Expand Down
1 change: 1 addition & 0 deletions app/models/concerns/patient_import_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def import_patients_and_parents(changesets, import)
.uniq { [_1.parent, _1.patient] }

deduplicate_patients!(patients, relationships)
patients.each(&:ensure_nhs_number_first_added_at)

patients_with_nhs_number_changes =
patients.select(&:nhs_number_previously_changed?)
Expand Down
23 changes: 23 additions & 0 deletions app/models/patient.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
# invalidated_at :datetime
# local_authority_mhclg_code :string
# nhs_number :string
# nhs_number_first_added_at :datetime
# pending_changes :jsonb not null
# preferred_family_name :string
# preferred_given_name :string
Expand All @@ -47,6 +48,7 @@
# index_patients_on_names_family_first (family_name,given_name)
# index_patients_on_names_given_first (given_name,family_name)
# index_patients_on_nhs_number (nhs_number) UNIQUE
# index_patients_on_nhs_number_first_added_at (nhs_number_first_added_at)
# index_patients_on_pending_changes_not_empty (id) WHERE (pending_changes <> '{}'::jsonb)
# index_patients_on_school_id (school_id)
#
Expand Down Expand Up @@ -495,6 +497,7 @@ class Patient < ApplicationRecord
it.blank? ? nil : it.normalise_whitespace.gsub(/\s/, "")
end

before_validation :ensure_nhs_number_first_added_at
after_update :sync_vaccinations_to_nhs_immunisations_api
after_commit :generate_important_notice_if_needed, on: :update
after_commit :search_vaccinations_from_nhs_immunisations_api, on: :update
Expand Down Expand Up @@ -810,6 +813,15 @@ def pds_lookup_match?

def notifier = Notifier::Patient.new(self)

def ensure_nhs_number_first_added_at
return unless will_save_change_to_nhs_number?

old_nhs_number, new_nhs_number = nhs_number_change_to_be_saved
return unless old_nhs_number.blank? && new_nhs_number.present?

self.nhs_number_first_added_at ||= Time.current
end

private

def locations_are_correct_type
Expand All @@ -822,6 +834,17 @@ def locations_are_correct_type
end
end

def destroy_childless_parents
parents_to_check = parents.to_a # Store parents before destroying relationships

# Manually destroy the parent_relationships associated with this Child
parent_relationships.each(&:destroy)

parents_to_check.each do |parent|
parent.destroy! if parent.parent_relationships.count.zero?
end
end

def archive_due_to_deceased!
archive_reasons =
teams.map do |team|
Expand Down
5 changes: 4 additions & 1 deletion app/models/patient_changeset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,10 @@ def set_child_attribute_if_valid(
end

if in_pending_changes && !in_existing_patient
existing_patient[attribute] = child_attributes[attribute.to_s]
existing_patient.public_send(
"#{attribute}=",
child_attributes[attribute.to_s]
)
Comment on lines +386 to +389
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this change necessary because the callbacks don't execute without it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, the callbacks didn't execute otherwise

end
end

Expand Down
62 changes: 36 additions & 26 deletions app/models/team.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,30 @@
#
# Table name: teams
#
# id :bigint not null, primary key
# careplus_namespace :string
# careplus_password :string
# careplus_staff_code :string
# careplus_staff_type :string
# careplus_username :string
# careplus_venue_code :string
# days_before_consent_reminders :integer default(7), not null
# days_before_consent_requests :integer default(21), not null
# email :string
# name :text not null
# national_reporting_cut_off_date :date
# phone :string
# phone_instructions :string
# privacy_notice_url :string
# privacy_policy_url :string
# programme_types :enum not null, is an Array
# type :integer not null
# workgroup :string not null
# created_at :datetime not null
# updated_at :datetime not null
# organisation_id :bigint not null
# reply_to_id :uuid
# id :bigint not null, primary key
# careplus_automated_reports_enabled_at :datetime
# careplus_namespace :string
# careplus_password :string
# careplus_staff_code :string
# careplus_staff_type :string
# careplus_username :string
# careplus_venue_code :string
# days_before_consent_reminders :integer default(7), not null
# days_before_consent_requests :integer default(21), not null
# email :string
# name :text not null
# national_reporting_cut_off_date :date
# phone :string
# phone_instructions :string
# privacy_notice_url :string
# privacy_policy_url :string
# programme_types :enum not null, is an Array
# type :integer not null
# workgroup :string not null
# created_at :datetime not null
# updated_at :datetime not null
# organisation_id :bigint not null
# reply_to_id :uuid
#
# Indexes
#
Expand Down Expand Up @@ -99,8 +100,12 @@ class Team < ApplicationRecord
.where.not(careplus_username: nil)
.where.not(careplus_password: nil)
end
scope :careplus_automated_reports_enabled,
-> { where.not(careplus_automated_reports_enabled_at: nil) }
scope :eligible_for_automated_careplus_reports,
-> { careplus_enabled.has_careplus_credentials }
-> do
careplus_enabled.has_careplus_credentials.careplus_automated_reports_enabled
end

enum :type,
{ point_of_care: 0, national_reporting: 1, support: 2 },
Expand Down Expand Up @@ -172,8 +177,13 @@ def careplus_enabled?
careplus_venue_code.present?
end

def has_careplus_credentials?
careplus_username.present? && careplus_password.present? &&
careplus_namespace.present?
end

def eligible_for_automated_careplus_reports?
careplus_enabled? && careplus_username.present? &&
careplus_password.present? && careplus_namespace.present?
careplus_enabled? && has_careplus_credentials? &&
careplus_automated_reports_enabled_at.present?
end
end
7 changes: 7 additions & 0 deletions app/models/vaccination_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ class VaccinationRecord < ApplicationRecord
scope
end

scope :created_or_updated_on_or_after,
->(timestamp) do
where("vaccination_records.created_at >= ?", timestamp).or(
where("vaccination_records.updated_at >= ?", timestamp)
)
end

enum :protocol, { pgd: 0, psd: 1, national: 2 }, validate: { allow_nil: true }

enum :delivery_method,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

class BackfillNHSNumberFirstAddedAtForPatients < ActiveRecord::Migration[8.1]
disable_ddl_transaction!

BATCH_SIZE = 1000

def up
migration = self.class.name
started_at = Time.zone.now
scope = Patient.where(nhs_number_first_added_at: nil).where.not(nhs_number: nil)
total_records = scope.count
total_batches = (total_records.to_f / BATCH_SIZE).ceil
records_updated = 0

Rails.logger.info(
event: "data_migration_start",
migration:,
total_records:,
batch_size: BATCH_SIZE,
total_batches:
)

scope.in_batches(of: BATCH_SIZE).each_with_index do |batch, index|
updated_count = batch.update_all("nhs_number_first_added_at = created_at")
records_updated += updated_count

Rails.logger.info(
event: "data_migration_batch",
migration:,
batch_number: index + 1,
total_batches:,
updated_count:,
records_updated:
)
end

duration_minutes = ((Time.zone.now - started_at) / 60.0).round

Rails.logger.info(
event: "data_migration_finish",
migration:,
duration_minutes:,
records_updated:
)
end

def down
raise ActiveRecord::IrreversibleMigration
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddNHSNumberFirstAddedAtForPatient < ActiveRecord::Migration[8.1]
def change
add_column :patients, :nhs_number_first_added_at, :datetime
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

class AddIndexOnPatientsNHSNumberFirstAddedAt < ActiveRecord::Migration[8.1]
disable_ddl_transaction!

def change
add_index :patients, :nhs_number_first_added_at, algorithm: :concurrently
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddCareplusAutomatedReportsEnabledAtForTeam < ActiveRecord::Migration[8.1]
def change
add_column :teams, :careplus_automated_reports_enabled_at, :datetime
end
end
6 changes: 3 additions & 3 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@
t.bigint "class_import_id", null: false
t.bigint "parent_relationship_id", null: false
t.index ["class_import_id", "parent_relationship_id"], name: "idx_on_class_import_id_parent_relationship_id_8225058195", unique: true
t.index ["parent_relationship_id", "class_import_id"], name: "idx_on_parent_relationship_id_class_import_id_d7c05d6c2c", unique: true
end

create_table "class_imports_parents", id: false, force: :cascade do |t|
Expand Down Expand Up @@ -221,7 +220,6 @@
t.bigint "cohort_import_id", null: false
t.bigint "parent_relationship_id", null: false
t.index ["cohort_import_id", "parent_relationship_id"], name: "idx_on_cohort_import_id_parent_relationship_id_c65e20d1f8", unique: true
t.index ["parent_relationship_id", "cohort_import_id"], name: "idx_on_parent_relationship_id_cohort_import_id_40fb9846d6", unique: true
end

create_table "cohort_imports_parents", id: false, force: :cascade do |t|
Expand Down Expand Up @@ -460,7 +458,6 @@
t.bigint "immunisation_import_id", null: false
t.bigint "vaccination_record_id", null: false
t.index ["immunisation_import_id", "vaccination_record_id"], name: "idx_on_immunisation_import_id_vaccination_record_id_588e859772", unique: true
t.index ["vaccination_record_id", "immunisation_import_id"], name: "idx_on_vaccination_record_id_immunisation_import_id_813c516ad7", unique: true
end

create_table "important_notices", force: :cascade do |t|
Expand Down Expand Up @@ -763,6 +760,7 @@
t.datetime "invalidated_at"
t.string "local_authority_mhclg_code"
t.string "nhs_number"
t.datetime "nhs_number_first_added_at"
t.jsonb "pending_changes", default: {}, null: false
t.string "preferred_family_name"
t.string "preferred_given_name"
Expand All @@ -784,6 +782,7 @@
t.index ["id"], name: "index_patients_on_pending_changes_not_empty", where: "(pending_changes <> '{}'::jsonb)"
t.index ["local_authority_mhclg_code"], name: "index_patients_on_local_authority_mhclg_code"
t.index ["nhs_number"], name: "index_patients_on_nhs_number", unique: true
t.index ["nhs_number_first_added_at"], name: "index_patients_on_nhs_number_first_added_at"
t.index ["school_id"], name: "index_patients_on_school_id"
end

Expand Down Expand Up @@ -912,6 +911,7 @@
end

create_table "teams", force: :cascade do |t|
t.datetime "careplus_automated_reports_enabled_at"
t.string "careplus_namespace"
t.string "careplus_password"
t.string "careplus_staff_code"
Expand Down
Loading
Loading