diff --git a/app/controllers/participants_controller.rb b/app/controllers/participants_controller.rb index d8b47272..8f0de7a3 100644 --- a/app/controllers/participants_controller.rb +++ b/app/controllers/participants_controller.rb @@ -4,6 +4,7 @@ class ParticipantsController < ApplicationController respond_to :html load_resource except: :confirm_email before_action :verify_owner, :only => [:edit, :update] + before_action :coc_agreement_param_as_timestamp, only: [:create, :update] def index respond_to do |format| @@ -40,7 +41,6 @@ def update end if @participant.update(new_params) - create_code_of_conduct_agreement_if_not_exists! flash[:notice] = "Profile updated successfully." redirect_to participant_path(@participant) else @@ -52,7 +52,6 @@ def update def create @participant.attributes = participant_params.except(:code_of_conduct_agreement) if @participant.save - create_code_of_conduct_agreement_if_not_exists! @participant.deliver_email_confirmation_instructions! flash[:notice] = "Thanks for registering an account. Please check your email to confirm your account." redirect_to root_path @@ -62,15 +61,6 @@ def create end end - def create_code_of_conduct_agreement_if_not_exists! - if participant_params[:code_of_conduct_agreement] == '1' && @participant.signed_code_of_conduct_for_current_event? == false - CodeOfConductAgreement.create!({ - participant_id: @participant.id, - event_id: Event.current_event.id, - }) - end - end - def send_confirmation_email @participant.deliver_email_confirmation_instructions! flash[:notice] = "Confirmation instructions sent! Please check your email." @@ -95,11 +85,17 @@ def participant_params :name, :email, :password, :bio, :code_of_conduct_agreement, - :contact_details + :contact_details, :coc_agreed_at ) end def verify_owner redirect_to participant_path(@participant) if @participant != current_participant end + + def coc_agreement_param_as_timestamp + if params[:participant][:code_of_conduct_agreement] == '1' + params[:participant][:coc_agreed_at] = Time.current + end + end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 6068295c..2880a638 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -78,11 +78,8 @@ def create end def create_code_of_conduct_agreement_if_not_exists! - if session_params[:code_of_conduct_agreement] == '1' && @session.participant.signed_code_of_conduct_for_current_event? == false - CodeOfConductAgreement.create!({ - participant_id: @session.participant.id, - event_id: Event.current_event.id, - }) + if session_params[:code_of_conduct_agreement] == '1' && !@session.participant.signed_code_of_conduct_for_current_event? + current_participant.update!(coc_agreed_at: Time.current) end end @@ -139,5 +136,4 @@ def sessions_for_event(event) .order('created_at desc') .distinct end - end diff --git a/app/models/code_of_conduct_agreement.rb b/app/models/code_of_conduct_agreement.rb deleted file mode 100644 index c8c8baf8..00000000 --- a/app/models/code_of_conduct_agreement.rb +++ /dev/null @@ -1,4 +0,0 @@ -class CodeOfConductAgreement < ActiveRecord::Base - belongs_to :participant - belongs_to :event -end diff --git a/app/models/event.rb b/app/models/event.rb index 5ea49b94..563bc60e 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -9,7 +9,6 @@ class NotEnoughRoomsError < StandardError; end has_many :categories, through: :event_categories has_many :presenter_timeslot_restrictions, :through => :timeslots - has_many :code_of_conduct_agreements, dependent: :destroy # Careful! Large joins here; use with caution: has_many :attendances, through: :sessions diff --git a/app/models/participant.rb b/app/models/participant.rb index 724e52dd..aa0a1d74 100644 --- a/app/models/participant.rb +++ b/app/models/participant.rb @@ -5,7 +5,6 @@ class Participant < ActiveRecord::Base has_many :presentations has_many :sessions_presenting, :through => :presentations, :source => :session has_many :presenter_timeslot_restrictions, dependent: :destroy - has_many :code_of_conduct_agreements, dependent: :destroy validates :name, presence: true validates :email, presence: true @@ -82,12 +81,7 @@ def deliver_password_reset_instructions! end def signed_code_of_conduct_for_current_event? - return false unless Event.current_event - - CodeOfConductAgreement.where({ - participant_id: id, - event_id: Event.current_event.id, - }).exists? + coc_agreed_at.present? end def attending_session?(session) diff --git a/db/migrate/20260520012517_add_coc_agreed_at_to_participants.rb b/db/migrate/20260520012517_add_coc_agreed_at_to_participants.rb new file mode 100644 index 00000000..6130eae1 --- /dev/null +++ b/db/migrate/20260520012517_add_coc_agreed_at_to_participants.rb @@ -0,0 +1,5 @@ +class AddCocAgreedAtToParticipants < ActiveRecord::Migration[7.2] + def change + add_column :participants, :coc_agreed_at, :datetime + end +end diff --git a/db/migrate/20260521194947_drop_code_of_conduct_agreements.rb b/db/migrate/20260521194947_drop_code_of_conduct_agreements.rb new file mode 100644 index 00000000..72a04ae3 --- /dev/null +++ b/db/migrate/20260521194947_drop_code_of_conduct_agreements.rb @@ -0,0 +1,16 @@ +class DropCodeOfConductAgreements < ActiveRecord::Migration[7.2] + def up + drop_table :code_of_conduct_agreements + end + + def down + create_table :code_of_conduct_agreements do |t| + t.bigint :participant_id, null: false + t.bigint :event_id, null: false + t.timestamps precision: nil, null: false + end + + add_index :code_of_conduct_agreements, :event_id + add_index :code_of_conduct_agreements, :participant_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 0e322fbb..442fc0e4 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: 2026_03_22_192507) do +ActiveRecord::Schema[7.2].define(version: 2026_05_21_194947) do create_schema "heroku_ext" # These are extensions that must be enabled in order to support this database @@ -55,15 +55,6 @@ t.index ["category_id", "session_id"], name: "index_categorizations_on_category_id_and_session_id", unique: true end - create_table "code_of_conduct_agreements", force: :cascade do |t| - t.bigint "participant_id", null: false - t.bigint "event_id", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["event_id"], name: "index_code_of_conduct_agreements_on_event_id" - t.index ["participant_id"], name: "index_code_of_conduct_agreements_on_participant_id" - end - create_table "event_categories", force: :cascade do |t| t.bigint "event_id", null: false t.bigint "category_id", null: false @@ -114,6 +105,7 @@ t.datetime "email_confirmed_at", precision: nil t.integer "presentations_count", default: 0 t.integer "attendances_count", default: 0 + t.datetime "coc_agreed_at" t.index ["email"], name: "index_participants_on_email", unique: true t.index ["perishable_token"], name: "index_participants_on_perishable_token" end diff --git a/lib/tasks/app.rake b/lib/tasks/app.rake index aae3860d..5f8533ac 100644 --- a/lib/tasks/app.rake +++ b/lib/tasks/app.rake @@ -517,6 +517,7 @@ namespace :app do participant.email = FFaker::Internet.safe_email participant.password = 'password' participant.bio = FFaker::Lorem.paragraph if [true, false].sample + participant.coc_agreed_at = Time.current participant.save! progress.increment end diff --git a/lib/tasks/backfill.rake b/lib/tasks/backfill.rake new file mode 100644 index 00000000..a7c6af99 --- /dev/null +++ b/lib/tasks/backfill.rake @@ -0,0 +1,16 @@ +namespace :backfill do + + desc 'backfill coc_agreed_at for participants who have already accepted' + task coc_agreed_at: :environment do + ActiveRecord::Base.connection.execute(<<~SQL) + UPDATE participants + SET coc_agreed_at = coc.created_at + FROM ( + SELECT DISTINCT ON (participant_id) participant_id, created_at + FROM code_of_conduct_agreements + ) AS coc + WHERE participants.id = coc.participant_id + AND participants.coc_agreed_at IS NULL + SQL + end +end \ No newline at end of file diff --git a/spec/controllers/participants_controller_spec.rb b/spec/controllers/participants_controller_spec.rb index 3633eea5..0425f31d 100644 --- a/spec/controllers/participants_controller_spec.rb +++ b/spec/controllers/participants_controller_spec.rb @@ -45,6 +45,25 @@ expect(response).to render_template(:new) expect(flash[:error]).to eq "There was a problem creating your account." end + + it "does not set coc_agreed_at on the participant when the user has not signed the code of conduct" do + post :create, params: { participant: { name: 'Alan Turing', + email: 'tapewriter@example.org', + password: 'infinite-memory', + code_of_conduct_agreement: '0' + } + } + expect(Participant.find_by(email: 'tapewriter@example.org').coc_agreed_at).to be_nil + end + it "sets coc_agreed_at on the participant when the user has signed the code of conduct" do + post :create, params: { participant: { name: 'Alan Turing', + email: 'tapewriter@example.org', + password: 'infinite-memory', + code_of_conduct_agreement: '1' + } + } + expect(Participant.find_by(email: 'tapewriter@example.org').coc_agreed_at).not_to be_nil + end end describe "#show" do @@ -137,6 +156,14 @@ expect(response).to redirect_to participant_path(joe) expect(joe.reload.name).to eq 'schmoe, joe' end + it "does not set coc_agreed_at on the participant when the user has not signed the code of conduct" do + put :update, params: { id: joe, participant: { name: 'Alan Turing', code_of_conduct_agreement: '0' } } + expect(joe.reload.coc_agreed_at).to be_nil + end + it "sets coc_agreed_at on the participant when the user has signed the code of conduct" do + put :update, params: { id: joe, participant: { code_of_conduct_agreement: '1' } } + expect(joe.reload.coc_agreed_at).not_to be_nil + end describe "more attributes are not required" do it "should be successful" do diff --git a/spec/controllers/presentations_controller_spec.rb b/spec/controllers/presentations_controller_spec.rb index d2493c12..3fe11228 100644 --- a/spec/controllers/presentations_controller_spec.rb +++ b/spec/controllers/presentations_controller_spec.rb @@ -20,10 +20,7 @@ context "when the user is found by id" do it "should be successful when the user has signed the code of conduct" do - CodeOfConductAgreement.create!({ - participant_id: participant.id, - event_id: Event.current_event.id, - }) + participant.update!(coc_agreed_at: Time.current) expect { post :create, params: { session_id: session, id: participant.id } @@ -41,10 +38,7 @@ context "when the user is found by name" do it "should be successful when the user has signed the code of conduct" do - CodeOfConductAgreement.create!({ - participant_id: participant.id, - event_id: Event.current_event.id, - }) + participant.update!(coc_agreed_at: Time.current) expect { post :create, params: { session_id: session, name: participant.name } diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 13b19981..801353e0 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -138,6 +138,29 @@ expect(assigns[:session].category_ids).to include category.id expect(flash[:notice]).to eq "Thanks for adding your session." end + it "should sign code of conduct if param is present" do + expect { + post :create, params: { session: { title: "new title", + description: "new description", + category_ids: [category.id], + level_id: "2", + code_of_conduct_agreement: "1", + } + } + }.to change { Session.count }.by(1) + expect(user.reload.coc_agreed_at).not_to be_nil + end + it "should not sign code of conduct if param is not present" do + expect { + post :create, params: { session: { title: "new title", + description: "new description", + category_ids: [category.id], + level_id: "2", + } + } + }.to change { Session.count }.by(1) + expect(user.reload.coc_agreed_at).to be_nil + end end context "with invalid values" do