Skip to content

Commit b1b4278

Browse files
authored
Merge pull request #94 from ruby-no-kai/create_broadcast_deliveries_job_fix_filters
fix recipient filters
2 parents a094a8f + 47fea09 commit b1b4278

File tree

14 files changed

+406
-69
lines changed

14 files changed

+406
-69
lines changed

Gemfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ gem 'simpacker'
3232

3333
gem 'premailer-rails'
3434

35-
gem 'letter_opener_web', git: 'https://github.com/fgrehm/letter_opener_web', ref: 'ab50ad09a2af5350bdca9c079bba73523e64f4cd' # https://github.com/fgrehm/letter_opener_web/pull/83
36-
gem 'rspec-rails'
37-
3835
gem 'revision_plate'
3936
gem "sentry-ruby"
4037
gem "sentry-rails"
@@ -48,6 +45,10 @@ end
4845
group :development, :test do
4946
# gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
5047
gem 'listen'
48+
49+
gem 'letter_opener_web', git: 'https://github.com/fgrehm/letter_opener_web', ref: 'ab50ad09a2af5350bdca9c079bba73523e64f4cd' # https://github.com/fgrehm/letter_opener_web/pull/83
50+
gem 'rspec-rails'
51+
gem 'factory_bot_rails'
5152
end
5253

5354
group :development do

Gemfile.lock

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ GEM
124124
diff-lcs (1.5.1)
125125
drb (2.2.1)
126126
erubi (1.13.0)
127+
factory_bot (6.5.1)
128+
activesupport (>= 6.1.0)
129+
factory_bot_rails (6.4.4)
130+
factory_bot (~> 6.5)
131+
railties (>= 5.0.0)
127132
faraday (1.10.4)
128133
faraday-em_http (~> 1.0)
129134
faraday-em_synchrony (~> 1.0)
@@ -370,6 +375,7 @@ DEPENDENCIES
370375
barnes
371376
commonmarker
372377
connection_pool
378+
factory_bot_rails
373379
faraday
374380
faraday_middleware
375381
haml

app/jobs/create_broadcast_deliveries_job.rb

Lines changed: 126 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,125 @@
11
class CreateBroadcastDeliveriesJob < ApplicationJob
22
Recipient = Struct.new(:sponsorship, :email, :email_ccs, keyword_init: true)
33

