Skip to content
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
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ gem "jwt"
gem "mechanize"
gem "notifications-ruby-client"
gem "okcomputer"
gem "omniauth_openid_connect"
gem "omniauth-rails_csrf_protection"
gem "omniauth_openid_connect"
gem "pagy"
gem "phonelib"
gem "pundit"
gem "rails_semantic_logger"
gem "rainbow"
gem "redis"
gem "ruby-progressbar"
gem "rubyzip"
gem "sentry-rails"
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ GEM
erb
psych (>= 4.0.0)
redcarpet (3.6.1)
redis (5.4.1)
redis-client (>= 0.22.0)
redis-client (0.25.2)
connection_pool
redis-prescription (2.6.0)
Expand Down Expand Up @@ -823,6 +825,7 @@ DEPENDENCIES
rails (~> 8.0.2)
rails_semantic_logger
rainbow
redis
rladr
rspec
rspec-html-matchers
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/api/testing/teams_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ def destroy
VaccinationRecord.where(performed_ods_code: team.organisation.ods_code)
)

TeamCachedCounts.new(team).reset_all!

unless keep_itself
log_destroy(SessionProgramme.where(session: sessions))
log_destroy(sessions)
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/consent_forms_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def edit_match
def update_match
@consent_form.match_with_patient!(@patient, current_user:)

reset_count!

session =
@patient
.sessions
Expand Down Expand Up @@ -109,6 +111,8 @@ def create_patient
school_move.confirm!

@consent_form.match_with_patient!(patient, current_user:)

reset_count!
end

if patient.nhs_number.nil?
Expand Down Expand Up @@ -144,4 +148,8 @@ def set_patient
def archive_params
params.expect(consent_form: :notes).merge(archived_at: Time.current)
end

def reset_count!
TeamCachedCounts.new(current_team).reset_unmatched_consent_responses!
end
end
3 changes: 2 additions & 1 deletion app/controllers/imports/issues_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def set_patient
def set_form
apply_changes = params.dig(:import_duplicate_form, :apply_changes)

@form = ImportDuplicateForm.new(object: @record, apply_changes:)
@form =
ImportDuplicateForm.new(current_team:, object: @record, apply_changes:)
end

def set_type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def record

session.delete(:consent_form_id)

TeamCachedCounts.new(@team).reset_unmatched_consent_responses!

send_consent_form_confirmation(@consent_form)

ConsentFormMatchingJob.perform_later(@consent_form)
Expand Down
8 changes: 7 additions & 1 deletion app/forms/import_duplicate_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
class ImportDuplicateForm
include ActiveModel::Model

attr_accessor :object, :apply_changes
attr_accessor :current_team, :object, :apply_changes

validates :apply_changes, inclusion: { in: :apply_changes_options }

Expand All @@ -21,6 +21,8 @@ def save
end
end

reset_count!

true
rescue ActiveRecord::RecordInvalid
errors.add(:base, "Failed to save changes")
Expand Down Expand Up @@ -51,4 +53,8 @@ def discard_pending_changes!
def keep_both_changes!
object.apply_pending_changes_to_new_record! if can_keep_both?
end

def reset_count!
TeamCachedCounts.new(current_team).reset_import_issues!
end
end
6 changes: 6 additions & 0 deletions app/forms/school_move_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ def save
@school_move.ignore!
end

TeamCachedCounts.new(team).reset_school_moves!

true
end

private

def team = current_user.selected_team
end
8 changes: 8 additions & 0 deletions app/jobs/commit_patient_changesets_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def perform(import)

import.postprocess_rows!

reset_counts(import)

import.update_columns(
processed_at: Time.zone.now,
status: :processed,
Expand Down Expand Up @@ -186,4 +188,10 @@ def has_auto_confirmable_school_move?(school_move, import)
academic_year: import.academic_year
) || school_move.patient.archived?(team: import.team)
end

