diff --git a/app/components/app_activity_log_component.rb b/app/components/app_activity_log_component.rb index 247e6cf816..e0a914c2c0 100644 --- a/app/components/app_activity_log_component.rb +++ b/app/components/app_activity_log_component.rb @@ -63,12 +63,13 @@ def initialize(team:, patient:, session: nil) session ? scope.where(programme_ids: session.programmes.ids) : scope end - @patient_sessions = + @patient_locations = @patient - .patient_sessions - .includes_programmes - .includes(session: :location) - .then { |scope| session ? scope.where(session:) : scope } + .patient_locations + .includes(:location) + .then do |scope| + session ? scope.where(location: session.location) : scope + end @patient_specific_directions = @patient @@ -105,7 +106,7 @@ def initialize(team:, patient:, session: nil) :notes, :notify_log_entries, :patient, - :patient_sessions, + :patient_locations, :patient_specific_directions, :pre_screenings, :attendance_records, @@ -346,15 +347,11 @@ def pre_screening_events end def session_events - patient_sessions.map do |patient_session| - patient = patient_session.patient - session = patient_session.session - + patient_locations.map do |patient_location| [ { - title: "Added to the session at #{session.location.name}", - at: patient_session.created_at, - programmes: session.programmes_for(patient:) + title: "Added to the session at #{patient_location.location.name}", + at: patient_location.created_at } ] end diff --git a/app/components/app_patient_pds_discrepancy_table_component.rb b/app/components/app_patient_pds_discrepancy_table_component.rb index d8d9738d8e..247fb2d9a5 100644 --- a/app/components/app_patient_pds_discrepancy_table_component.rb +++ b/app/components/app_patient_pds_discrepancy_table_component.rb @@ -12,9 +12,7 @@ def initialize(discrepancies:, current_user:) delegate :format_nhs_number, :govuk_table, to: :helpers - def can_link_to?(record) - allowed_ids.include?(record.id) - end + def can_link_to?(record) = allowed_ids.include?(record.id) def allowed_ids @allowed_ids ||= PatientPolicy::Scope.new(current_user, Patient).resolve.ids diff --git a/app/components/app_patient_programmes_table_component.rb b/app/components/app_patient_programmes_table_component.rb index 7464847b37..e2e8ccf29d 100644 --- a/app/components/app_patient_programmes_table_component.rb +++ b/app/components/app_patient_programmes_table_component.rb @@ -258,7 +258,7 @@ def vaccination_records_for(programme:, academic_year: nil) end def eligible_year_groups_for(programme:) - location_ids = patient.patient_sessions.joins(:session).select(:location_id) + location_ids = patient.patient_locations.select(:location_id) LocationProgrammeYearGroup .where(location_id: location_ids) diff --git a/app/components/app_programme_session_table_component.rb b/app/components/app_programme_session_table_component.rb index dd6c343b6d..e7e181fc4c 100644 --- a/app/components/app_programme_session_table_component.rb +++ b/app/components/app_programme_session_table_component.rb @@ -1,23 +1,28 @@ # frozen_string_literal: true class AppProgrammeSessionTableComponent < ViewComponent::Base - def initialize(sessions, programme:) + def initialize(sessions, programme:, academic_year:) @sessions = sessions @programme = programme + @academic_year = academic_year end private - attr_reader :sessions, :programme + attr_reader :sessions, :programme, :academic_year delegate :govuk_table, to: :helpers def cohort_count(session:) - format_number(patient_sessions(session:).count) + format_number(patients(session:).count) end def no_response_scope(session:) - patient_sessions(session:).has_consent_status(:no_response, programme:) + patients(session:).has_consent_status( + :no_response, + programme:, + academic_year: + ) end def no_response_count(session:) @@ -27,13 +32,17 @@ def no_response_count(session:) def no_response_percentage(session:) format_percentage( no_response_scope(session:).count, - patient_sessions(session:).count + patients(session:).count ) end def triage_needed_count(session:) format_number( - patient_sessions(session:).has_triage_status(:required, programme:).count + patients(session:).has_triage_status( + :required, + programme:, + academic_year: + ).count ) end @@ -48,15 +57,22 @@ def vaccinated_count(session:) def vaccinated_percentage(session:) format_percentage( vaccinated_scope(session:).count, - patient_sessions(session:).count + patients(session:).count + ) + end + + def patients(session:) + @patients ||= {} + @patients[session] = session.patients.where( + birth_academic_year: birth_academic_years(session:) ) end - def patient_sessions(session:) - session - .patient_sessions - .joins(:patient, :session) - .appear_in_programmes([programme]) + def birth_academic_years(session:) + @birth_academic_years ||= {} + @birth_academic_years[session] = session.programme_birth_academic_years[ + programme + ] end def format_number(count) = count.to_s diff --git a/app/components/app_session_actions_component.rb b/app/components/app_session_actions_component.rb index 98f1d10fc7..59f8cb729f 100644 --- a/app/components/app_session_actions_component.rb +++ b/app/components/app_session_actions_component.rb @@ -19,12 +19,7 @@ def render? = rows.any? delegate :govuk_summary_list, to: :helpers delegate :academic_year, :programmes, to: :session - def patient_sessions - session - .patient_sessions - .joins(:patient, :session) - .appear_in_programmes(programmes) - end + def patients = session.patients def rows @rows ||= [ @@ -38,7 +33,7 @@ def rows end def no_nhs_number_row - count = patient_sessions.merge(Patient.without_nhs_number).count + count = patients.without_nhs_number.count href = session_patients_path(session, missing_nhs_number: true) generate_row(:children_without_nhs_number, count:, href:) @@ -47,7 +42,11 @@ def no_nhs_number_row def no_consent_response_row status = "no_response" count = - patient_sessions.has_consent_status(status, programme: programmes).count + patients.has_consent_status( + status, + programme: programmes, + academic_year: + ).count href = session_consent_path(session, consent_statuses: [status]) actions = [ { @@ -61,7 +60,11 @@ def no_consent_response_row def conflicting_consent_row status = "conflicts" count = - patient_sessions.has_consent_status(status, programme: programmes).count + patients.has_consent_status( + status, + programme: programmes, + academic_year: + ).count href = session_consent_path(session, consent_statuses: [status]) generate_row(:children_with_conflicting_consent_response, count:, href:) @@ -70,7 +73,11 @@ def conflicting_consent_row def triage_required_row status = "required" count = - patient_sessions.has_triage_status(status, programme: programmes).count + patients.has_triage_status( + status, + programme: programmes, + academic_year: + ).count href = session_triage_path(session, triage_status: status) generate_row(:children_requiring_triage, count:, href:) @@ -80,7 +87,7 @@ def register_attendance_row return nil unless session.requires_registration? && session.today? status = "unknown" - count = patient_sessions.has_registration_status(status).count + count = patients.has_registration_status(status, session:).count href = session_register_path(session, register_status: status) generate_row(:children_to_register, count:, href:) @@ -91,13 +98,11 @@ def ready_for_vaccinator_row counts_by_programme = session.programmes.index_with do |programme| - patient_sessions - .has_registration_status(%w[attending completed]) - .includes( - patient: %i[consent_statuses triage_statuses vaccination_statuses] - ) - .count do |patient_session| - patient_session.patient.consent_given_and_safe_to_vaccinate?( + patients + .has_registration_status(%w[attending completed], session:) + .includes(:consent_statuses, :triage_statuses, :vaccination_statuses) + .count do |patient| + patient.consent_given_and_safe_to_vaccinate?( programme:, academic_year: ) diff --git a/app/components/app_session_details_summary_component.rb b/app/components/app_session_details_summary_component.rb index 5abb39c2ad..e9d3aadef3 100644 --- a/app/components/app_session_details_summary_component.rb +++ b/app/components/app_session_details_summary_component.rb @@ -14,17 +14,12 @@ def call attr_reader :session delegate :govuk_summary_list, to: :helpers - delegate :programmes, to: :session + delegate :programmes, :academic_year, to: :session - def patient_sessions - session - .patient_sessions - .joins(:patient, :session) - .appear_in_programmes(programmes) - end + def patients = session.patients def cohort_row - count = patient_sessions.count + count = patients.count { key: { text: "Cohort" }, value: { text: I18n.t("children", count:) } } end @@ -33,7 +28,11 @@ def consent_refused_row status = "refused" count = - patient_sessions.has_consent_status(status, programme: programmes).count + patients.has_consent_status( + status, + programme: programmes, + academic_year: + ).count href = session_consent_path(session, consent_statuses: [status]) diff --git a/app/components/app_session_needs_review_warning_component.rb b/app/components/app_session_needs_review_warning_component.rb index 14c4ee1ac1..3f902ea1da 100644 --- a/app/components/app_session_needs_review_warning_component.rb +++ b/app/components/app_session_needs_review_warning_component.rb @@ -32,8 +32,7 @@ def warning_href def warning_counts @warning_counts ||= { - children_without_nhs_number: - patient_sessions.merge(Patient.without_nhs_number).count + children_without_nhs_number: patients.without_nhs_number.count } end @@ -42,10 +41,5 @@ def make_row_from_warning(warning) link_to(t(warning, count: warning_counts[warning]), warning_href[warning]) end - def patient_sessions - @session - .patient_sessions - .joins(:patient, :session) - .appear_in_programmes(@session.programmes) - end + def patients = @session.patients end diff --git a/app/controllers/api/testing/teams_controller.rb b/app/controllers/api/testing/teams_controller.rb index 98ab0b3eab..3041075d73 100644 --- a/app/controllers/api/testing/teams_controller.rb +++ b/app/controllers/api/testing/teams_controller.rb @@ -28,7 +28,9 @@ def destroy patient_ids = team.patients.pluck(:id) consent_form_ids = team.consent_forms.pluck(:id) - log_destroy(PatientSession.where(session: sessions)) + log_destroy( + PatientLocation.where(location_id: sessions.select(:location_id)) + ) log_destroy(AccessLogEntry.where(patient_id: patient_ids)) log_destroy(ArchiveReason.where(patient_id: patient_ids)) @@ -41,7 +43,7 @@ def destroy log_destroy(NotifyLogEntry.where(patient_id: patient_ids)) log_destroy(NotifyLogEntry.where(consent_form_id: consent_form_ids)) log_destroy(PatientChangeset.where(patient_id: patient_ids)) - log_destroy(PatientSession.where(patient_id: patient_ids)) + log_destroy(PatientLocation.where(patient_id: patient_ids)) log_destroy(PatientSpecificDirection.where(patient_id: patient_ids)) log_destroy(PDSSearchResult.where(patient_id: patient_ids)) log_destroy(PreScreening.where(patient_id: patient_ids)) diff --git a/app/controllers/consent_forms_controller.rb b/app/controllers/consent_forms_controller.rb index 85dbb28b3f..6f50912688 100644 --- a/app/controllers/consent_forms_controller.rb +++ b/app/controllers/consent_forms_controller.rb @@ -37,10 +37,11 @@ def update_match session = @patient - .pending_sessions + .sessions .includes(:location_programme_year_groups, :programmes) .has_programmes(@consent_form.programmes) - .first || @consent_form.original_session + .find_by(academic_year: AcademicYear.pending) || + @consent_form.original_session programme = session.programmes_for(patient: @patient).first @@ -136,7 +137,6 @@ def set_patient @patient = policy_scope(Patient).includes( parent_relationships: :parent, - pending_sessions: :programmes, vaccination_records: :programme ).find(params[:patient_id]) end diff --git a/app/controllers/imports/issues_controller.rb b/app/controllers/imports/issues_controller.rb index c768c964da..684ba85de6 100644 --- a/app/controllers/imports/issues_controller.rb +++ b/app/controllers/imports/issues_controller.rb @@ -40,7 +40,6 @@ def set_import_issues @patients = policy_scope(Patient).with_pending_changes.includes( :gp_practice, - :pending_sessions, :school, :school_moves ) diff --git a/app/controllers/patient_sessions/base_controller.rb b/app/controllers/patient_sessions/base_controller.rb index dde01ee584..35d4e04adb 100644 --- a/app/controllers/patient_sessions/base_controller.rb +++ b/app/controllers/patient_sessions/base_controller.rb @@ -4,7 +4,7 @@ class PatientSessions::BaseController < ApplicationController before_action :set_session before_action :set_academic_year before_action :set_patient - before_action :set_patient_session + before_action :set_patient_location before_action :set_programme before_action :set_breadcrumb_item @@ -34,9 +34,13 @@ def set_patient ) end - def set_patient_session - @patient_session = - PatientSession.find_by!(patient: @patient, session: @session) + def set_patient_location + @patient_location = + PatientLocation.find_by!( + patient: @patient, + location: @session.location, + academic_year: @session.academic_year + ) end def set_programme diff --git a/app/controllers/patients_controller.rb b/app/controllers/patients_controller.rb index b30d4465e0..41ace8abdd 100644 --- a/app/controllers/patients_controller.rb +++ b/app/controllers/patients_controller.rb @@ -26,10 +26,11 @@ def edit end def invite_to_clinic - session = - current_team.generic_clinic_session(academic_year: AcademicYear.pending) - - PatientSession.find_or_create_by!(patient: @patient, session:) + PatientLocation.find_or_create_by!( + patient: @patient, + location: current_team.generic_clinic, + academic_year: AcademicYear.pending + ) redirect_to patient_path(@patient), flash: { @@ -46,15 +47,16 @@ def set_patient :school, consents: %i[parent patient], parent_relationships: :parent, - sessions: :location, vaccination_records: :programme ).find(params[:id]) end def set_in_generic_clinic - generic_clinic_session = - current_team.generic_clinic_session(academic_year: AcademicYear.pending) - @in_generic_clinic = @patient.sessions.include?(generic_clinic_session) + @in_generic_clinic = + @patient.patient_locations.exists?( + location: current_team.generic_clinic, + academic_year: AcademicYear.pending + ) end def record_access_log_entry diff --git a/app/controllers/programmes/base_controller.rb b/app/controllers/programmes/base_controller.rb index 2b88881161..6f499e7fc8 100644 --- a/app/controllers/programmes/base_controller.rb +++ b/app/controllers/programmes/base_controller.rb @@ -22,21 +22,11 @@ def set_academic_year def patient_ids @patient_ids ||= - PatientSession - .distinct - .joins(:patient, :session) - .where(session_id: session_ids) - .appear_in_programmes([@programme]) - .not_archived(team: current_team) - .pluck(:patient_id) - end - - def session_ids - @session_ids ||= current_team - .sessions - .where(academic_year: @academic_year) - .has_programmes([@programme]) + .patients + .appear_in_programmes([@programme], academic_year: @academic_year) + .not_archived(team: current_team) .pluck(:id) + .uniq end end diff --git a/app/controllers/sessions/consent_controller.rb b/app/controllers/sessions/consent_controller.rb index 10bf452bb1..8e2f419622 100644 --- a/app/controllers/sessions/consent_controller.rb +++ b/app/controllers/sessions/consent_controller.rb @@ -27,16 +27,16 @@ def show scope = @session - .patient_sessions - .includes_programmes - .includes(patient: [:consent_statuses, { notes: :created_by }]) + .patients + .includes(:consent_statuses, { notes: :created_by }) .has_consent_status( statuses_except_not_required, - programme: @form.programmes + programme: @form.programmes, + academic_year: @session.academic_year ) - patient_sessions = @form.apply(scope) - @pagy, @patient_sessions = pagy(patient_sessions) + patients = @form.apply(scope) + @pagy, @patients = pagy(patients) end private diff --git a/app/controllers/sessions/invite_to_clinic_controller.rb b/app/controllers/sessions/invite_to_clinic_controller.rb index 55f00e84ec..1d920580b3 100644 --- a/app/controllers/sessions/invite_to_clinic_controller.rb +++ b/app/controllers/sessions/invite_to_clinic_controller.rb @@ -14,7 +14,7 @@ def edit def update if @session.school? - factory.create_patient_sessions! + factory.create_patient_locations! flash[ :success @@ -54,7 +54,7 @@ def set_generic_clinic_session def set_patients_to_invite @patients_to_invite = if @session.school? - factory.patient_sessions_to_create.map(&:patient) + factory.patient_locations_to_create.map(&:patient) else session_date = @generic_clinic_session.next_date(include_today: true) SendClinicSubsequentInvitationsJob.new.patients(@session, session_date:) @@ -68,7 +68,7 @@ def set_invitations_to_send def factory @factory ||= if @session.school? - ClinicPatientSessionsFactory.new( + ClinicPatientLocationsFactory.new( school_session: @session, generic_clinic_session: @generic_clinic_session ) diff --git a/app/controllers/sessions/patient_specific_directions_controller.rb b/app/controllers/sessions/patient_specific_directions_controller.rb index 90507b7886..04d959a4c7 100644 --- a/app/controllers/sessions/patient_specific_directions_controller.rb +++ b/app/controllers/sessions/patient_specific_directions_controller.rb @@ -9,21 +9,18 @@ class Sessions::PatientSpecificDirectionsController < ApplicationController before_action :set_patient_search_form def show - scope = - @session.patient_sessions.includes_programmes.includes( - patient: { - patient_specific_directions: :programme - } - ) - @eligible_for_bulk_psd_count = patient_sessions_allowed_psd.count - patient_sessions = @form.apply(scope) - @pagy, @patient_sessions = pagy(patient_sessions) + scope = @session.patients.includes(patient_specific_directions: :programme) + + @eligible_for_bulk_psd_count = patients_allowed_psd.count + + patients = @form.apply(scope) + @pagy, @patients = pagy(patients) render layout: "full" end def new - @eligible_for_bulk_psd_count = patient_sessions_allowed_psd.count + @eligible_for_bulk_psd_count = patients_allowed_psd.count end def create @@ -56,12 +53,12 @@ def set_vaccine end def psds_to_create - patient_sessions_allowed_psd.map do |patient_session| + patients_allowed_psd.map do |patient| PatientSpecificDirection.new( academic_year: @session.academic_year, created_by: current_user, delivery_site: "nose", - patient_id: patient_session.patient_id, + patient:, programme: @programme, team: current_team, vaccine: @vaccine, @@ -70,18 +67,24 @@ def psds_to_create end end - def patient_sessions_allowed_psd - @patient_sessions_allowed_psd ||= + def patients_allowed_psd + @patients_allowed_psd ||= @session - .patient_sessions + .patients .has_consent_status( "given", programme: @programme, + academic_year: @session.academic_year, vaccine_method: "nasal" ) - .has_triage_status("not_required", programme: @programme) + .has_triage_status( + "not_required", + programme: @programme, + academic_year: @session.academic_year + ) .without_patient_specific_direction( programme: @programme, + academic_year: @session.academic_year, team: current_team ) end diff --git a/app/controllers/sessions/patients_controller.rb b/app/controllers/sessions/patients_controller.rb index 87f0390318..6c52fb2d99 100644 --- a/app/controllers/sessions/patients_controller.rb +++ b/app/controllers/sessions/patients_controller.rb @@ -12,12 +12,10 @@ def show @statuses = Patient::VaccinationStatus.statuses.keys scope = - @session.patient_sessions.includes_programmes.includes( - patient: [:vaccination_statuses, { notes: :created_by }] - ) + @session.patients.includes(:vaccination_statuses, notes: :created_by) - patient_sessions = @form.apply(scope) - @pagy, @patient_sessions = pagy(patient_sessions) + patients = @form.apply(scope) + @pagy, @patients = pagy(patients) end private diff --git a/app/controllers/sessions/record_controller.rb b/app/controllers/sessions/record_controller.rb index 0655f461d4..e54c09add3 100644 --- a/app/controllers/sessions/record_controller.rb +++ b/app/controllers/sessions/record_controller.rb @@ -15,28 +15,31 @@ class Sessions::RecordController < ApplicationController def show scope = - @session.patient_sessions.includes( - patient: [ - :consent_statuses, - :triage_statuses, - :vaccination_statuses, - { notes: :created_by } - ] + @session.patients.includes( + :consent_statuses, + :triage_statuses, + :vaccination_statuses, + notes: :created_by ) if @session.requires_registration? - scope = scope.has_registration_status(%w[attending completed]) + scope = + scope.has_registration_status( + %w[attending completed], + session: @session + ) end - patient_sessions = + patients = filter_on_vaccine_method_or_patient_specific_direction( @form.apply(scope) ).consent_given_and_ready_to_vaccinate( programmes: @form.programmes, + academic_year: @session.academic_year, vaccine_method: @form.vaccine_method.presence ) - @pagy, @patient_sessions = pagy_array(patient_sessions) + @pagy, @patients = pagy_array(patients) render layout: "full" end @@ -103,28 +106,32 @@ def filter_on_vaccine_method_or_patient_specific_direction(scope) original_scope = scope + programme = @session.programmes + academic_year = @session.academic_year + team = current_team + if @session.psd_enabled? && @session.national_protocol_enabled? - original_scope.has_patient_specific_direction( - programme: @session.programmes, - team: current_team + original_scope.with_patient_specific_direction( + programme:, + academic_year:, + team: ).or( original_scope.has_vaccine_method( "injection", - programme: @session.programmes + programme:, + academic_year: ) ) elsif @session.pgd_supply_enabled? && @session.national_protocol_enabled? original_scope.has_vaccine_method( %w[nasal injection], - programme: @session.programmes + programme:, + academic_year: ) elsif @session.pgd_supply_enabled? - original_scope.has_vaccine_method("nasal", programme: @session.programmes) + original_scope.has_vaccine_method("nasal", programme:, academic_year:) elsif @session.national_protocol_enabled? - original_scope.has_vaccine_method( - "injection", - programme: @session.programmes - ) + original_scope.has_vaccine_method("injection", programme:, academic_year:) else original_scope.none end diff --git a/app/controllers/sessions/register_controller.rb b/app/controllers/sessions/register_controller.rb index dc5d30c1c0..8a717d855b 100644 --- a/app/controllers/sessions/register_controller.rb +++ b/app/controllers/sessions/register_controller.rb @@ -14,18 +14,16 @@ def show @statuses = Patient::RegistrationStatus.statuses.keys scope = - @session.patient_sessions.includes_programmes.includes( - patient: [ - :consent_statuses, - :registration_statuses, - :triage_statuses, - :vaccination_statuses, - { notes: :created_by } - ] + @session.patients.includes( + :consent_statuses, + :registration_statuses, + :triage_statuses, + :vaccination_statuses, + notes: :created_by ) - patient_sessions = @form.apply(scope) - @pagy, @patient_sessions = pagy(patient_sessions) + patients = @form.apply(scope) + @pagy, @patients = pagy(patients) end def create diff --git a/app/controllers/sessions/triage_controller.rb b/app/controllers/sessions/triage_controller.rb index 206e8528c0..e159a965d9 100644 --- a/app/controllers/sessions/triage_controller.rb +++ b/app/controllers/sessions/triage_controller.rb @@ -13,13 +13,16 @@ def show scope = @session - .patient_sessions - .includes_programmes - .includes(patient: [:triage_statuses, { notes: :created_by }]) - .has_triage_status(@statuses, programme: @form.programmes) - - patient_sessions = @form.apply(scope) - @pagy, @patient_sessions = pagy(patient_sessions) + .patients + .includes(:triage_statuses, notes: :created_by) + .has_triage_status( + @statuses, + programme: @form.programmes, + academic_year: @session.academic_year + ) + + patients = @form.apply(scope) + @pagy, @patients = pagy(patients) end private diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 0c2c07519e..81f0f532e0 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -17,11 +17,12 @@ def index sessions = @form.apply(scope) @patient_count_by_session_id = - PatientSession - .where(session_id: sessions.map(&:id)) - .joins(:patient, :session) + PatientLocation + .joins_sessions + .where("sessions.id IN (?)", sessions.pluck(:id)) + .joins(:patient) .appear_in_programmes(@programmes) - .group(:session_id) + .group("sessions.id") .count @pagy, @sessions = pagy_array(sessions) diff --git a/app/forms/concerns/patient_merge_form_concern.rb b/app/forms/concerns/patient_merge_form_concern.rb index 9f25e6f0fd..29ba0245df 100644 --- a/app/forms/concerns/patient_merge_form_concern.rb +++ b/app/forms/concerns/patient_merge_form_concern.rb @@ -20,7 +20,7 @@ def existing_patient ) || Patient .where - .missing(:patient_sessions) + .missing(:patient_locations) .includes(vaccination_records: :programme) .find_by(nhs_number:) end diff --git a/app/forms/patient_search_form.rb b/app/forms/patient_search_form.rb index 25a22b7301..4d4222f213 100644 --- a/app/forms/patient_search_form.rb +++ b/app/forms/patient_search_form.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class PatientSearchForm < SearchForm - attr_accessor :current_user attr_writer :academic_year attribute :aged_out_of_programmes, :boolean @@ -43,7 +42,7 @@ def programmes if programme_types.present? Programme.where(type: programme_types) else - @session&.programmes + session&.programmes end end @@ -67,10 +66,12 @@ def apply(scope) private + attr_reader :current_user, :session + def academic_year = - @session&.academic_year || @academic_year || AcademicYear.pending + session&.academic_year || @academic_year || AcademicYear.pending - def team = @session&.team || @current_user.selected_team + def team = session&.team || current_user.selected_team def filter_name(scope) q.present? ? scope.search_by_name(q) : scope @@ -87,11 +88,11 @@ def filter_year_groups(scope) def filter_aged_out_of_programmes(scope) if aged_out_of_programmes scope.not_appear_in_programmes(team.programmes, academic_year:) - elsif @session || archived - scope - else + elsif session || archived # Archived patients won't appear in programmes, so we need to # skip this check if we're trying to view archived patients. + scope + else scope.appear_in_programmes(team.programmes, academic_year:) end end @@ -99,7 +100,7 @@ def filter_aged_out_of_programmes(scope) def filter_archived(scope) if archived scope.archived(team:) - elsif @session + elsif session scope else scope.not_archived(team:) @@ -127,9 +128,14 @@ def filter_nhs_number(scope) end def filter_programmes(scope) - if programmes.present? - if @session - scope.joins(:patient, :session).appear_in_programmes(programmes) + if programme_types.present? + if session + birth_academic_years = + programmes.flat_map do |programme| + session.programme_birth_academic_years[programme] + end + + scope.where(birth_academic_year: birth_academic_years) else scope.appear_in_programmes(programmes, academic_year:) end @@ -148,33 +154,21 @@ def filter_consent_statuses(scope) vaccine_methods = given_with_vaccine_method_statuses.map { it.sub("given_", "") } - if @session - scope.has_consent_status( - "given", - programme: programmes, - vaccine_method: vaccine_methods - ) - else - scope.has_consent_status( - "given", - programme: programmes, - academic_year:, - vaccine_method: vaccine_methods - ) - end + scope.has_consent_status( + "given", + programme: programmes, + academic_year:, + vaccine_method: vaccine_methods + ) end other_status_scope = if other_statuses.any? - if @session - scope.has_consent_status(other_statuses, programme: programmes) - else - scope.has_consent_status( - other_statuses, - programme: programmes, - academic_year: - ) - end + scope.has_consent_status( + other_statuses, + programme: programmes, + academic_year: + ) end if given_with_vaccine_method_scope && other_status_scope @@ -189,15 +183,11 @@ def filter_consent_statuses(scope) def filter_vaccination_statuses(scope) if (status = vaccination_status&.to_sym).present? - if @session - scope.has_vaccination_status(status, programme: programmes) - else - scope.has_vaccination_status( - status, - programme: programmes, - academic_year: - ) - end + scope.has_vaccination_status( + status, + programme: programmes, + academic_year: + ) else scope end @@ -208,9 +198,17 @@ def filter_patient_specific_direction_status(scope) case status when :added - scope.has_patient_specific_direction(programme: programmes, team:) + scope.with_patient_specific_direction( + programme: programmes, + academic_year:, + team: + ) when :not_added - scope.without_patient_specific_direction(programme: programmes, team:) + scope.without_patient_specific_direction( + programme: programmes, + academic_year:, + team: + ) else scope end @@ -218,7 +216,7 @@ def filter_patient_specific_direction_status(scope) def filter_register_status(scope) if (status = register_status&.to_sym).present? - scope.has_registration_status(status) + scope.has_registration_status(status, session:) else scope end @@ -226,11 +224,7 @@ def filter_register_status(scope) def filter_triage_status(scope) if (status = triage_status&.to_sym).present? - if @session - scope.has_triage_status(status, programme: programmes) - else - scope.has_triage_status(status, programme: programmes, academic_year:) - end + scope.has_triage_status(status, programme: programmes, academic_year:) else scope end @@ -238,7 +232,11 @@ def filter_triage_status(scope) def filter_vaccine_method(scope) if vaccine_method.present? - scope.has_vaccine_method(vaccine_method, programme: programmes) + scope.has_vaccine_method( + vaccine_method, + programme: programmes, + academic_year: + ) else scope end diff --git a/app/jobs/concerns/send_school_consent_notification_concern.rb b/app/jobs/concerns/send_school_consent_notification_concern.rb index c183e1d240..c297e1985d 100644 --- a/app/jobs/concerns/send_school_consent_notification_concern.rb +++ b/app/jobs/concerns/send_school_consent_notification_concern.rb @@ -9,11 +9,10 @@ def patient_programmes_eligible_for_notification(session:) return unless session.school? && session.open_for_consent? session - .patient_sessions - .includes_programmes + .patient_locations .includes(patient: %i[consent_notifications consents vaccination_records]) - .find_each do |patient_session| - patient = patient_session.patient + .find_each do |patient_location| + patient = patient_location.patient next unless patient.send_notifications? ProgrammeGrouper diff --git a/app/jobs/enqueue_vaccinations_search_in_nhs_job.rb b/app/jobs/enqueue_vaccinations_search_in_nhs_job.rb index d1ff147b06..e52c3937d5 100644 --- a/app/jobs/enqueue_vaccinations_search_in_nhs_job.rb +++ b/app/jobs/enqueue_vaccinations_search_in_nhs_job.rb @@ -4,8 +4,10 @@ class EnqueueVaccinationsSearchInNHSJob < ApplicationJob queue_as :immunisations_api def perform(sessions = nil) - sessions ||= - begin + scope = + if sessions + Session.where(id: sessions.map(&:id)) + else flu = Programme.flu.sole Session .includes(:session_dates) @@ -15,14 +17,11 @@ def perform(sessions = nil) .references(:session_dates) end - patient_ids = - Patient - .includes(:sessions) - .where(patient_sessions: { session: sessions.pluck(:id) }) - .ids + scope.find_each do |session| + ids = session.patients.pluck(:id) + next if ids.empty? - return if patient_ids.empty? - - SearchVaccinationRecordsInNHSJob.perform_bulk(patient_ids.zip) + SearchVaccinationRecordsInNHSJob.perform_bulk(ids.zip) + end end end diff --git a/app/jobs/send_school_session_reminders_job.rb b/app/jobs/send_school_session_reminders_job.rb index 3a1c248382..0de6aa9071 100644 --- a/app/jobs/send_school_session_reminders_job.rb +++ b/app/jobs/send_school_session_reminders_job.rb @@ -22,7 +22,7 @@ def perform SessionNotification .where(session:) .where( - "session_notifications.patient_id = patient_sessions.patient_id" + "session_notifications.patient_id = patient_locations.patient_id" ) .where(session_date: date) .arel diff --git a/app/lib/clinic_patient_sessions_factory.rb b/app/lib/clinic_patient_locations_factory.rb similarity index 74% rename from app/lib/clinic_patient_sessions_factory.rb rename to app/lib/clinic_patient_locations_factory.rb index e3f38fd91e..ef81d452e9 100644 --- a/app/lib/clinic_patient_sessions_factory.rb +++ b/app/lib/clinic_patient_locations_factory.rb @@ -1,19 +1,19 @@ # frozen_string_literal: true -class ClinicPatientSessionsFactory +class ClinicPatientLocationsFactory def initialize(school_session:, generic_clinic_session:) @school_session = school_session @generic_clinic_session = generic_clinic_session end - def create_patient_sessions! - PatientSession.import!( - patient_sessions_to_create, + def create_patient_locations! + PatientLocation.import!( + patient_locations_to_create, on_duplicate_key_ignore: true ) end - def patient_sessions_to_create + def patient_locations_to_create patients_in_school.filter_map do |patient| if SendClinicInitialInvitationsJob.new.should_send_notification?( patient:, @@ -21,9 +21,10 @@ def patient_sessions_to_create programmes:, session_date: ) - PatientSession.includes(:session_notifications).find_or_initialize_by( + PatientLocation.new( patient:, - session: generic_clinic_session + academic_year: generic_clinic_session.academic_year, + location: generic_clinic_session.location ) end end diff --git a/app/lib/generate/vaccination_records.rb b/app/lib/generate/vaccination_records.rb index 65f5f6550b..f806cd99ce 100644 --- a/app/lib/generate/vaccination_records.rb +++ b/app/lib/generate/vaccination_records.rb @@ -8,9 +8,7 @@ def initialize(team:, programme: nil, session: nil, administered: nil) @administered = administered end - def call - create_vaccinations - end + def call = create_vaccinations def self.call(...) = new(...).call @@ -22,34 +20,34 @@ def create_vaccinations attendance_records = [] vaccination_records = [] - random_patient_sessions.each do |patient_session| - patient = patient_session.patient - session = patient_session.session + sessions.each do |session| + location = session.location + + random_patients_for(session:).each do |patient| + unless AttendanceRecord.exists?(patient:, location:) + attendance_records << FactoryBot.build( + :attendance_record, + :present, + patient:, + session: + ) + end + + location_name = location.name if session.clinic? - unless AttendanceRecord.exists?(patient:, location: session.location) - attendance_records << FactoryBot.build( - :attendance_record, - :present, + vaccination_records << FactoryBot.build( + :vaccination_record, + :administered, patient:, - session: + programme:, + team:, + performed_by:, + session:, + vaccine:, + batch:, + location_name: ) end - - location_name = - patient_session.location.name if patient_session.session.clinic? - - vaccination_records << FactoryBot.build( - :vaccination_record, - :administered, - patient: patient_session.patient, - programme:, - team:, - performed_by:, - session: patient_session.session, - vaccine:, - batch:, - location_name: - ) end AttendanceRecord.import!(attendance_records) @@ -58,39 +56,39 @@ def create_vaccinations StatusUpdater.call(patient: vaccination_records.map(&:patient)) end - def random_patient_sessions + def random_patients_for(session:) if administered&.positive? - patient_sessions + patients_for(session:) .sample(administered) .tap do |selected| if selected.size < administered info = - "#{selected.size} (patient_sessions) < #{administered} (administered)" + "#{selected.size} (patient_locations) < #{administered} (administered)" raise "Not enough patients to generate vaccinations: #{info}" end end else - patient_sessions + patients_for(session:) end end - def patient_sessions - (session.presence || team) - .patient_sessions - .joins(:patient) - .includes( - :session, - :location, - session: :session_dates, - patient: %i[consent_statuses vaccination_statuses triage_statuses] - ) - .appear_in_programmes([programme]) - .has_consent_status("given", programme:) + def sessions + ( + @sessions ||= + session ? [session] : team.sessions.includes(:location, :session_dates) + ) + end + + def patients_for(session:) + academic_year = session.academic_year + + session + .patients + .includes(:consent_statuses, :vaccination_statuses, :triage_statuses) + .appear_in_programmes([programme], academic_year:) + .has_consent_status("given", programme:, academic_year:) .select do - it.patient.consent_given_and_safe_to_vaccinate?( - programme:, - academic_year: it.session.academic_year - ) + it.consent_given_and_safe_to_vaccinate?(programme:, academic_year:) end end diff --git a/app/lib/location_sessions_factory.rb b/app/lib/location_sessions_factory.rb index e8be90a105..483a17d573 100644 --- a/app/lib/location_sessions_factory.rb +++ b/app/lib/location_sessions_factory.rb @@ -9,17 +9,16 @@ def initialize(location, academic_year:) def call ActiveRecord::Base.transaction do if location.generic_clinic? - add_patients!( - session: find_or_create_session!(programmes: location.programmes) - ) + find_or_create_session!(programmes: location.programmes) else ProgrammeGrouper .call(location.programmes) .values .reject { |programmes| already_exists?(programmes:) } .map { |programmes| create_session!(programmes:) } - .each { |session| add_patients!(session:) } end + + add_patients! end end @@ -55,10 +54,10 @@ def find_or_create_session!(programmes:) end end - def add_patients!(session:) - PatientSession.import!( - %i[patient_id session_id], - patient_ids.map { [it, session.id] }, + def add_patients! + PatientLocation.import!( + %i[patient_id location_id academic_year], + patient_ids.map { [it, location.id, academic_year] }, on_duplicate_key_ignore: true ) end diff --git a/app/lib/mavis_cli/schools/move_patients.rb b/app/lib/mavis_cli/schools/move_patients.rb index 870c38d4b9..c8289cb5e4 100644 --- a/app/lib/mavis_cli/schools/move_patients.rb +++ b/app/lib/mavis_cli/schools/move_patients.rb @@ -11,6 +11,8 @@ class MovePatients < Dry::CLI::Command def call(source_urn:, target_urn:) MavisCLI.load_rails + academic_year = AcademicYear.pending + old_loc = Location.school.find_by_urn_and_site(source_urn) new_loc = Location.school.find_by_urn_and_site(target_urn) @@ -19,15 +21,14 @@ def call(source_urn:, target_urn:) return end - unless PatientSession - .joins(:patient, :session) + unless PatientLocation + .joins(:patient) .where( patient: { school: old_loc }, - session: { - location: old_loc - } + academic_year:, + location: old_loc ) .all?(&:safe_to_destroy?) raise "Some patient sessions at #{old_loc.urn} are not safe to destroy. Cannot complete transfer." @@ -42,6 +43,10 @@ def call(source_urn:, target_urn:) location_id: new_loc.id ) Patient.where(school_id: old_loc.id).update_all(school_id: new_loc.id) + PatientLocation.where( + academic_year:, + location_id: old_loc.id + ).update_all(location_id: new_loc.id) ConsentForm.where(location_id: old_loc.id).update_all( location_id: new_loc.id ) diff --git a/app/lib/patient_merger.rb b/app/lib/patient_merger.rb index 31e4fdc72c..f8b21acf26 100644 --- a/app/lib/patient_merger.rb +++ b/app/lib/patient_merger.rb @@ -100,16 +100,17 @@ def call end end - patient_to_destroy.patient_sessions.each do |patient_session| - if patient_to_keep.patient_sessions.exists?( - session_id: patient_session.session_id + patient_to_destroy.patient_locations.each do |patient_location| + if patient_to_keep.patient_locations.exists?( + academic_year: patient_location.academic_year, + location_id: patient_location.location_id ) next end - patient_session.update_column(:patient_id, patient_to_keep.id) + patient_location.update_column(:patient_id, patient_to_keep.id) end - PatientSession.where(patient: patient_to_destroy).destroy_all + PatientLocation.where(patient: patient_to_destroy).destroy_all patient_to_destroy.changesets.update_all(patient_id: nil) diff --git a/app/lib/programme_birth_academic_years.rb b/app/lib/programme_birth_academic_years.rb new file mode 100644 index 0000000000..3c71c09b8d --- /dev/null +++ b/app/lib/programme_birth_academic_years.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class ProgrammeBirthAcademicYears + def initialize(programme_year_groups, academic_year:) + @programme_year_groups = programme_year_groups + @academic_year = academic_year + end + + def [](programme) + programme_year_groups[programme].map do + it.to_birth_academic_year(academic_year:) + end + end + + private + + attr_reader :programme_year_groups, :academic_year +end diff --git a/app/lib/stats/consents_by_school.rb b/app/lib/stats/consents_by_school.rb index f037d2e8ab..a7ca0e9d47 100644 --- a/app/lib/stats/consents_by_school.rb +++ b/app/lib/stats/consents_by_school.rb @@ -47,13 +47,13 @@ def collect_data sessions.find_each do |session| session - .patient_sessions + .patient_locations .includes(patient: { consents: %i[consent_form parent] }) - .find_each do |patient_session| + .find_each do |patient_location| grouped_consents = @programmes.map do |programme| ConsentGrouper.call( - patient_session.patient.consents, + patient_location.patient.consents, programme_id: programme.id, academic_year: @academic_year )&.min_by(&:created_at) diff --git a/app/lib/stats/organisations.rb b/app/lib/stats/organisations.rb index df907af5d9..4d80f98714 100644 --- a/app/lib/stats/organisations.rb +++ b/app/lib/stats/organisations.rb @@ -20,7 +20,11 @@ def self.call(...) = new(...).call attr_reader :organisation, :teams, :programmes, :academic_year, :patients def build_patients_scope - Patient.joins(:teams).where(teams: { id: teams.pluck(:id) }).distinct + Patient.distinct.joins_sessions.where( + sessions: { + team_id: teams.map(&:id) + } + ) end def calculate_organisation_stats @@ -182,6 +186,6 @@ def calculate_vaccination_stats(programme) end def get_eligible_patients(programme) - patients.appear_in_programmes([programme], academic_year: academic_year) + patients.appear_in_programmes([programme], academic_year:) end end diff --git a/app/lib/status_updater.rb b/app/lib/status_updater.rb index c1af25ff92..8ae1281d56 100644 --- a/app/lib/status_updater.rb +++ b/app/lib/status_updater.rb @@ -2,12 +2,17 @@ class StatusUpdater def initialize(patient: nil, session: nil) - scope = PatientSession + scope = PatientLocation.joins_sessions scope = scope.where(patient:) if patient - scope = scope.where(session:) if session - @patient_sessions = scope + if session.is_a?(Session) + scope = scope.where(sessions: { id: session.id }) + elsif session + scope = scope.where(sessions: { id: session.pluck(:id) }) + end + + @patient_locations = scope end def call @@ -23,7 +28,7 @@ def self.call(...) = new(...).call private - attr_reader :patient_sessions + attr_reader :patient_locations def update_consent_statuses! Patient::ConsentStatus.import!( @@ -33,7 +38,7 @@ def update_consent_statuses! ) Patient::ConsentStatus - .where(patient: patient_sessions.select(:patient_id)) + .where(patient: patient_locations.select(:patient_id)) .includes(:consents, :patient, :programme, :vaccination_records) .find_in_batches(batch_size: 10_000) do |batch| batch.each(&:assign_status) @@ -51,14 +56,14 @@ def update_consent_statuses! def update_registration_statuses! Patient::RegistrationStatus.import!( %i[patient_id session_id], - patient_session_statuses_to_import, + patient_location_statuses_to_import, on_duplicate_key_ignore: true ) Patient::RegistrationStatus .where( - patient: patient_sessions.select(:patient_id), - session: patient_sessions.select(:session_id) + "(patient_id, session_id) IN (?)", + patient_locations.select("patient_id", "sessions.id") ) .includes( :patient, @@ -87,7 +92,7 @@ def update_triage_statuses! ) Patient::TriageStatus - .where(patient: patient_sessions.select(:patient_id)) + .where(patient: patient_locations.select(:patient_id)) .includes(:patient, :programme, :consents, :triages, :vaccination_records) .find_in_batches(batch_size: 10_000) do |batch| batch.each(&:assign_status) @@ -110,7 +115,7 @@ def update_vaccination_statuses! ) Patient::VaccinationStatus - .where(patient: patient_sessions.select(:patient_id)) + .where(patient: patient_locations.select(:patient_id)) .includes( :patient, :programme, @@ -138,7 +143,7 @@ def academic_years def patient_statuses_to_import @patient_statuses_to_import ||= - patient_sessions + patient_locations .joins(:patient) .pluck(:patient_id, :"patients.birth_academic_year") .uniq @@ -164,15 +169,15 @@ def vaccination_statuses_to_import end end - def patient_session_statuses_to_import - patient_sessions - .joins(:patient, :session) + def patient_location_statuses_to_import + patient_locations + .joins(:patient) .pluck( - :"patients.id", - :"sessions.id", - :"sessions.location_id", - :"sessions.academic_year", - :"patients.birth_academic_year" + "patients.id", + "sessions.id", + "sessions.location_id", + "sessions.academic_year", + "patients.birth_academic_year" ) .filter_map do |patient_id, session_id, location_id, academic_year, birth_academic_year| year_group = birth_academic_year.to_year_group(academic_year:) @@ -202,7 +207,6 @@ def programme_ids_per_year_group def programme_ids_per_location_id_and_year_group @programme_ids_per_location_id_and_year_group ||= LocationProgrammeYearGroup - .distinct .pluck(:location_id, :programme_id, :year_group) .each_with_object({}) do |(location_id, programme_id, year_group), hash| hash[location_id] ||= {} diff --git a/app/lib/team_sessions_factory.rb b/app/lib/team_sessions_factory.rb index 465a0ff198..d65a4dc034 100644 --- a/app/lib/team_sessions_factory.rb +++ b/app/lib/team_sessions_factory.rb @@ -36,9 +36,7 @@ def destroy_orphaned_sessions! .unscheduled .where(academic_year:) .where.not(location: team.locations) - .where - .missing(:patient_sessions) - .destroy_all + .find_each { |session| session.destroy! if session.patients.empty? } end end end diff --git a/app/models/class_import.rb b/app/models/class_import.rb index 9a2cadc313..8fe3a99613 100644 --- a/app/models/class_import.rb +++ b/app/models/class_import.rb @@ -56,10 +56,10 @@ def postprocess_rows! existing_patients = Patient.where(birth_academic_year: birth_academic_years).where( - PatientSession - .joins(session: :location) + PatientLocation + .joins(:location) .where("patient_id = patients.id") - .where(session: { academic_year:, location: }) + .where(academic_year:, location:) .arel .exists ) diff --git a/app/models/immunisation_import.rb b/app/models/immunisation_import.rb index 619562805d..192f7b26d8 100644 --- a/app/models/immunisation_import.rb +++ b/app/models/immunisation_import.rb @@ -34,7 +34,7 @@ class ImmunisationImport < ApplicationRecord include CSVImportable has_and_belongs_to_many :batches - has_and_belongs_to_many :patient_sessions + has_and_belongs_to_many :patient_locations has_and_belongs_to_many :sessions has_and_belongs_to_many :vaccination_records @@ -57,7 +57,7 @@ def process_row(row) @vaccination_records_batch ||= Set.new @batches_batch ||= Set.new @patients_batch ||= Set.new - @patient_sessions_batch ||= Set.new + @patient_locations_batch ||= Set.new @vaccination_records_batch.add(vaccination_record) if (batch = vaccination_record.batch) @@ -65,8 +65,8 @@ def process_row(row) end @patients_batch.add(vaccination_record.patient) - if (patient_session = row.to_patient_session) - @patient_sessions_batch.add(patient_session) + if (patient_location = row.to_patient_location) + @patient_locations_batch.add(patient_location) end count_column_to_increment @@ -78,16 +78,16 @@ def bulk_import(rows: 100) # We need to convert the batch to an array as `import` modifies the # objects to add IDs to any new records. vaccination_records = @vaccination_records_batch.to_a - patient_sessions = @patient_sessions_batch.to_a + patient_locations = @patient_locations_batch.to_a VaccinationRecord.import(vaccination_records, on_duplicate_key_update: :all) - PatientSession.import(patient_sessions, on_duplicate_key_ignore: :all) + PatientLocation.import(patient_locations, on_duplicate_key_ignore: :all) [ [:vaccination_records, vaccination_records], [:batches, @batches_batch], [:patients, @patients_batch], - [:patient_sessions, patient_sessions.select { it.id.present? }] + [:patient_locations, patient_locations.select { it.id.present? }] ].each do |association, collection| link_records_by_type(association, collection) collection.clear diff --git a/app/models/immunisation_import_row.rb b/app/models/immunisation_import_row.rb index c358410632..b1ad69ee23 100644 --- a/app/models/immunisation_import_row.rb +++ b/app/models/immunisation_import_row.rb @@ -156,8 +156,10 @@ def to_vaccination_record vaccination_record end - def to_patient_session - PatientSession.new(patient:, session:) if patient && session + def to_patient_location + if patient && session + PatientLocation.new(patient:, location: session.location, academic_year:) + end end def batch_expiry = @data[:batch_expiry_date] diff --git a/app/models/location.rb b/app/models/location.rb index 7b5f08207e..8abe5bdd2e 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -56,6 +56,7 @@ class Location < ApplicationRecord has_many :attendance_records has_many :consent_forms has_many :location_programme_year_groups + has_many :patient_locations has_many :patients, foreign_key: :school_id has_many :sessions diff --git a/app/models/location_programme_year_group.rb b/app/models/location_programme_year_group.rb index 845f2e282a..6129e9d1d4 100644 --- a/app/models/location_programme_year_group.rb +++ b/app/models/location_programme_year_group.rb @@ -29,6 +29,11 @@ class LocationProgrammeYearGroup < ApplicationRecord scope :pluck_year_groups, -> { distinct.order(:year_group).pluck(:year_group) } + scope :pluck_birth_academic_years, + ->(academic_year:) do + pluck_year_groups.map { it.to_birth_academic_year(academic_year:) } + end + def birth_academic_year(academic_year: nil) year_group.to_birth_academic_year(academic_year:) end diff --git a/app/models/patient.rb b/app/models/patient.rb index 0e8389dbfd..c4fa6ba76c 100644 --- a/app/models/patient.rb +++ b/app/models/patient.rb @@ -71,7 +71,7 @@ class Patient < ApplicationRecord has_many :notes has_many :notify_log_entries has_many :parent_relationships, -> { order(:created_at) } - has_many :patient_sessions + has_many :patient_locations has_many :pds_search_results has_many :pre_screenings has_many :registration_statuses @@ -84,15 +84,8 @@ class Patient < ApplicationRecord has_many :vaccination_statuses has_many :patient_specific_directions + has_many :locations, through: :patient_locations has_many :parents, through: :parent_relationships - has_many :patient_specific_directions - has_many :sessions, through: :patient_sessions - has_many :teams, -> { distinct }, through: :sessions - - has_many :pending_sessions, - -> { where(academic_year: AcademicYear.pending) }, - through: :patient_sessions, - source: :session has_and_belongs_to_many :class_imports has_and_belongs_to_many :cohort_imports @@ -104,12 +97,30 @@ class Patient < ApplicationRecord scope :joins_archive_reasons, ->(team:) do joins( - "LEFT JOIN archive_reasons " \ + "LEFT OUTER JOIN archive_reasons " \ "ON archive_reasons.patient_id = patients.id " \ "AND archive_reasons.team_id = #{team.id}" ) end + scope :joins_sessions, -> { joins(:patient_locations).joins(<<-SQL) } + INNER JOIN sessions + ON sessions.location_id = patient_locations.location_id + AND sessions.academic_year = patient_locations.academic_year + SQL + + scope :joins_session_programmes, -> { joins(<<-SQL) } + INNER JOIN session_programmes + ON session_programmes.session_id = sessions.id + SQL + + scope :joins_location_programme_year_groups, -> { joins(<<-SQL) } + INNER JOIN location_programme_year_groups + ON location_programme_year_groups.location_id = sessions.location_id + AND location_programme_year_groups.programme_id = session_programmes.programme_id + AND location_programme_year_groups.year_group = sessions.academic_year - patients.birth_academic_year - #{Integer::AGE_CHILDREN_START_SCHOOL} + SQL + scope :archived, ->(team:) do joins_archive_reasons(team:).where("archive_reasons.id IS NOT NULL") @@ -142,26 +153,34 @@ class Patient < ApplicationRecord scope :appear_in_programmes, ->(programmes, academic_year:) do where( - PatientSession - .joins(:session) - .where(sessions: { academic_year: }) - .where("patient_id = patients.id") - .appear_in_programmes(programmes) - .arel - .exists + id: + joins_sessions + .joins_session_programmes + .joins_location_programme_year_groups + .where(sessions: { academic_year: }) + .where( + session_programmes: { + programme_id: programmes.map(&:id) + } + ) + .select("patients.id") ) end scope :not_appear_in_programmes, ->(programmes, academic_year:) do where.not( - PatientSession - .joins(:session) - .where(sessions: { academic_year: }) - .where("patient_id = patients.id") - .appear_in_programmes(programmes) - .arel - .exists + id: + joins_sessions + .joins_session_programmes + .joins_location_programme_year_groups + .where(sessions: { academic_year: }) + .where( + session_programmes: { + programme_id: programmes.map(&:id) + } + ) + .select("patients.id") ) end @@ -246,6 +265,80 @@ class Patient < ApplicationRecord ) end + scope :has_vaccine_method, + ->(vaccine_method, programme:, academic_year:) do + where( + Patient::TriageStatus + .where("patient_id = patients.id") + .where(vaccine_method:, programme:, academic_year:) + .arel + .exists + ).or( + where( + Patient::TriageStatus + .where("patient_id = patients.id") + .where(status: "not_required", programme:, academic_year:) + .arel + .exists + ).where( + Patient::ConsentStatus + .where("patient_id = patients.id") + .where(programme:, academic_year:) + .has_vaccine_method(vaccine_method) + .arel + .exists + ) + ) + end + + scope :has_registration_status, + ->(status, session:) do + where( + Patient::RegistrationStatus + .where("patient_id = patients.id") + .where(session:, status:) + .arel + .exists + ) + end + + scope :with_patient_specific_direction, + ->(programme:, academic_year:, team:) do + where( + PatientSpecificDirection + .where("patient_id = patients.id") + .where(programme:, academic_year:, team:) + .not_invalidated + .arel + .exists + ) + end + + scope :without_patient_specific_direction, + ->(programme:, academic_year:, team:) do + where.not( + PatientSpecificDirection + .where("patient_id = patients.id") + .where(programme:, academic_year:, team:) + .not_invalidated + .arel + .exists + ) + end + + scope :consent_given_and_ready_to_vaccinate, + ->(programmes:, academic_year:, vaccine_method:) do + select do |patient| + programmes.any? do |programme| + patient.consent_given_and_safe_to_vaccinate?( + programme:, + academic_year:, + vaccine_method: + ) + end + end + end + validates :given_name, :family_name, :date_of_birth, presence: true validates :birth_academic_year, comparison: { greater_than_or_equal_to: 1990 } @@ -346,6 +439,25 @@ def self.match_existing( results end + def sessions + Session + .joins_patient_locations + .joins_patients + .joins(:session_programmes) + .joins_location_programme_year_groups + .where(patients: { id: }) + .distinct + end + + def teams + Team.left_outer_joins(:sessions).joins(<<-SQL) + INNER JOIN patient_locations + ON patient_locations.patient_id = #{id} + AND patient_locations.location_id = sessions.location_id + AND patient_locations.academic_year = sessions.academic_year + SQL + end + def archived?(team:) archive_reasons.exists?(team:) end @@ -503,9 +615,9 @@ def invalidate! end def not_in_team?(team:, academic_year:) - patient_sessions - .joins(:session) - .where(session: { academic_year:, team: }) + patient_locations + .joins(location: :subteam) + .where(academic_year:, subteams: { team_id: team.id }) .empty? end @@ -513,8 +625,10 @@ def dup_for_pending_changes dup.tap do |new_patient| new_patient.nhs_number = nil - pending_sessions.each do |session| - new_patient.patient_sessions.build(session:) + patient_locations.pending.find_each do |patient_location| + new_patient.patient_locations.build( + **patient_location.slice(:academic_year, :location_id) + ) end school_moves.each do |school_move| @@ -530,11 +644,13 @@ def dup_for_pending_changes end def clear_pending_sessions!(team: nil) - sessions = pending_sessions + scope = patient_locations.pending - sessions = sessions.where(team_id: team.id) unless team.nil? + unless team.nil? + scope = scope.joins_sessions.where("sessions.team_id = ?", team.id) + end - patient_sessions.where(session: sessions).destroy_all_if_safe + scope.destroy_all_if_safe end def self.from_consent_form(consent_form) diff --git a/app/models/patient/vaccination_status.rb b/app/models/patient/vaccination_status.rb index 3454fc8cbe..b174b33657 100644 --- a/app/models/patient/vaccination_status.rb +++ b/app/models/patient/vaccination_status.rb @@ -38,7 +38,7 @@ class Patient::VaccinationStatus < ApplicationRecord -> { kept.order(performed_at: :desc) }, through: :patient - has_one :patient_session + has_one :patient_location has_one :attendance_record, -> { today }, diff --git a/app/models/patient_changeset.rb b/app/models/patient_changeset.rb index 577967cae3..5a230173a2 100644 --- a/app/models/patient_changeset.rb +++ b/app/models/patient_changeset.rb @@ -114,7 +114,7 @@ def patient Patient.new( child_attributes.merge( "home_educated" => false, - "patient_sessions" => [] + "patient_locations" => [] ) ) end @@ -209,7 +209,7 @@ def existing_patients end matches = - Patient.includes(:patient_sessions).match_existing( + Patient.includes(:patient_locations).match_existing( nhs_number: child_attributes["nhs_number"], given_name: child_attributes["given_name"], family_name: child_attributes["family_name"], diff --git a/app/models/patient_import_row.rb b/app/models/patient_import_row.rb index cd0dd2ea99..e0a42dd291 100644 --- a/app/models/patient_import_row.rb +++ b/app/models/patient_import_row.rb @@ -51,7 +51,7 @@ def to_patient prepare_patient_changes(existing_patient, import_attributes) else Patient.new( - import_attributes.merge(home_educated: false, patient_sessions: []) + import_attributes.merge(home_educated: false, patient_locations: []) ) end end @@ -346,7 +346,7 @@ def existing_patients return end - Patient.includes(:patient_sessions).match_existing( + Patient.includes(:patient_locations).match_existing( nhs_number: nhs_number_value, given_name: first_name.to_s, family_name: last_name.to_s, diff --git a/app/models/patient_location.rb b/app/models/patient_location.rb new file mode 100644 index 0000000000..e23f0acaec --- /dev/null +++ b/app/models/patient_location.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: patient_locations +# +# id :bigint not null, primary key +# academic_year :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# location_id :bigint not null +# patient_id :bigint not null +# +# Indexes +# +# idx_on_patient_id_location_id_academic_year_08a1dc4afe (patient_id,location_id,academic_year) UNIQUE +# index_patient_locations_on_location_id (location_id) +# index_patient_locations_on_location_id_and_academic_year (location_id,academic_year) +# +# Foreign Keys +# +# fk_rails_... (location_id => locations.id) +# fk_rails_... (patient_id => patients.id) +# + +class PatientLocation < ApplicationRecord + audited associated_with: :patient + has_associated_audits + + belongs_to :patient + belongs_to :location + + has_many :sessions, + -> { where(academic_year: it.academic_year) }, + through: :location, + class_name: "Session" + + has_one :organisation, through: :location + has_one :subteam, through: :location + has_one :team, through: :location + + has_many :attendance_records, + -> { where(patient_id: it.patient_id) }, + through: :location + + has_many :gillick_assessments, + -> { where(patient_id: it.patient_id) }, + through: :sessions + + has_many :pre_screenings, + -> { where(patient_id: it.patient_id) }, + through: :sessions + + has_many :vaccination_records, + -> { where(patient_id: it.patient_id) }, + through: :sessions + + has_and_belongs_to_many :immunisation_imports + + scope :current, -> { where(academic_year: AcademicYear.current) } + scope :pending, -> { where(academic_year: AcademicYear.pending) } + + scope :joins_sessions, -> { joins(<<-SQL) } + INNER JOIN sessions + ON sessions.location_id = patient_locations.location_id + AND sessions.academic_year = patient_locations.academic_year + SQL + + scope :joins_session_programmes, -> { joins(<<-SQL) } + INNER JOIN session_programmes + ON session_programmes.session_id = sessions.id + SQL + + scope :joins_location_programme_year_groups, -> { joins(<<-SQL) } + INNER JOIN location_programme_year_groups + ON location_programme_year_groups.location_id = patient_locations.location_id + AND location_programme_year_groups.programme_id = session_programmes.programme_id + AND location_programme_year_groups.year_group = patient_locations.academic_year - patients.birth_academic_year - #{Integer::AGE_CHILDREN_START_SCHOOL} + SQL + + scope :appear_in_programmes, + ->(programmes) do + where( + id: + joins_session_programmes + .joins_location_programme_year_groups + .where( + session_programmes: { + programme_id: programmes.map(&:id) + } + ) + .select("patient_locations.id") + ) + end + + scope :destroy_all_if_safe, + -> do + includes( + :attendance_records, + :gillick_assessments, + :pre_screenings, + :vaccination_records + ).find_each(&:destroy_if_safe!) + end + + def safe_to_destroy? + attendance_records.none?(&:attending?) && gillick_assessments.empty? && + pre_screenings.empty? && vaccination_records.empty? + end + + def destroy_if_safe! + destroy! if safe_to_destroy? + end +end diff --git a/app/models/patient_session.rb b/app/models/patient_session.rb deleted file mode 100644 index 89b1e8fe94..0000000000 --- a/app/models/patient_session.rb +++ /dev/null @@ -1,284 +0,0 @@ -# frozen_string_literal: true - -# == Schema Information -# -# Table name: patient_sessions -# -# id :bigint not null, primary key -# created_at :datetime not null -# updated_at :datetime not null -# patient_id :bigint not null -# session_id :bigint not null -# -# Indexes -# -# index_patient_sessions_on_patient_id_and_session_id (patient_id,session_id) UNIQUE -# index_patient_sessions_on_session_id (session_id) -# -# Foreign Keys -# -# fk_rails_... (patient_id => patients.id) -# fk_rails_... (session_id => sessions.id) -# - -# The patient session model represents a patient who goes to the school or -# clinic location represented by the session. This doesn't necessarily mean -# that the patient is eligible for any of the programmes administered in any -# particular session they belong to. -# -# This is designed to support programmes being dynamically added or removed to -# sessions without needing to also create or destroy patient session -# instances. While also adding support for patients becoming eligible if year -# groups are added or removed to locations, again without needing to also -# create or destroy patient session instances. -# -# It also supports the scenario where a patient belongs to a session but is -# only eligible for one of many programmes administered in the session. In -# that case, the list of programmes they appear in won't be the same as the -# complete list of programmes administered in the session. - -class PatientSession < ApplicationRecord - audited associated_with: :patient - has_associated_audits - - belongs_to :patient - belongs_to :session - - has_one :location, through: :session - has_one :subteam, through: :session - has_one :team, through: :session - has_one :organisation, through: :team - - has_many :gillick_assessments, - -> { where(patient_id: it.patient_id) }, - through: :session - - has_many :pre_screenings, - -> { where(patient_id: it.patient_id) }, - through: :session - - has_many :attendance_records, - -> { where(patient_id: it.patient_id) }, - through: :location - - has_many :vaccination_records, - -> { where(session_id: it.session_id) }, - through: :patient - - has_and_belongs_to_many :immunisation_imports - - scope :archived, ->(team:) { merge(Patient.archived(team:)) } - - scope :not_archived, ->(team:) { merge(Patient.not_archived(team:)) } - - scope :appear_in_programmes, - ->(programmes) do - # Are any of the programmes administered in the session? - programme_in_session = - SessionProgramme - .where(programme: programmes) - .where("session_programmes.session_id = sessions.id") - .arel - .exists - - # Is the patient eligible for any of those programmes by year group? - patient_in_administered_year_groups = - LocationProgrammeYearGroup - .where(programme: programmes) - .where("location_id = sessions.location_id") - .where( - "year_group = sessions.academic_year " \ - "- patients.birth_academic_year " \ - "- #{Integer::AGE_CHILDREN_START_SCHOOL}" - ) - .arel - .exists - - where(programme_in_session).where(patient_in_administered_year_groups) - end - - scope :search_by_name, - ->(name) { joins(:patient).merge(Patient.search_by_name(name)) } - - scope :search_by_year_groups, - ->(year_groups, academic_year:) do - joins(:patient).merge( - Patient.search_by_year_groups(year_groups, academic_year:) - ) - end - - scope :search_by_date_of_birth_year, - ->(year) do - where("extract(year from patients.date_of_birth) = ?", year) - end - - scope :search_by_date_of_birth_month, - ->(month) do - where("extract(month from patients.date_of_birth) = ?", month) - end - - scope :search_by_date_of_birth_day, - ->(day) { where("extract(day from patients.date_of_birth) = ?", day) } - - scope :search_by_nhs_number, - ->(nhs_number) { merge(Patient.search_by_nhs_number(nhs_number)) } - - scope :order_by_name, - -> do - joins(:patient).order( - "LOWER(patients.family_name)", - "LOWER(patients.given_name)" - ) - end - - scope :includes_programmes, - -> do - preload( - :patient, - session: %i[programmes location_programme_year_groups] - ) - end - - scope :has_consent_status, - ->(status, programme:, vaccine_method: nil) do - consent_status_scope = - Patient::ConsentStatus - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(status:, programme:) - - if vaccine_method - consent_status_scope = - consent_status_scope.has_vaccine_method(vaccine_method) - end - - joins(:session).where(consent_status_scope.arel.exists) - end - - scope :has_registration_status, - ->(status) do - where( - Patient::RegistrationStatus - .where("patient_id = patient_sessions.patient_id") - .where("session_id = patient_sessions.session_id") - .where(status:) - .arel - .exists - ) - end - - scope :has_triage_status, - ->(status, programme:) do - joins(:session).where( - Patient::TriageStatus - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(status:, programme:) - .arel - .exists - ) - end - - scope :has_vaccination_status, - ->(status, programme:) do - joins(:session).where( - Patient::VaccinationStatus - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(status:, programme:) - .arel - .exists - ) - end - - scope :has_vaccine_method, - ->(vaccine_method, programme:) do - joins(:session).where( - Patient::TriageStatus - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(vaccine_method:, programme:) - .arel - .exists - ).or( - joins(:session).where( - Patient::TriageStatus - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(status: "not_required", programme:) - .arel - .exists - ).where( - Patient::ConsentStatus - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(programme:) - .has_vaccine_method(vaccine_method) - .arel - .exists - ) - ) - end - - scope :consent_given_and_ready_to_vaccinate, - ->(programmes:, vaccine_method:) do - select do |patient_session| - patient = patient_session.patient - session = patient_session.session - - programmes.any? do |programme| - patient.consent_given_and_safe_to_vaccinate?( - programme:, - academic_year: session.academic_year, - vaccine_method: - ) - end - end - end - - scope :without_patient_specific_direction, - ->(programme:, team:) do - joins(:session).where.not( - PatientSpecificDirection - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(programme:, team:) - .not_invalidated - .arel - .exists - ) - end - - scope :has_patient_specific_direction, - ->(programme:, team:) do - joins(:session).where( - PatientSpecificDirection - .where("patient_id = patient_sessions.patient_id") - .where("academic_year = sessions.academic_year") - .where(programme:, team:) - .not_invalidated - .arel - .exists - ) - end - - scope :destroy_all_if_safe, - -> do - includes( - :gillick_assessments, - :attendance_records, - :vaccination_records - ).find_each(&:destroy_if_safe!) - end - - delegate :academic_year, to: :session - - def safe_to_destroy? - vaccination_records.empty? && gillick_assessments.empty? && - attendance_records.none?(&:attending?) - end - - def destroy_if_safe! - destroy! if safe_to_destroy? - end -end diff --git a/app/models/school_move.rb b/app/models/school_move.rb index e16187e993..1461f6e424 100644 --- a/app/models/school_move.rb +++ b/app/models/school_move.rb @@ -98,33 +98,15 @@ def update_archive_reasons!(user:) def update_sessions! patient - .patient_sessions - .joins(:session) + .patient_locations .where("academic_year >= ?", academic_year) .destroy_all_if_safe - sessions_to_add.find_each do |session| - PatientSession.find_or_create_by!(patient:, session:) - end + location = school || team.generic_clinic - StatusUpdater.call(patient:) - end + PatientLocation.find_or_create_by!(patient:, location:, academic_year:) - def sessions_to_add - @sessions_to_add ||= - begin - scope = - Session.includes(:location, :session_dates).where( - "academic_year >= ?", - academic_year - ) - - if school - scope.where(team: school.team, location: school) - else - scope.where(team:, locations: { type: "generic_clinic" }) - end - end + StatusUpdater.call(patient:) end def create_log_entry!(user:) diff --git a/app/models/session.rb b/app/models/session.rb index b23129714a..d07e92e260 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -20,9 +20,10 @@ # # Indexes # -# index_sessions_on_location_id (location_id) -# index_sessions_on_team_id_and_academic_year (team_id,academic_year) -# index_sessions_on_team_id_and_location_id (team_id,location_id) +# index_sessions_on_location_id (location_id) +# index_sessions_on_location_id_and_academic_year_and_team_id (location_id,academic_year,team_id) +# index_sessions_on_team_id_and_academic_year (team_id,academic_year) +# index_sessions_on_team_id_and_location_id (team_id,location_id) # # Foreign Keys # @@ -39,7 +40,6 @@ class Session < ApplicationRecord has_many :consent_notifications has_many :notes - has_many :patient_sessions has_many :session_dates, -> { order(:value) } has_many :session_notifications has_many :session_programmes, @@ -49,12 +49,16 @@ class Session < ApplicationRecord has_and_belongs_to_many :immunisation_imports + has_many :patient_locations, + -> { where(academic_year: it.academic_year) }, + through: :location + has_one :organisation, through: :team has_one :subteam, through: :location has_many :pre_screenings, through: :session_dates has_many :programmes, through: :session_programmes has_many :gillick_assessments, through: :session_dates - has_many :patients, through: :patient_sessions + has_many :patients, through: :patient_locations has_many :vaccines, through: :programmes has_many :location_programme_year_groups, @@ -63,6 +67,24 @@ class Session < ApplicationRecord accepts_nested_attributes_for :session_dates, allow_destroy: true + scope :joins_patient_locations, -> { joins(<<-SQL) } + INNER JOIN patient_locations + ON patient_locations.location_id = sessions.location_id + AND patient_locations.academic_year = sessions.academic_year + SQL + + scope :joins_patients, -> { joins(<<-SQL) } + INNER JOIN patients + ON patients.id = patient_locations.patient_id + SQL + + scope :joins_location_programme_year_groups, -> { joins(<<-SQL) } + INNER JOIN location_programme_year_groups + ON location_programme_year_groups.location_id = sessions.location_id + AND location_programme_year_groups.programme_id = session_programmes.programme_id + AND location_programme_year_groups.year_group = sessions.academic_year - patients.birth_academic_year - #{Integer::AGE_CHILDREN_START_SCHOOL} + SQL + scope :has_date, ->(value) { where(SessionDate.for_session.where(value:).arel.exists) } @@ -174,18 +196,27 @@ class Session < ApplicationRecord delegate :clinic?, :generic_clinic?, :school?, to: :location - def to_param - slug + def programme_birth_academic_years + @programme_birth_academic_years ||= + ProgrammeBirthAcademicYears.new(programme_year_groups, academic_year:) end - def today? - dates.any?(&:today?) - end + def patients + birth_academic_years = + location_programme_year_groups.pluck_birth_academic_years(academic_year:) - def unscheduled? - dates.empty? + Patient + .joins_sessions + .where(sessions: { id: }) + .where(birth_academic_year: birth_academic_years) end + def to_param = slug + + def today? = dates.any?(&:today?) + + def unscheduled? = dates.empty? + def completed? return false if dates.empty? Date.current > dates.max @@ -301,7 +332,7 @@ def open_for_consent? def next_reminder_date = next_reminder_dates.first def patients_with_no_consent_response_count - patient_sessions.has_consent_status( + patient_locations.has_consent_status( "no_response", programme: programmes ).count diff --git a/app/models/team.rb b/app/models/team.rb index 0c4e1614d9..3e67de8a6d 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -52,8 +52,6 @@ class Team < ApplicationRecord has_many :community_clinics, through: :subteams has_many :locations, through: :subteams - has_many :patient_sessions, through: :sessions - has_many :patients, -> { distinct }, through: :patient_sessions has_many :programmes, through: :team_programmes has_many :schools, through: :subteams has_many :vaccination_records, through: :sessions @@ -76,13 +74,17 @@ class Team < ApplicationRecord validates :privacy_policy_url, presence: true validates :workgroup, presence: true, uniqueness: true + def patients + Patient.joins_sessions.where(sessions: { team_id: id }) + end + def year_groups @year_groups ||= location_programme_year_groups.pluck_year_groups end - def generic_clinic_session(academic_year:) - location = locations.generic_clinic.first + def generic_clinic = locations.generic_clinic.first + def generic_clinic_session(academic_year:) sessions .includes( :location, @@ -91,7 +93,7 @@ def generic_clinic_session(academic_year:) :session_dates ) .create_with(programmes:) - .find_or_create_by!(academic_year:, location:) + .find_or_create_by!(academic_year:, location: generic_clinic) end def weeks_before_consent_reminders diff --git a/app/policies/patient_policy.rb b/app/policies/patient_policy.rb index e7f2ca1d1d..89263f38c4 100644 --- a/app/policies/patient_policy.rb +++ b/app/policies/patient_policy.rb @@ -9,11 +9,11 @@ def resolve return scope.none if team.nil? existence_criteria = [ - PatientSession + PatientLocation .select("1") - .joins(:session) - .where("patient_sessions.patient_id = patients.id") - .where(sessions: { team_id: team.id }) + .joins_sessions + .where("patient_locations.patient_id = patients.id") + .where("sessions.team_id = ?", team.id) .arel, ArchiveReason .select("1") diff --git a/app/policies/school_move_policy.rb b/app/policies/school_move_policy.rb index a1c15ef832..7bded900f2 100644 --- a/app/policies/school_move_policy.rb +++ b/app/policies/school_move_policy.rb @@ -6,23 +6,16 @@ def resolve team = user.selected_team return scope.none if team.nil? - patient_subquery = - Patient - .joins(patient_sessions: :session) - .select(:id) - .distinct - .where(sessions: { team_id: team.id }) + patient_in_team = + team + .patients + .select("1") + .where("patients.id = school_moves.patient_id") .arel - .as("patients") + .exists + scope - .joins( - SchoolMove - .arel_table - .join(patient_subquery, Arel::Nodes::OuterJoin) - .on(SchoolMove.arel_table[:patient_id].eq(patient_subquery[:id])) - .join_sources - ) - .where(patient_subquery[:id].not_eq(nil)) + .where(patient_in_team) .or(scope.where(school: team.schools)) .or(scope.where(team:)) end diff --git a/app/policies/vaccination_record_policy.rb b/app/policies/vaccination_record_policy.rb index b6b8ed8a19..a4d19d4764 100644 --- a/app/policies/vaccination_record_policy.rb +++ b/app/policies/vaccination_record_policy.rb @@ -67,16 +67,16 @@ def resolve team = user.selected_team return scope.none if team.nil? - relevant_patients = - Patient + patient_in_team = + team + .patients .select("1") - .joins(patient_sessions: :session) .where("patients.id = vaccination_records.patient_id") - .where(sessions: { team_id: team.id }) .arel + .exists scope .kept - .where(relevant_patients.exists) + .where(patient_in_team) .or(scope.kept.where(session: team.sessions)) .or( scope.kept.where( diff --git a/app/views/programmes/sessions/index.html.erb b/app/views/programmes/sessions/index.html.erb index 7f85f6ff85..6aeffa0992 100644 --- a/app/views/programmes/sessions/index.html.erb +++ b/app/views/programmes/sessions/index.html.erb @@ -12,4 +12,4 @@ <%= render AppProgrammeNavigationComponent.new(@programme, @academic_year, active: :sessions) %> -<%= render AppProgrammeSessionTableComponent.new(@sessions, programme: @programme) %> +<%= render AppProgrammeSessionTableComponent.new(@sessions, programme: @programme, academic_year: @academic_year) %> diff --git a/app/views/sessions/consent/show.html.erb b/app/views/sessions/consent/show.html.erb index 72cfa112ea..8094ee63ee 100644 --- a/app/views/sessions/consent/show.html.erb +++ b/app/views/sessions/consent/show.html.erb @@ -22,10 +22,10 @@