4+
module Filters
5+
class Base
6+
def initialize(broadcast:, params:)
7+
@broadcast = broadcast
8+
@params = params
9+
end
10+
11+
attr_reader :broadcast, :params
12+
13+
def recipients
14+
raise NotImplementedError
15+
end
16+
17+
private def status_scope
18+
case params[:status]
19+
when 'all', nil
20+
Sponsorship.all
21+
when 'not_accepted'
22+
Sponsorship.not_accepted
23+
when 'pending'
24+
Sponsorship.pending
25+
when 'accepted'
26+
Sponsorship.accepted
27+
when 'active'
28+
Sponsorship.active
29+
when 'withdrawn'
30+
Sponsorship.withdrawn
31+
else
32+
Sponsorship.none
33+
end
34+
end
35+
36+
private def plan_scope
37+
if params[:plan_id].present?
38+
plan = @broadcast.conference.plans.where(id: params[:plan_id]).first
39+
if plan
40+
return Sponsorship.where(plan:)
41+
end
42+
end
43+
nil
44+
end
45+
46+
private def locale_scope
47+
if params[:locale].present?
48+
Sponsorship.where(locale: params[:locale])
49+
else
50+
nil
51+
end
52+
end
53+
54+
private def exhibitor_scope
55+
if params[:exhibitors].present?
56+
Sponsorship.exhibitor
57+
else
58+
nil
59+
end
60+
end
61+
62+
private def scope_sponsorships(scope)
63+
[
64+
status_scope,
65+
plan_scope,
66+
locale_scope,
67+
exhibitor_scope,
68+
].inject(scope) do |r,i|
69+
i ? r.merge(i) : r
70+
end
71+
end
72+
73+
private def sponsorships_to_recipients(scope)
74+
scope.map do |sponsorship|
75+
Recipient.new(
76+
sponsorship: sponsorship,
77+
email: sponsorship.contact.email,
78+
email_ccs: sponsorship.contact.email_ccs,
79+
)
80+
end
81+
end
82+
end
83+
84+
class All < Base
85+
def recipients
86+
scope = scope_sponsorships(@broadcast.conference.sponsorships.includes(:contact))
87+
sponsorships_to_recipients(scope)
88+
end
89+
end
90+
91+
class PastSponsors < Base
92+
def recipients
93+
conference = Conference.find_by!(id: params[:id])
94+
scope = scope_sponsorships(conference.sponsorships.includes(:contact))
95+
sponsorships_to_recipients(scope)
96+
end
97+
end
98+
99+
class Manual < Base
100+
def recipients
101+
scope = Sponsorship.where(id: [*params[:sponsorship_ids]])
102+
if params[:exclude_current_sponsors].present?
103+
scope = scope.where.not(organization_id: @broadcast.conference.sponsorships.pluck(:organization_id))
104+
end
105+
sponsorships_to_recipients(scope)
106+
end
107+
end
108+
109+
class Raw < Base
110+
def recipients
111+
[*params[:emails]].flatten.flat_map do |email_lines|
112+
email_lines.to_s.each_line.map do |email|
113+
Recipient.new(
114+
email: email.chomp,
115+
email_ccs: [],
116+
)
117+
end
118+
end
119+
end
120+
end
121+
end
122+
4123
def perform(broadcast, recipient_filter_params)
5124
ApplicationRecord.transaction do
6125
@broadcast = broadcast
@@ -17,16 +136,16 @@ def perform(broadcast, recipient_filter_params)
17136
raise "Invalid state for CreateBroadcastDeliveriesJob (broadcast_id=#{broadcast.id}, state=#{broadcast.status})"
18137
end
19138

20-
existing_emails = broadcast.deliveries.pluck(:recipient)
139+
existing_emails = broadcast.deliveries.pluck(:recipient).tally
21140
recipients = recipient_filter_params.flat_map do |filter|
22141
filter = filter.dup
23-
kind = filter.delete('kind') || target.delete(:kind)
142+
kind = filter.delete('kind')
24143

25144
filter_recipients(kind, filter)
26145
end
27146

