From 27a82489c6dc2901fbf8ef70323c77c53dbf47a8 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Thu, 5 Sep 2024 13:30:49 +0100 Subject: [PATCH 1/3] Rename Session draft column This will be renamed to active to match the column naming convention we have in the other tables, to improve consistency. --- app/models/session.rb | 6 ++--- ...05113128_rename_session_draft_to_active.rb | 21 ++++++++++++++++++ db/schema.rb | 4 ++-- erd.pdf | Bin 59915 -> 59911 bytes spec/factories/sessions.rb | 2 +- spec/models/session_spec.rb | 2 +- 6 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20240905113128_rename_session_draft_to_active.rb diff --git a/app/models/session.rb b/app/models/session.rb index 7ccea3145b..b1e1b8d9b7 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -5,9 +5,9 @@ # Table name: sessions # # id :bigint not null, primary key +# active :boolean default(FALSE), not null # close_consent_at :date # date :date -# draft :boolean default(FALSE) # send_consent_at :date # send_reminders_at :date # time_of_day :integer @@ -98,8 +98,8 @@ class Session < ApplicationRecord if: -> { close_consent_on == "custom" } end - def active? - !draft + def draft? + !active end def health_questions diff --git a/db/migrate/20240905113128_rename_session_draft_to_active.rb b/db/migrate/20240905113128_rename_session_draft_to_active.rb new file mode 100644 index 0000000000..6554356bb4 --- /dev/null +++ b/db/migrate/20240905113128_rename_session_draft_to_active.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RenameSessionDraftToActive < ActiveRecord::Migration[7.2] + def up + change_table :sessions, bulk: true do |t| + t.rename :draft, :active + t.change_null :active, false + end + + Session.update_all("active = NOT active") + end + + def down + Session.update_all("active = NOT active") + + change_table :sessions, bulk: true do |t| + t.rename :active, :draft + t.change_null :draft, true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b0bf6f8d83..bcd744b251 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_09_05_090740) do +ActiveRecord::Schema[7.2].define(version: 2024_09_05_113128) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -360,7 +360,7 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.bigint "campaign_id" - t.boolean "draft", default: false + t.boolean "active", default: false, null: false t.date "send_consent_at" t.date "send_reminders_at" t.date "close_consent_at" diff --git a/erd.pdf b/erd.pdf index 2703b017d548b7682107846c14b7e3e60745a5f0..5cf99e3404f8a224c193314aa90bd679ade47a31 100644 GIT binary patch delta 13789 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 COIVoz delta 13757 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 PQaD}&Dk>>uX@vg^kmgV_ diff --git a/spec/factories/sessions.rb b/spec/factories/sessions.rb index d57ad94ee0..5eeaef5cb0 100644 --- a/spec/factories/sessions.rb +++ b/spec/factories/sessions.rb @@ -5,9 +5,9 @@ # Table name: sessions # # id :bigint not null, primary key +# active :boolean default(FALSE), not null # close_consent_at :date # date :date -# draft :boolean default(FALSE) # send_consent_at :date # send_reminders_at :date # time_of_day :integer diff --git a/spec/models/session_spec.rb b/spec/models/session_spec.rb index 16f7e966c6..93ccdbfe0a 100644 --- a/spec/models/session_spec.rb +++ b/spec/models/session_spec.rb @@ -5,9 +5,9 @@ # Table name: sessions # # id :bigint not null, primary key +# active :boolean default(FALSE), not null # close_consent_at :date # date :date -# draft :boolean default(FALSE) # send_consent_at :date # send_reminders_at :date # time_of_day :integer From 455a325e1e8c21096e806f5879e8d82de00581d1 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Thu, 5 Sep 2024 13:53:03 +0100 Subject: [PATCH 2/3] Handle Session draft column rename This updates the code in a number of places to support the rename of the session draft column from `draft` to `active`, the functionality should remain the same before and after. --- app/controllers/sessions/edit_controller.rb | 20 +++++++-------- app/controllers/sessions_controller.rb | 2 +- app/models/immunisation_import.rb | 2 +- app/models/immunisation_import_row.rb | 2 +- app/models/session.rb | 4 +-- .../app_session_details_component_spec.rb | 1 - spec/factories/sessions.rb | 10 ++++++++ spec/jobs/consent_reminders_job_spec.rb | 9 +++---- spec/jobs/consent_requests_job_spec.rb | 25 ++++--------------- spec/jobs/session_reminders_job_spec.rb | 8 +++--- spec/models/session_spec.rb | 25 ++++++++++--------- spec/policies/session_policy_spec.rb | 10 +++----- 12 files changed, 52 insertions(+), 66 deletions(-) diff --git a/app/controllers/sessions/edit_controller.rb b/app/controllers/sessions/edit_controller.rb index 7be58350f6..9a612a22b2 100644 --- a/app/controllers/sessions/edit_controller.rb +++ b/app/controllers/sessions/edit_controller.rb @@ -21,7 +21,7 @@ def show def update case current_step when :confirm - @session.draft = false + @session.active = true @session.patient_sessions.update_all(active: true) if @session.send_consent_at.today? @@ -109,16 +109,14 @@ def set_patients @session .location .patients - .where( - "NOT EXISTS (:sessions)", - sessions: - Session - .select(1) - .joins(:patient_sessions) - .where( - "patient_sessions.patient_id = patients.id AND draft = false AND campaign_id = :campaign_id", - campaign_id: @session.campaign_id - ) + .where.not( + Session + .joins(:patient_sessions) + .active + .where(campaign: @session.campaign) + .where("patient_sessions.patient_id = patients.id") + .arel + .exists ) .sort_by(&:last_name) end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 17d70b3de7..285fcaa931 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -8,7 +8,7 @@ def create campaign = current_user.team.campaigns.first - @session = Session.create!(draft: true, campaign:) + @session = Session.create!(active: false, campaign:) redirect_to session_edit_path(@session, :location) end diff --git a/app/models/immunisation_import.rb b/app/models/immunisation_import.rb index 5dbe96b4d4..bbaba2c501 100644 --- a/app/models/immunisation_import.rb +++ b/app/models/immunisation_import.rb @@ -107,7 +107,7 @@ def record! end if (session = vaccination_record.session).draft? - session.update!(draft: false) + session.update!(active: true) end vaccination_record.update!(recorded_at:) diff --git a/app/models/immunisation_import_row.rb b/app/models/immunisation_import_row.rb index a48c9de8f7..18719154b7 100644 --- a/app/models/immunisation_import_row.rb +++ b/app/models/immunisation_import_row.rb @@ -113,7 +113,7 @@ def session .sessions .active .or(Session.where(imported_from:)) - .create_with(imported_from:, draft: true) + .create_with(imported_from:, active: false) .find_or_create_by!( date: session_date, location:, diff --git a/app/models/session.rb b/app/models/session.rb index b1e1b8d9b7..ec0404e3f1 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -46,8 +46,8 @@ class Session < ApplicationRecord enum :time_of_day, %w[morning afternoon all_day] - scope :active, -> { where(draft: false) } - scope :draft, -> { where(draft: true) } + scope :active, -> { where(active: true) } + scope :draft, -> { where(active: false) } scope :past, -> { where(date: ..Time.zone.yesterday) } scope :in_progress, -> { where(date: Time.zone.today) } scope :future, -> { where(date: Time.zone.tomorrow..) } diff --git a/spec/components/app_session_details_component_spec.rb b/spec/components/app_session_details_component_spec.rb index e35171f94f..63547a55c0 100644 --- a/spec/components/app_session_details_component_spec.rb +++ b/spec/components/app_session_details_component_spec.rb @@ -51,7 +51,6 @@ context "for a session with the minimum amount of information" do let(:session) do Session.new( - draft: false, location: create(:location, :school), campaign: create(:campaign, :hpv), date:, diff --git a/spec/factories/sessions.rb b/spec/factories/sessions.rb index 5eeaef5cb0..3142a13a93 100644 --- a/spec/factories/sessions.rb +++ b/spec/factories/sessions.rb @@ -40,6 +40,16 @@ time_of_day { %w[morning afternoon all_day].sample } + active { campaign.active } + + trait :active do + active { true } + end + + trait :draft do + active { false } + end + trait :in_progress do date { Time.zone.now } end diff --git a/spec/jobs/consent_reminders_job_spec.rb b/spec/jobs/consent_reminders_job_spec.rb index 76323de38b..b9667ecaf5 100644 --- a/spec/jobs/consent_reminders_job_spec.rb +++ b/spec/jobs/consent_reminders_job_spec.rb @@ -8,10 +8,9 @@ context "with draft and active sessions" do it "enqueues ConsentRemindersSessionBatchJob for each active sessions" do - active_session = - create(:session, draft: false, send_reminders_at: Time.zone.today) + active_session = create(:session, send_reminders_at: Time.zone.today) _draft_session = - create(:session, draft: true, campaign: active_session.campaign) + create(:session, :draft, campaign: active_session.campaign) described_class.perform_now expect(ConsentRemindersSessionBatchJob).to have_been_enqueued.once @@ -23,12 +22,10 @@ context "with sessions set to send consent today and in the future" do it "enqueues ConsentRemindersSessionBatchJob for the session set to send consent today" do - active_session = - create(:session, draft: false, send_reminders_at: Time.zone.today) + active_session = create(:session, send_reminders_at: Time.zone.today) _later_session = create( :session, - draft: false, send_reminders_at: 2.days.from_now, campaign: active_session.campaign ) diff --git a/spec/jobs/consent_requests_job_spec.rb b/spec/jobs/consent_requests_job_spec.rb index 9082502d6e..1b94ea105b 100644 --- a/spec/jobs/consent_requests_job_spec.rb +++ b/spec/jobs/consent_requests_job_spec.rb @@ -6,18 +6,13 @@ ActiveJob::Base.queue_adapter.enqueued_jobs.clear end - let(:campaign) { create(:campaign) } + let(:campaign) { create(:campaign, :active) } context "with draft and active sessions" do it "enqueues ConsentRequestsSessionBatchJob for each active sessions" do active_session = - create( - :session, - draft: false, - send_consent_at: Time.zone.today, - campaign: - ) - _draft_session = create(:session, draft: true, campaign:) + create(:session, send_consent_at: Time.zone.today, campaign:) + _draft_session = create(:session, :draft, campaign:) described_class.perform_now expect(ConsentRequestsSessionBatchJob).to have_been_enqueued.once @@ -30,19 +25,9 @@ context "with sessions set to send consent today and in the future" do it "enqueues ConsentRequestsSessionBatchJob for the session set to send consent today" do active_session = - create( - :session, - draft: false, - send_consent_at: Time.zone.today, - campaign: - ) + create(:session, send_consent_at: Time.zone.today, campaign:) _later_session = - create( - :session, - draft: false, - send_consent_at: 2.days.from_now, - campaign: - ) + create(:session, send_consent_at: 2.days.from_now, campaign:) described_class.perform_now expect(ConsentRequestsSessionBatchJob).to have_been_enqueued.once diff --git a/spec/jobs/session_reminders_job_spec.rb b/spec/jobs/session_reminders_job_spec.rb index df094369f1..85dd4a18b2 100644 --- a/spec/jobs/session_reminders_job_spec.rb +++ b/spec/jobs/session_reminders_job_spec.rb @@ -6,7 +6,7 @@ ActiveJob::Base.queue_adapter.enqueued_jobs.clear end - let(:campaign) { create(:campaign) } + let(:campaign) { create(:campaign, :active) } it "enqueues SessionRemdindersJob for each session happening tomorrow" do tomorrow_session = create(:session, date: Date.tomorrow, campaign:) @@ -22,10 +22,8 @@ context "with draft and active sessions" do it "enqueues ConsentRemindersSessionBatchJob for each active sessions" do - active_session = - create(:session, draft: false, date: Date.tomorrow, campaign:) - _draft_session = - create(:session, draft: true, date: Date.tomorrow, campaign:) + active_session = create(:session, date: Date.tomorrow, campaign:) + _draft_session = create(:session, :draft, date: Date.tomorrow, campaign:) described_class.perform_now expect(SessionRemindersBatchJob).to have_been_enqueued.once diff --git a/spec/models/session_spec.rb b/spec/models/session_spec.rb index 93ccdbfe0a..e81b34c6e6 100644 --- a/spec/models/session_spec.rb +++ b/spec/models/session_spec.rb @@ -27,7 +27,7 @@ # fk_rails_... (imported_from_id => immunisation_imports.id) # -describe Session do +describe Session, type: :model do describe "validations" do context "when wizard_step is location" do subject { build(:session, wizard_step:, campaign:) } @@ -40,6 +40,18 @@ end end + describe "scopes" do + describe "#active" do + subject(:scope) { described_class.active } + + let!(:active_session) { create(:session) } + let!(:draft_session) { create(:session, :draft) } + + it { should include(active_session) } + it { should_not include(draft_session) } + end + end + describe "#in_progress?" do subject { session.in_progress? } @@ -61,15 +73,4 @@ it { should be_falsey } end end - - describe ".active scope" do - subject { described_class.active } - - let(:campaign) { create(:campaign) } - let!(:active_session) { create(:session, campaign:) } - let!(:draft_session) { create(:session, campaign:, draft: true) } - - it { should include active_session } - it { should_not include draft_session } - end end diff --git a/spec/policies/session_policy_spec.rb b/spec/policies/session_policy_spec.rb index 6c1bdfe4b3..53be712fc5 100644 --- a/spec/policies/session_policy_spec.rb +++ b/spec/policies/session_policy_spec.rb @@ -26,8 +26,8 @@ let(:team) { create :team } let(:user) { create :user, teams: [team] } let(:location) { create(:location, :school) } - let(:campaign) { create :campaign, team: } - let(:draft_session) { create :session, draft: true, location:, campaign: } + let(:campaign) { create :campaign, :active, team: } + let(:draft_session) { create :session, :draft, location:, campaign: } let(:session) { create :session, location:, campaign: } it { should include draft_session } @@ -35,16 +35,14 @@ context "location and campaign are nil" do let(:draft_session) do - create :session, draft: true, location: nil, campaign: nil + create :session, :draft, location: nil, campaign: nil end it { should include draft_session } end context "campaign is set but not location" do - let(:draft_session) do - create :session, draft: true, location: nil, campaign: - end + let(:draft_session) { create :session, :draft, location: nil, campaign: } it { should include draft_session } end From fc83f74c17ed9609138e0dccc197e6b268611648 Mon Sep 17 00:00:00 2001 From: Thomas Leese Date: Thu, 5 Sep 2024 13:35:15 +0100 Subject: [PATCH 3/3] Add Draftable concern This adds a new concern to encapsulate the logic around models which have an active field, and two states of either draft or active. --- app/models/campaign.rb | 3 +-- app/models/concerns/draftable.rb | 14 ++++++++++++++ app/models/patient_session.rb | 8 ++------ app/models/session.rb | 14 +++----------- spec/factories/patient_sessions.rb | 2 +- 5 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 app/models/concerns/draftable.rb diff --git a/app/models/campaign.rb b/app/models/campaign.rb index ede63bbb68..fc135beb80 100644 --- a/app/models/campaign.rb +++ b/app/models/campaign.rb @@ -24,6 +24,7 @@ # fk_rails_... (team_id => teams.id) # class Campaign < ApplicationRecord + include Draftable include WizardStepConcern self.inheritance_column = nil @@ -44,8 +45,6 @@ class Campaign < ApplicationRecord enum :type, { flu: "flu", hpv: "hpv" }, validate: { allow_nil: true } - scope :active, -> { where(active: true) } - normalizes :name, with: ->(name) { name&.strip } on_wizard_step :details do diff --git a/app/models/concerns/draftable.rb b/app/models/concerns/draftable.rb new file mode 100644 index 0000000000..55a12d9e71 --- /dev/null +++ b/app/models/concerns/draftable.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Draftable + extend ActiveSupport::Concern + + included do + scope :active, -> { where(active: true) } + scope :draft, -> { where(active: false) } + end + + def draft? + !active + end +end diff --git a/app/models/patient_session.rb b/app/models/patient_session.rb index 7a576d26f8..23e73457e6 100644 --- a/app/models/patient_session.rb +++ b/app/models/patient_session.rb @@ -24,6 +24,8 @@ # class PatientSession < ApplicationRecord + include Draftable + audited has_associated_audits @@ -50,12 +52,6 @@ class PatientSession < ApplicationRecord through: :patient, class_name: "Consent" - scope :active, -> { where(active: true) } - - def draft? - !active - end - def vaccination_record # HACK: in future, it will be possible to have multiple vaccination records for a patient session vaccination_records.recorded.last diff --git a/app/models/session.rb b/app/models/session.rb index ec0404e3f1..876678dbb7 100644 --- a/app/models/session.rb +++ b/app/models/session.rb @@ -27,7 +27,9 @@ # fk_rails_... (imported_from_id => immunisation_imports.id) # class Session < ApplicationRecord + include Draftable include WizardStepConcern + audited DEFAULT_DAYS_FOR_REMINDER = 2 @@ -46,8 +48,6 @@ class Session < ApplicationRecord enum :time_of_day, %w[morning afternoon all_day] - scope :active, -> { where(active: true) } - scope :draft, -> { where(active: false) } scope :past, -> { where(date: ..Time.zone.yesterday) } scope :in_progress, -> { where(date: Time.zone.today) } scope :future, -> { where(date: Time.zone.tomorrow..) } @@ -55,11 +55,7 @@ class Session < ApplicationRecord after_initialize :set_timeline_attributes after_validation :set_timeline_timestamps - validates :time_of_day, - inclusion: { - in: time_of_days.keys - }, - unless: -> { draft? } + validates :time_of_day, inclusion: { in: time_of_days.keys }, unless: :draft? on_wizard_step :location, exact: true do validates :location_id, presence: true @@ -98,10 +94,6 @@ class Session < ApplicationRecord if: -> { close_consent_on == "custom" } end - def draft? - !active - end - def health_questions campaign.vaccines.first.health_questions end diff --git a/spec/factories/patient_sessions.rb b/spec/factories/patient_sessions.rb index 38bd45e47d..39303fcce6 100644 --- a/spec/factories/patient_sessions.rb +++ b/spec/factories/patient_sessions.rb @@ -33,7 +33,7 @@ patient { association :patient, session:, **patient_attributes } created_by { association :user } - active { session.active? } + active { session.active } trait :active do active { true }