Skip to content

Commit d31b7ab

Browse files
committed
Backend changes to allow disabling ranked choice on a per-round basis
1 parent 4c3519f commit d31b7ab

12 files changed

Lines changed: 164 additions & 7 deletions
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Types::SignupRoundAutomationAction < Types::BaseEnum
2+
description "An action to take when a signup round opens."
3+
4+
value "EXECUTE_RANKED_CHOICE",
5+
"Execute any pending ranked choices as allowed by this signup round",
6+
value: "execute_ranked_choice"
7+
end

app/graphql/types/signup_round_type.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ class SignupRoundType < Types::BaseObject
1010
signup automation.
1111
MARKDOWN
1212

13+
field :automation_action, Types::SignupRoundAutomationAction, null: true do
14+
description "The action to take when this signup round opens."
15+
end
1316
field :convention, Types::ConventionType, null: false, description: "The convention this SignupRound is in."
1417
field :created_at, Types::DateType, null: false, description: "When this SignupRound was first created."
1518
field :executed_at, Types::DateType do

app/javascript/enumTypes.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,23 @@
951951
}
952952
]
953953
},
954+
"SignupRoundAutomationAction": {
955+
"kind": "ENUM",
956+
"name": "SignupRoundAutomationAction",
957+
"description": "An action to take when a signup round opens.",
958+
"interfaces": null,
959+
"possibleTypes": null,
960+
"fields": null,
961+
"inputFields": null,
962+
"enumValues": [
963+
{
964+
"name": "EXECUTE_RANKED_CHOICE",
965+
"description": "Execute any pending ranked choices as allowed by this signup round",
966+
"isDeprecated": false,
967+
"deprecationReason": null
968+
}
969+
]
970+
},
954971
"SignupState": {
955972
"kind": "ENUM",
956973
"name": "SignupState",

app/javascript/graphqlTypes.generated.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5821,6 +5821,8 @@ export type SignupRequestsPagination = PaginationInterface & {
58215821
*/
58225822
export type SignupRound = {
58235823
__typename: 'SignupRound';
5824+
/** The action to take when this signup round opens. */
5825+
automation_action?: Maybe<SignupRoundAutomationAction>;
58245826
/** The convention this SignupRound is in. */
58255827
convention: Convention;
58265828
/** When this SignupRound was first created. */
@@ -5861,6 +5863,12 @@ export type SignupRoundRanked_Choice_Decisions_PaginatedArgs = {
58615863
sort?: InputMaybe<Array<SortInput>>;
58625864
};
58635865

5866+
/** An action to take when a signup round opens. */
5867+
export enum SignupRoundAutomationAction {
5868+
/** Execute any pending ranked choices as allowed by this signup round */
5869+
ExecuteRankedChoice = 'EXECUTE_RANKED_CHOICE'
5870+
}
5871+
58645872
/** An input for creating or modifying SignupRounds. */
58655873
export type SignupRoundInput = {
58665874
/** The maximum number of signups allowed during this signup round */

app/models/signup_round.rb

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Table name: signup_rounds
55
#
66
# id :bigint not null, primary key
7+
# automation_action :text
78
# executed_at :datetime
89
# maximum_event_signups :text not null
910
# ranked_choice_order :text
@@ -23,13 +24,16 @@
2324
# rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective
2425

2526
class SignupRound < ApplicationRecord
27+
AUTOMATION_ACTIONS = Types::SignupRoundAutomationAction.values.values.map(&:value).freeze
2628
RANKED_CHOICE_ORDERS = Types::RankedChoiceOrder.values.values.map(&:value).freeze
2729

2830
belongs_to :convention
2931
has_many :ranked_choice_decisions, dependent: :restrict_with_exception
3032

33+
validates :automation_action, inclusion: { in: AUTOMATION_ACTIONS, allow_nil: true }
3134
validates :ranked_choice_order, inclusion: { in: RANKED_CHOICE_ORDERS, allow_nil: true }
3235
validates :start, { uniqueness: { scope: :convention_id } }
36+
validate :automation_action_must_be_allowed
3337

3438
scope :due_at, ->(time) { where("executed_at IS NULL AND (start IS NULL OR start <= ?)", time) }
3539
scope :currently_due, -> { due_at(Time.zone.now) }
@@ -42,7 +46,7 @@ def self.execute_currently_due_rounds!
4246

4347
def execute!
4448
transaction do
45-
if convention.signup_automation_mode == "ranked_choice"
49+
if convention.signup_automation_mode == "ranked_choice" && automation_action == "execute_ranked_choice"
4650
ExecuteRankedChoiceSignupRoundService.new(signup_round: self, whodunit: nil).call!
4751
end
4852

@@ -69,4 +73,15 @@ def serpentine_ranked_choice_order?
6973
false
7074
end
7175
end
76+
77+
private
78+
79+
def automation_action_must_be_allowed
80+
return unless automation_action
81+
82+
return unless automation_action == "execute_ranked_choice" && convention.signup_automation_mode != "ranked_choice"
83+
errors.add :base,
84+
"The execute_ranked_choice action is only allowed in conventions that use the ranked_choice signup \
85+
automation mode."
86+
end
7287
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class AddAutomationActionToSignupRounds < ActiveRecord::Migration[8.0]
2+
def change
3+
add_column :signup_rounds, :automation_action, :text
4+
5+
reversible { |dir| dir.up { execute <<~SQL } }
6+
UPDATE signup_rounds SET automation_action = 'execute_ranked_choice'
7+
FROM conventions
8+
WHERE signup_rounds.convention_id = conventions.id
9+
AND conventions.signup_automation_mode = 'ranked_choice'
10+
SQL
11+
end
12+
end

db/structure.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
SET statement_timeout = 0;
22
SET lock_timeout = 0;
33
SET idle_in_transaction_session_timeout = 0;
4+
SET transaction_timeout = 0;
45
SET client_encoding = 'UTF8';
56
SET standard_conforming_strings = on;
67
SELECT pg_catalog.set_config('search_path', '', false);
@@ -9,6 +10,13 @@ SET xmloption = content;
910
SET client_min_messages = warning;
1011
SET row_security = off;
1112

13+
--
14+
-- Name: public; Type: SCHEMA; Schema: -; Owner: -
15+
--
16+
17+
-- *not* creating schema, since initdb creates it
18+
19+
1220
--
1321
-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: -
1422
--
@@ -2656,6 +2664,7 @@ CREATE TABLE public.signup_rounds (
26562664
updated_at timestamp(6) without time zone NOT NULL,
26572665
ranked_choice_order text,
26582666
executed_at timestamp without time zone,
2667+
automation_action text,
26592668
CONSTRAINT chk_rails_4c92d587c4 CHECK (((maximum_event_signups = ANY (ARRAY['not_yet'::text, 'not_now'::text, 'unlimited'::text])) OR ((maximum_event_signups)::integer >= 1)))
26602669
);
26612670

@@ -6125,6 +6134,7 @@ ALTER TABLE ONLY public.cms_files_pages
61256134
SET search_path TO "$user", public;
61266135

61276136
INSERT INTO "schema_migrations" (version) VALUES
6137+
('20250906190727'),
61286138
('20250324175507'),
61296139
('20250324172627'),
61306140
('20250203173940'),

schema.graphql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7439,6 +7439,11 @@ In conventions that use automated signups (e.g. ranked-choice signups), signup r
74397439
signup automation.
74407440
"""
74417441
type SignupRound {
7442+
"""
7443+
The action to take when this signup round opens.
7444+
"""
7445+
automation_action: SignupRoundAutomationAction
7446+
74427447
"""
74437448
The convention this SignupRound is in.
74447449
"""
@@ -7506,6 +7511,16 @@ type SignupRound {
75067511
updated_at: Date!
75077512
}
75087513

7514+
"""
7515+
An action to take when a signup round opens.
7516+
"""
7517+
enum SignupRoundAutomationAction {
7518+
"""
7519+
Execute any pending ranked choices as allowed by this signup round
7520+
"""
7521+
EXECUTE_RANKED_CHOICE
7522+
}
7523+
75097524
"""
75107525
An input for creating or modifying SignupRounds.
75117526
"""

schema.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33114,6 +33114,18 @@
3311433114
"interfaces": [],
3311533115
"possibleTypes": null,
3311633116
"fields": [
33117+
{
33118+
"name": "automation_action",
33119+
"description": "The action to take when this signup round opens.",
33120+
"type": {
33121+
"kind": "ENUM",
33122+
"name": "SignupRoundAutomationAction",
33123+
"ofType": null
33124+
},
33125+
"isDeprecated": false,
33126+
"deprecationReason": null,
33127+
"args": []
33128+
},
3311733129
{
3311833130
"name": "convention",
3311933131
"description": "The convention this SignupRound is in.",
@@ -33307,6 +33319,23 @@
3330733319
"inputFields": null,
3330833320
"enumValues": null
3330933321
},
33322+
{
33323+
"kind": "ENUM",
33324+
"name": "SignupRoundAutomationAction",
33325+
"description": "An action to take when a signup round opens.",
33326+
"interfaces": null,
33327+
"possibleTypes": null,
33328+
"fields": null,
33329+
"inputFields": null,
33330+
"enumValues": [
33331+
{
33332+
"name": "EXECUTE_RANKED_CHOICE",
33333+
"description": "Execute any pending ranked choices as allowed by this signup round",
33334+
"isDeprecated": false,
33335+
"deprecationReason": null
33336+
}
33337+
]
33338+
},
3331033339
{
3331133340
"kind": "INPUT_OBJECT",
3331233341
"name": "SignupRoundInput",

test/factories/root_sites.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
FactoryBot.define do
2424
factory :root_site do
2525
site_name { "The Root Site" }
26-
association :root_page, factory: :page
27-
association :default_layout, factory: :cms_layout
26+
root_page factory: :page
27+
default_layout factory: :cms_layout
2828
end
2929
end

0 commit comments

Comments
 (0)