28147
recipients.map do |recipient|
29-
next if existing_emails.include?(recipient.email)
148+
next if existing_emails[recipient.email]
30149
broadcast.deliveries.create!(
31150
status: :ready,
32151
sponsorship: recipient.sponsorship,
@@ -45,65 +164,13 @@ def perform(broadcast, recipient_filter_params)
45164
def filter_recipients(kind, params)
46165
case kind.to_s
47166
when 'all'
48-
scope = @broadcast.conference.sponsorships.not_withdrawn.includes(:contact)
49-
scope = scope.where(plan: @broadcast.conference.plans.find_by!(id: params[:plan_id])) if params[:plan_id].present?
50-
scope = scope.where(locale: params[:locale]) if params[:locale].present?
51-
scope = scope.exhibitor if params[:exhibitors].present?
52-
case params[:status]
53-
when 'all'
54-
# do nothing
55-
when 'not_accepted'
56-
scope = scope.where(accepted_at: nil)
57-
when 'accepted'
58-
scope = scope.accepted
59-
end
60-
61-
scope.map do |sponsorship|
62-
Recipient.new(
63-
sponsorship: sponsorship,
64-
email: sponsorship.contact.email,
65-
email_ccs: sponsorship.contact.email_ccs,
66-
)
67-
end
167+
Filters::All.new(broadcast: @broadcast, params: params).recipients
68168
when 'past_sponsors'
69-
conference = Conference.find_by!(id: params[:id])
70-
scope = conference.sponsorships.not_withdrawn.includes(:contact)
71-
scope = scope.where(locale: params[:locale]) if params[:locale].present?
72-
scope = scope.where.not(organization_id: @broadcast.conference.sponsorships.pluck(:organization_id)) if params[:exclude_current_sponsors]
73-
case params[:status]
74-
when 'all'
75-
# do nothing
76-
when 'not_accepted'
77-
scope = scope.where(accepted_at: nil)
78-
when 'accepted'
79-
scope = scope.accepted
80-
end
81-
82-
scope.map do |sponsorship|
83-
Recipient.new(
84-
sponsorship: sponsorship,
85-
email: sponsorship.contact.email,
86-
email_ccs: sponsorship.contact.email_ccs,
87-
)
88-
end
169+
Filters::PastSponsors.new(broadcast: @broadcast, params: params).recipients
89170
when 'raw'
90-
[*params[:emails]].flatten.flat_map do |email_lines|
91-
email_lines.to_s.each_line.map do |email|
92-
Recipient.new(
93-
email: email.chomp,
94-
email_ccs: [],
95-
)
96-
end
97-
end
171+
Filters::Raw.new(broadcast: @broadcast, params: params).recipients
98172
when 'manual'
99-
scope = Sponsorship.where(id: [*params[:sponsorship_ids]])
100-
scope.map do |sponsorship|
101-
Recipient.new(
102-
sponsorship: sponsorship,
103-
email: sponsorship.contact.email,
104-
email_ccs: sponsorship.contact.email_ccs,
105-
)
106-
end
173+
Filters::Manual.new(broadcast: @broadcast, params: params).recipients
107174
when 'none'
108175
[]
109176
else

app/models/sponsorship.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,17 @@ def tito_booth_staff_discount_code
4747
@tito_booth_staff_discount_code ||= tito_discount_codes.where(kind: 'booth_staff').first
4848
end
4949

50-
scope :active, -> { where(withdrawn_at: nil).where.not(accepted_at: nil) }
50+
scope :active, -> { accepted.not_withdrawn }
51+
scope :pending, -> { not_accepted.not_withdrawn }
52+
5153
scope :exhibitor, -> { where(booth_assigned: true) }
5254
scope :plan_determined, -> { where.not(plan_id: nil) }
55+
5356
scope :withdrawn, -> { where.not(withdrawn_at: nil) }
5457
scope :not_withdrawn, -> { where(withdrawn_at: nil) }
5558
scope :accepted, -> { where.not(accepted_at: nil) }
59+
scope :not_accepted, -> { where(accepted_at: nil) }
60+
5661
scope :have_presence, -> { where(suspended: false).merge(Sponsorship.active).merge(Sponsorship.plan_determined) }
5762

5863
scope :includes_contacts, -> { includes(:contact, :alternate_billing_contact) }

app/views/admin/broadcasts/_new_recipient_fields.html.haml

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,22 @@
2020
.form-check
2121
%label.form-check-label
2222
= radio_button_tag "recipient_filter[status]", 'all', true, {class: 'form-check-input'}
23-
All applications (where not withdrawn)
23+
All
24+
%label.form-check-label
25+
= radio_button_tag "recipient_filter[status]", 'pending', false, {class: 'form-check-input'}
26+
Pending
27+
%label.form-check-label
28+
= radio_button_tag "recipient_filter[status]", 'active', false, {class: 'form-check-input'}
29+
Active
2430
%label.form-check-label
2531
= radio_button_tag "recipient_filter[status]", 'not_accepted', false, {class: 'form-check-input'}
26-
Not accepted only
32+
Not accepted (incl. withdrawn)
2733
%label.form-check-label
2834
= radio_button_tag "recipient_filter[status]", 'accepted', false, {class: 'form-check-input'}
29-
Accepted only
35+
Accepted (incl. withdrawn)
36+
%label.form-check-label
37+
= radio_button_tag "recipient_filter[status]", 'withdrawn', false, {class: 'form-check-input'}
38+
Withdrawn
3039

3140
%fieldset.broadcast_new_recipient_fields_kind__past_sponsors.d-none{disabled: true}
3241
.form-group
@@ -42,13 +51,24 @@
4251
.form-check
4352
%label.form-check-label
4453
= radio_button_tag "recipient_filter[status]", 'all', true, {class: 'form-check-input'}
45-
All applications (where not withdrawn)
54+
All
55+
%label.form-check-label
56+
= radio_button_tag "recipient_filter[status]", 'pending', false, {class: 'form-check-input'}
57+
Pending
58+
%label.form-check-label
59+
= radio_button_tag "recipient_filter[status]", 'active', false, {class: 'form-check-input'}
60+
Active
4661
%label.form-check-label
4762
= radio_button_tag "recipient_filter[status]", 'not_accepted', false, {class: 'form-check-input'}
48-
Not accepted only
63+
Not accepted (incl. withdrawn)
4964
%label.form-check-label
5065
= radio_button_tag "recipient_filter[status]", 'accepted', false, {class: 'form-check-input'}
51-
Accepted only
66+
Accepted (incl. withdrawn)
67+
%label.form-check-label
68+
= radio_button_tag "recipient_filter[status]", 'withdrawn', false, {class: 'form-check-input'}
69+
Withdrawn
70+
71+
5272

5373
%fieldset.broadcast_new_recipient_fields_kind__manual.d-none{disabled: true}
5474
.form-group

spec/factories/broadcasts.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FactoryBot.define do
2+
factory :broadcast do
3+
conference
4+
staff
5+
status { :created }
6+
sequence(:campaign) { |n| "Campaign #{n}" }
7+
sequence(:description) { |n| "#{n}" }
8+
sequence(:title) { |n| "Email #{n}" }
9+
sequence(:body) { |n| "- Email #{n}" }
10+
end
11+
end

spec/factories/conferences.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FactoryBot.define do
2+
factory :conference do
3+
sequence(:name) { |n| "Conf #{n}" }
4+
sequence(:slug) { |n| "conf#{n}" }
5+
booth_capacity { 50 }
6+
sequence(:contact_email_address) { |n| "info+#{n}@conf.test.invalid" }
7+
8+
trait :full do
9+
after(:create) do |conference, context|
10+
create(:form_description, conference:)
11+
create(:plan, conference:)
12+
end
13+
end
14+
end
15+
end

spec/factories/contacts.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FactoryBot.define do
2+
factory :contact do
3+
#sponsorship
4+
kind { "primary" }
5+
sequence(:email) { |n| "primary@#{n}.co.invalid" }
6+
sequence(:address) { |n| "#{n}-#{n}-#{n}" }
7+
sequence(:organization) { |n| "Contoso" }
8+
sequence(:name) { |n| "User #{n}" }
9+
end
10+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FactoryBot.define do
2+
factory :form_description do
3+
conference
4+
locale { "en" }
5+
head { "- Head #{locale} #{conference.id}" }
6+
plan_help { "- Plan #{locale} #{conference.id}" }
7+
booth_help { "- Booth #{locale} #{conference.id}" }
8+
policy_help { "- Policy #{locale} #{conference.id}" }
9+
ticket_help { "- Ticket #{locale} #{conference.id}" }
10+
end
11+
end

spec/factories/plans.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FactoryBot.define do
2+
factory :plan do
3+
conference
4+
sequence(:name) { |n| "Plan #{n}" }
5+
sequence(:rank) { |n| n }
6+
summary { "na" }
7+
capacity { 10 }
8+
number_of_guests { 1 }
9+
price_text { "na" }
10+
words_limit { 15 }
11+
auto_acceptance { true }
12+
end
13+
end

0 commit comments

Comments
 (0)