def reset_counts(import)
cached_counts = TeamCachedCounts.new(import.team)
cached_counts.reset_import_issues!
cached_counts.reset_school_moves!
end
end
7 changes: 7 additions & 0 deletions app/jobs/consent_form_matching_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def match_with_exact_nhs_number
patient.update_from_pds!(pds_patient)
send_parental_contact_warning_if_needed(patient, @consent_form)
@consent_form.match_with_patient!(patient, current_user: nil)
reset_counts
true
end

def session_patients
Expand Down Expand Up @@ -99,5 +101,10 @@ def match_patient(patient)

send_parental_contact_warning_if_needed(patient, @consent_form)
@consent_form.match_with_patient!(patient, current_user: nil)
reset_counts
end

def reset_counts
TeamCachedCounts.new(@consent_form.team).reset_unmatched_consent_responses!
end
end
80 changes: 59 additions & 21 deletions app/lib/team_cached_counts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,81 @@ def initialize(team)
def import_issues
return nil if current_user.nil?

vaccination_records_with_issues =
VaccinationRecordPolicy::Scope
.new(current_user, VaccinationRecord)
.resolve
.with_pending_changes
.pluck(:patient_id)
Rails
.cache
.fetch(import_issues_key) do
vaccination_records_with_issues =
VaccinationRecordPolicy::Scope
.new(current_user, VaccinationRecord)
.resolve
.with_pending_changes
.pluck(:patient_id)

patients_with_issues =
PatientPolicy::Scope
.new(current_user, Patient)
.resolve
.with_pending_changes
.pluck(:id)
patients_with_issues =
PatientPolicy::Scope
.new(current_user, Patient)
.resolve
.with_pending_changes
.pluck(:id)

(vaccination_records_with_issues + patients_with_issues).uniq.length
(vaccination_records_with_issues + patients_with_issues).uniq.length
end
end

def reset_import_issues!
Rails.cache.delete(import_issues_key)
end

def school_moves
return nil if current_user.nil?

SchoolMovePolicy::Scope.new(current_user, SchoolMove).resolve.count
Rails
.cache
.fetch(school_moves_key) do
SchoolMovePolicy::Scope.new(current_user, SchoolMove).resolve.count
end
end

def reset_school_moves!
Rails.cache.delete(school_moves_key)
end

def unmatched_consent_responses
return nil if current_user.nil?

ConsentFormPolicy::Scope
.new(current_user, ConsentForm)
.resolve
.unmatched
.recorded
.not_archived
.count
Rails
.cache
.fetch(unmatched_consent_responses_key) do
ConsentFormPolicy::Scope
.new(current_user, ConsentForm)
.resolve
.unmatched
.recorded
.not_archived
.count
end
end

def reset_unmatched_consent_responses!
Rails.cache.delete(unmatched_consent_responses_key)
end

def reset_all!
reset_import_issues!
reset_school_moves!
reset_unmatched_consent_responses!
end

private

attr_reader :team

def import_issues_key = cache_key("import-issues")

def school_moves_key = cache_key("school-moves")

def unmatched_consent_responses_key = cache_key("unmatched-consent-responses")

def current_user
# We can't use the policy_scope helper here as we're not in a controller.
# Instead, we can mock what a `User` looks like from the perspective of a
Expand All @@ -56,4 +92,6 @@ def current_user
OpenStruct.new(selected_team: team, selected_organisation: organisation)
end
end

def cache_key(type) = "cached-counts/#{type}/#{team.id}"
end
16 changes: 15 additions & 1 deletion config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,21 @@
)

# Replace the default in-process memory cache store with a durable alternative.
# config.cache_store = :mem_cache_store
config.cache_store =
:redis_cache_store,
{
url: ENV["REDIS_CACHE_URL"],
error_handler: ->(method:, returning:, exception:) do
Sentry.capture_exception(
exception,
level: "warning",
tags: {
method:,
returning:
}
)
end
}

# Replace the default in-process and non-durable queuing backend for Active Job.
# config.active_job.queue_adapter = :resque
Expand Down