hackathon_manager 0.5.11 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec278de1d0233b9b79cc1eae32b6b77bbcbde698
4
- data.tar.gz: e28e82e40163c942d4c876647fcee18ffa54b0d7
3
+ metadata.gz: 7b2b4e0940990bd9d79779dc11df2261481e953a
4
+ data.tar.gz: 231e89a71e42dd25ed692d41c2bf285dbee54962
5
5
  SHA512:
6
- metadata.gz: 698c9f2095e0148ab36d8d2c0e93888c4318e252d2529e5c077d5342e11b64503faa5d64ec2a782dfa4725ffa6af249f9e31b2da3e15a7e3304b1493aabcd840
7
- data.tar.gz: c58b013667063273e696bee1cad99f8d7f75c205ac814b2ac55564b82849066b79d25b9f586252fd5f39f0c18696082e24c5d8db304142f38a0308764a935765
6
+ metadata.gz: eeb9342f9cd545f46932c0330154888d8cc549f26199b19d5c96f31a47d4503ba801aadf4913d6e2908ff947841ddb39158306aa0eda9b3dca2bdb572774542a
7
+ data.tar.gz: c17ae7672d988fe083cce647d3c1e4e0edb4005961a433ab803d0d7f9af8f6adfdca2a260a7390a2b432e19a9f6b7d8d6243cc81666180c35425acd999f40bea
@@ -72,6 +72,7 @@ var setupDataTables = function() {
72
72
  { orderable: true, data: 'id'},
73
73
  { orderable: true, data: 'name'},
74
74
  { orderable: true, data: 'subject'},
75
+ { orderable: true, data: 'trigger'},
75
76
  { orderable: false, data: 'status'},
76
77
  { orderable: true, data: 'delivered_at'}
77
78
  ]
@@ -4,3 +4,6 @@
4
4
  .session-link
5
5
  font-weight: bold
6
6
  text-transform: uppercase
7
+
8
+ .no-margin
9
+ margin: 0
@@ -71,7 +71,7 @@ class Manage::MessagesController < Manage::ApplicationController
71
71
 
72
72
  def message_params
73
73
  params.require(:message).permit(
74
- :name, :subject, :template, :body, recipients: []
74
+ :name, :subject, :template, :body, :trigger, recipients: []
75
75
  )
76
76
  end
77
77
 
@@ -6,6 +6,7 @@ class MessageDatatable < AjaxDatatablesRails::Base
6
6
  id: { source: "Message.id" },
7
7
  name: { source: "Message.name" },
8
8
  subject: { source: "Message.subject" },
9
+ trigger: { source: "Message.trigger" },
9
10
  delivered_at: { source: "Message.delivered_at", searchable: false }
10
11
  }
11
12
  end
@@ -19,6 +20,7 @@ class MessageDatatable < AjaxDatatablesRails::Base
19
20
  id: record.id,
20
21
  name: record.name,
21
22
  subject: record.subject,
23
+ trigger: Message::POSSIBLE_TRIGGERS[record.trigger],
22
24
  status: record.status.titleize,
23
25
  delivered_at: record.delivered_at.present? ? record.delivered_at.strftime("%B %d, %Y at %I:%M %p") : ''
24
26
  }
@@ -1,12 +1,12 @@
1
1
  class Message < ApplicationRecord
2
- validates_presence_of :name, :subject, :recipients, :template
2
+ validates_presence_of :name, :subject, :template
3
3
  validates_presence_of :body, if: :using_default_template?
4
4
 
5
5
  strip_attributes
6
6
 
7
7
  POSSIBLE_TEMPLATES = ["default"].freeze
8
8
 
9
- POSSIBLE_RECIPIENTS = {
9
+ POSSIBLE_SIMPLE_RECIPIENTS = {
10
10
  "all" => "Everyone",
11
11
  "incomplete" => "Incomplete Applications",
12
12
  "complete" => "Complete Applications",
@@ -19,23 +19,18 @@ class Message < ApplicationRecord
19
19
  "checked-in" => "Checked-In Attendees",
20
20
  "non-checked-in" => "Non-Checked-In, Accepted & RSVP'd Applications",
21
21
  "non-checked-in-excluding" => "Non-Checked-In Applications, Excluding Accepted & RSVP'd",
22
- "bus-list-cornell-bing" => "Bus List: Cornell + Binghamton (Confirmed)",
23
- "bus-list-buffalo" => "Bus List: Buffalo (Confirmed)",
24
- "bus-list-albany" => "Bus List: Albany (Confirmed)",
25
- "bus-list-cornell-bing-eligible" => "Bus List: Cornell + Binghamton (eligible, not signed up)",
26
- "bus-list-buffalo-eligible" => "Bus List: Buffalo (eligible, not signed up)",
27
- "bus-list-albany-eligible" => "Bus List: Albany (eligible, not signed up)",
28
- "bus-list-cornell-bing-applied" => "Bus List: Cornell + Binghamton (applied/not accepted)",
29
- "bus-list-buffalo-applied" => "Bus List: Buffalo (applied/not accepted)",
30
- "bus-list-albany-applied" => "Bus List: Albany (applied/not accepted)",
31
- "school-rit" => "Confirmed or accepted: RIT",
32
- "school-cornell" => "Confirmed or accepted: Cornell",
33
- "school-binghamton" => "Confirmed or accepted: Binghamton",
34
- "school-buffalo" => "Confirmed or accepted: Buffalo",
35
- "school-waterloo" => "Confirmed or accepted: Waterloo",
36
- "school-toronto" => "Confirmed or accepted: Toronto",
37
- "school-umd-collegepark" => "Confirmed or accepted: UMD College Park"
38
22
  }.freeze
23
+
24
+ POSSIBLE_TRIGGERS = {
25
+ "questionnaire.pending" => "Questionnaire Status: Pending Review (new application)",
26
+ "questionnaire.accepted" => "Questionnaire Status: Accepted",
27
+ "questionnaire.waitlist" => "Questionnaire Status: Waitlisted",
28
+ "questionnaire.denied" => "Questionnaire Status: Denied",
29
+ "questionnaire.late_waitlist" => "Questionnaire Status: Waitlisted, Late",
30
+ "questionnaire.rsvp_confirmed" => "Questionnaire Status: RSVP Confirmed",
31
+ "questionnaire.rsvp_denied" => "Questionnaire Status: RSVP Denied"
32
+ }.freeze
33
+
39
34
  serialize :recipients, Array
40
35
 
41
36
  validates_inclusion_of :template, in: POSSIBLE_TEMPLATES
@@ -45,7 +40,16 @@ class Message < ApplicationRecord
45
40
  end
46
41
 
47
42
  def recipients_list
48
- recipients.map { |r| POSSIBLE_RECIPIENTS[r] }.join(', ')
43
+ labels = recipients.map do |r|
44
+ if POSSIBLE_SIMPLE_RECIPIENTS.include?(r)
45
+ POSSIBLE_SIMPLE_RECIPIENTS[r]
46
+ elsif r =~ /(.*)::(\d*)/
47
+ MessageRecipientQuery.friendly_name(r)
48
+ else
49
+ "(unknown)"
50
+ end
51
+ end
52
+ labels.join(', ')
49
53
  end
50
54
 
51
55
  def delivered?
@@ -68,10 +72,41 @@ class Message < ApplicationRecord
68
72
  end
69
73
 
70
74
  def can_edit?
71
- status == "drafted"
75
+ status == "drafted" || trigger.present?
72
76
  end
73
77
 
74
78
  def using_default_template?
75
79
  template == "default"
76
80
  end
81
+
82
+ def self.possible_recipients
83
+ # Produce an array like:
84
+ # ["School: My University", "school::123"]
85
+ option = ->(query, model) { [MessageRecipientQuery.friendly_name(query, model), query] }
86
+ bus_list_recipients = BusList.select(:id, :name).map do |bus_list|
87
+ [
88
+ option.call("bus-list::#{bus_list.id}", bus_list),
89
+ option.call("bus-list--eligible::#{bus_list.id}", bus_list),
90
+ option.call("bus-list--applied::#{bus_list.id}", bus_list)
91
+ ]
92
+ end
93
+ bus_list_recipients.flatten!(1) # Required since we have multiple options for each bus list
94
+
95
+ school_recipients = School.select(:id, :name).map do |school|
96
+ option.call("school::#{school.id}", school)
97
+ end
98
+ # No flatten needed here since each map returns a single option
99
+
100
+ # Combine all recipients. push(*recipients) is the most efficient,
101
+ # as it doesn't create a new array each time (concat() does)
102
+ recipients = POSSIBLE_SIMPLE_RECIPIENTS.invert.to_a
103
+ recipients.push(*bus_list_recipients)
104
+ recipients.push(*school_recipients)
105
+ recipients
106
+ end
107
+
108
+ def self.queue_for_trigger(trigger, user_id)
109
+ messages_to_queue = Message.where(trigger: trigger)
110
+ messages_to_queue.map { |message| Mailer.delay.bulk_message_email(message.id, user_id) }
111
+ end
77
112
  end
@@ -0,0 +1,61 @@
1
+ class MessageRecipientQuery
2
+ attr_accessor :query
3
+ attr_accessor :type
4
+ attr_accessor :id
5
+ attr_accessor :model
6
+
7
+ def initialize(query, type, id, model)
8
+ @query = query
9
+ @type = type
10
+ @id = id
11
+ @model = model
12
+ end
13
+
14
+ def self.parse(query, model = nil)
15
+ # Format:
16
+ # model::ID
17
+ # model--modifier::ID
18
+ match = query.match(/(.*)::(\d*)/)
19
+ type = match[1]
20
+ id = match[2]
21
+
22
+ # Find the backing database model, ensuring the given ID exists for that model.
23
+ model_name = nil
24
+ case type
25
+ when "bus-list", "bus-list--applied", "bus-list--eligible"
26
+ model ||= BusList.find_by_id(id)
27
+ model_name = "Bus List"
28
+ when "school"
29
+ model ||= School.find_by_id(id)
30
+ model_name = "School"
31
+ else
32
+ raise "Unknown recipient query type: #{type.inspect} (in message recipient query: #{query.inspect}"
33
+ end
34
+ raise "Could not find #{model_name} with ID #{id.inspect} (in message recipient query: #{query.inspect}" if model.blank?
35
+
36
+ MessageRecipientQuery.new(
37
+ query,
38
+ type,
39
+ id,
40
+ model
41
+ )
42
+ end
43
+
44
+ def self.friendly_name(query, model = nil)
45
+ recipient_query = parse(query, model)
46
+ model = recipient_query.model
47
+
48
+ case recipient_query.type
49
+ when "bus-list"
50
+ "Bus List: #{model.name} (signed up for bus)"
51
+ when "bus-list--eligible"
52
+ "Bus List: #{model.name} (eligible, not signed up for bus)"
53
+ when "bus-list--applied"
54
+ "Bus List: #{model.name} (applied/not accepted)"
55
+ when "school"
56
+ "Confirmed or Accepted: #{model.name}"
57
+ else
58
+ raise "Unknown recipient query type: #{recipient_query.type.inspect} (in message recipient query: #{r.inspect}"
59
+ end
60
+ end
61
+ end
@@ -2,6 +2,8 @@ class Questionnaire < ApplicationRecord
2
2
  include ActiveModel::Dirty
3
3
 
4
4
  before_validation :consolidate_school_names
5
+ after_create :queue_triggered_email_create
6
+ after_update :queue_triggered_email_update
5
7
  after_save :update_school_questionnaire_count
6
8
  after_destroy :update_school_questionnaire_count
7
9
 
@@ -181,4 +183,12 @@ class Questionnaire < ApplicationRecord
181
183
  School.increment_counter(:questionnaire_count, school_id)
182
184
  end
183
185
  end
186
+
187
+ def queue_triggered_email_update
188
+ Message.queue_for_trigger("questionnaire.#{acc_status}", id) if saved_change_to_acc_status?
189
+ end
190
+
191
+ def queue_triggered_email_create
192
+ Message.queue_for_trigger("questionnaire.#{acc_status}", id)
193
+ end
184
194
  end
@@ -0,0 +1,27 @@
1
+ %h3.no-margin Triggered Email Overview
2
+ %p
3
+ Recipient updates that match a given event will be sent the corresponding email(s) automatically.
4
+ %br
5
+ For example, when a user moves from the "Accepted" to "RSVP Confirmed" state, the user will be sent an email for the "RSVP Confirmed" event.
6
+ %p Hard-coded emails are those managed in the source code repository, and cannot be disabled.
7
+
8
+ %table.table
9
+ %thead
10
+ %tr
11
+ %th Trigger Event
12
+ %th Hard-coded Email
13
+ %th Triggered Emails
14
+ %tbody
15
+ - Message::POSSIBLE_TRIGGERS.keys.each do |trigger|
16
+ %tr
17
+ %td= Message::POSSIBLE_TRIGGERS[trigger]
18
+ - messages = Message.where(trigger: trigger).all
19
+ %td
20
+ = ['questionnaire.pending', 'questionnaire.accepted', 'questionnaire.denied', 'questionnaire.rsvp_confirmed'].include?(trigger) ? '<strong>Yes</strong>'.html_safe : 'No'
21
+ %td
22
+ - if messages.present?
23
+ %ul.no-margin
24
+ - messages.each do |message|
25
+ %li= link_to(message.name, manage_message_path(message))
26
+ - else
27
+ None
@@ -1,14 +1,15 @@
1
- = simple_form_for(@bus_list, url: url_for(action: @bus_list.new_record? ? "create" : "update", controller: "bus_lists")) do |f|
2
- = f.error_notification
1
+ .form-container
2
+ = simple_form_for(@bus_list, url: url_for(action: @bus_list.new_record? ? "create" : "update", controller: "bus_lists")) do |f|
3
+ = f.error_notification
3
4
 
4
- .form-inputs
5
- = f.input :name
6
- = f.input :capacity
7
- = f.input :needs_bus_captain
8
- = f.input :notes, input_html: { rows: 10 }
9
- %p
10
- %small
11
- %em Notes support Markdown and HTML.
5
+ .form-inputs
6
+ = f.input :name
7
+ = f.input :capacity
8
+ = f.input :needs_bus_captain
9
+ = f.input :notes, input_html: { rows: 10 }
10
+ %p
11
+ %small
12
+ %em Notes support Markdown and HTML.
12
13
 
13
- .form-actions
14
- = f.button :submit
14
+ .form-actions
15
+ = f.button :submit
@@ -1,15 +1,17 @@
1
- = simple_form_for @message, url: url_for(action: @message.new_record? ? "create" : "update", controller: "messages") do |f|
2
- = f.error_notification
1
+ .form-container
2
+ = simple_form_for @message, url: url_for(action: @message.new_record? ? "create" : "update", controller: "messages") do |f|
3
+ = f.error_notification
3
4
 
4
- .form-inputs
5
- = f.input :name
6
- = f.input :subject, hint: "All emails are from <pre>#{html_escape(Rails.configuration.hackathon['email_from'])}</pre>".html_safe
7
- = f.input :template, as: :select, collection: Message::POSSIBLE_TEMPLATES.map { |x| [x.titleize, x] }, include_blank: false
8
- = f.input :recipients, as: :select, collection: Message::POSSIBLE_RECIPIENTS.invert, input_html: { class: "selectize", multiple: true }
9
- = f.input :body, input_html: { rows: 10 }
10
- %p
11
- %small
12
- %em Markdown and HTML supported.
5
+ .form-inputs
6
+ = f.input :name
7
+ = f.input :subject, hint: "All emails are from <pre>#{html_escape(Rails.configuration.hackathon['email_from'])}</pre>".html_safe
8
+ = f.input :template, as: :select, collection: Message::POSSIBLE_TEMPLATES.map { |x| [x.titleize, x] }, include_blank: false
9
+ - if @message.status == "drafted"
10
+ = f.input :recipients, as: :select, collection: Message.possible_recipients, input_html: { class: "selectize", multiple: true, }, hint: "Sent manually, in bulk", placeholder: "Type to search by school or type..."
11
+ - else
12
+ = f.input :recipients, as: :select, collection: Message.possible_recipients, input_html: { class: "selectize", multiple: true }, hint: "Cannot be edited once a message has been delivered", disabled: true
13
+ = f.input :trigger, as: :select, collection: Message::POSSIBLE_TRIGGERS.invert, include_blank: "(no automatic trigger)", hint: "Sent automatically, to an individual applicant"
14
+ = f.input :body, input_html: { rows: 10 }, hint: "Supports markdown and HTML"
13
15
 
14
- .form-actions
15
- = f.button :submit
16
+ .form-actions
17
+ = f.button :submit
@@ -1,5 +1,6 @@
1
1
  %section.section.manage
2
- %h1.section-title= title "Editing Message"
2
+ .form-container
3
+ %h1.section-title= title "Editing Message"
3
4
 
4
5
  = render 'form'
5
6
 
@@ -8,11 +8,16 @@
8
8
  %th ID
9
9
  %th Name
10
10
  %th Subject
11
- %th Status
12
- %th Delivered
11
+ %th Trigger
12
+ %th Bulk Status
13
+ %th Bulk Delivered
13
14
  %tbody
14
15
 
15
16
  %br
16
17
 
17
18
  - unless current_user.admin_limited_access
18
- = btn_link_to 'New Message', new_manage_message_path
19
+ %p= btn_link_to 'New Message', new_manage_message_path
20
+
21
+ %br
22
+
23
+ = render 'triggered_email_summary'
@@ -14,34 +14,47 @@
14
14
  = @message.template.titleize
15
15
  %p
16
16
  %b Recipients:
17
- = @message.recipients_list
17
+ = @message.recipients_list.presence || "(none)"
18
18
  %br
19
19
  %small
20
- %em #{recipient_count} currently match this query.
20
+ %em Sent manually, in bulk
21
+ - if @message.recipients.present?
22
+ %br
23
+ %small
24
+ %em #{recipient_count} currently match this query
25
+ %p
26
+ %b Trigger:
27
+ = Message::POSSIBLE_TRIGGERS[@message.trigger] || "(no automatic trigger)"
28
+ %br
29
+ %small
30
+ %em Sent automatically, to an individual applicant
21
31
  %p
22
32
  %b Preview:
23
33
  = link_to "Open full preview &raquo;".html_safe, preview_manage_message_path
24
34
 
25
35
  %iframe.email-preview{src: preview_manage_message_path(@message)}
26
36
 
27
- %p
28
- %b
29
- Status:
30
- = @message.status.titleize
31
- %p
32
- %b Queued At:
33
- = @message.queued_at || "(n/a)"
34
- %p
35
- %b Started At:
36
- = @message.started_at || "(n/a)"
37
- %p
38
- %b Delivered At:
39
- = @message.delivered_at || "(n/a)"
37
+ - if @message.recipients_list.present?
38
+ %fieldset
39
+ %legend Bulk delivery details
40
+ %p
41
+ %b
42
+ Status:
43
+ = @message.status.titleize
44
+ %p
45
+ %b Queued At:
46
+ = @message.queued_at || "(n/a)"
47
+ %p
48
+ %b Started At:
49
+ = @message.started_at || "(n/a)"
50
+ %p
51
+ %b Delivered At:
52
+ = @message.delivered_at || "(n/a)"
40
53
 
41
54
  %hr
42
55
 
43
56
  - unless current_user.admin_limited_access
44
- - if @message.status == "drafted"
57
+ - if @message.status == "drafted" && @message.recipients_list.present?
45
58
  = btn_link_to 'Deliver', deliver_manage_message_path(@message), method: :patch, data: { confirm: "Are you sure? The message \"#{@message.name}\" will be sent to #{recipient_count}." }
46
59
  \|
47
60
  - if @message.can_edit?
@@ -25,9 +25,20 @@
25
25
 
26
26
  - unless current_user.admin_limited_access?
27
27
  %br
28
- = btn_link_to 'New Questionnaire', new_manage_questionnaire_path
28
+ .container.container-half
29
+ = btn_link_to 'New Questionnaire', new_manage_questionnaire_path
29
30
 
30
- %h4 Bulk Action:
31
- = simple_form_for Questionnaire.new, url: bulk_apply_manage_questionnaires_path, html: { data: { bulk_row_edit: true } } do |f|
32
- = f.input :acc_status, as: :select, collection: Questionnaire::POSSIBLE_ACC_STATUS.invert, include_blank: false, label: "Acceptance Status:", input_html: { data: { bulk_row_edit: true } }
33
- = f.button :submit, value: "Update Status", data: { bulk_row_edit: true }
31
+ .container.container-half
32
+
33
+ .container.container-half
34
+ %fieldset
35
+ %legend Bulk Action
36
+ = simple_form_for Questionnaire.new, url: bulk_apply_manage_questionnaires_path, html: { data: { bulk_row_edit: true } } do |f|
37
+ = f.input :acc_status, as: :select, collection: Questionnaire::POSSIBLE_ACC_STATUS.invert, include_blank: false, label: "Acceptance Status:", input_html: { data: { bulk_row_edit: true } }, hint: "Updating this status may trigger an automatic email to each applicant - see below for details."
38
+ = f.button :submit, value: "Update Status", data: { bulk_row_edit: true }
39
+
40
+ %br
41
+ %br
42
+ %br
43
+
44
+ = render 'triggered_email_summary'
@@ -51,7 +51,7 @@
51
51
  - unless current_user.admin_limited_access?
52
52
  %hr
53
53
  = simple_form_for @questionnaire, url: url_for(action: "update_acc_status", controller: "questionnaires") do |f|
54
- = f.input :acc_status, as: :select, collection: Questionnaire::POSSIBLE_ACC_STATUS.invert, include_blank: false, label: "Acceptance Status:"
54
+ = f.input :acc_status, as: :select, collection: Questionnaire::POSSIBLE_ACC_STATUS.invert, include_blank: false, label: "Acceptance Status:", hint: "Updating this status may trigger an automatic email to the applicant - see #{link_to('messages', manage_messages_path)} for details.".html_safe
55
55
  = f.button :submit, value: "Update Status"
56
56
 
57
57
  %hr
@@ -18,13 +18,13 @@ class BulkMessageWorker
18
18
  def self.build_recipients(recipient_types)
19
19
  recipients = Set.new
20
20
  recipient_types.each do |type|
21
- recipients += recipients_query(type)
21
+ recipients += user_ids(type)
22
22
  end
23
23
  recipients
24
24
  end
25
25
 
26
26
  # rubocop:disable CyclomaticComplexity
27
- def self.recipients_query(type)
27
+ def self.user_ids(type)
28
28
  case type
29
29
  when "all"
30
30
  User.where(admin: false).pluck(:id)
@@ -50,38 +50,32 @@ class BulkMessageWorker
50
50
  Questionnaire.where("(acc_status = 'accepted' OR acc_status = 'rsvp_confirmed' OR acc_status = 'rsvp_denied') AND checked_in_at IS NULL").pluck(:user_id)
51
51
  when "non-checked-in-excluding"
52
52
  Questionnaire.where("acc_status != 'accepted' AND acc_status != 'rsvp_confirmed' AND acc_status != 'rsvp_denied' AND checked_in_at IS NULL").pluck(:user_id)
53
- when "bus-list-cornell-bing"
54
- BusList.find(1).passengers.pluck(:user_id)
55
- when "bus-list-buffalo"
56
- BusList.find(2).passengers.pluck(:user_id)
57
- when "bus-list-albany"
58
- BusList.find(3).passengers.pluck(:user_id)
59
- when "bus-list-cornell-bing-eligible"
60
- Questionnaire.joins(:school).where("(schools.bus_list_id = 1 AND riding_bus != 1) AND (acc_status = 'accepted' OR acc_status = 'rsvp_confirmed')").pluck(:user_id)
61
- when "bus-list-buffalo-eligible"
62
- Questionnaire.joins(:school).where("(schools.bus_list_id = 2 AND riding_bus != 1) AND (acc_status = 'accepted' OR acc_status = 'rsvp_confirmed')").pluck(:user_id)
63
- when "bus-list-albany-eligible"
64
- Questionnaire.joins(:school).where("(schools.bus_list_id = 3 AND riding_bus != 1) AND (acc_status = 'accepted' OR acc_status = 'rsvp_confirmed')").pluck(:user_id)
65
- when "bus-list-cornell-bing-applied"
66
- Questionnaire.joins(:school).where("(schools.bus_list_id = 1) AND (acc_status != 'accepted' AND acc_status != 'rsvp_confirmed' AND acc_status != 'rsvp_denied')").pluck(:user_id)
67
- when "bus-list-buffalo-applied"
68
- Questionnaire.joins(:school).where("(schools.bus_list_id = 2) AND (acc_status != 'accepted' AND acc_status != 'rsvp_confirmed' AND acc_status != 'rsvp_denied')").pluck(:user_id)
69
- when "bus-list-albany-applied"
70
- Questionnaire.joins(:school).where("(schools.bus_list_id = 3) AND (acc_status != 'accepted' AND acc_status != 'rsvp_confirmed' AND acc_status != 'rsvp_denied')").pluck(:user_id)
71
- when "school-rit"
72
- Questionnaire.where("school_id = 2304 AND (acc_status = \"rsvp_confirmed\" OR acc_status = \"accepted\")").pluck(:user_id)
73
- when "school-cornell"
74
- Questionnaire.where("school_id = 2164 AND (acc_status = \"rsvp_confirmed\" OR acc_status = \"accepted\")").pluck(:user_id)
75
- when "school-binghamton"
76
- Questionnaire.where("school_id = 5526 AND (acc_status = \"rsvp_confirmed\" OR acc_status = \"accepted\")").pluck(:user_id)
77
- when "school-buffalo"
78
- Questionnaire.where("school_id = 2345 AND (acc_status = \"rsvp_confirmed\" OR acc_status = \"accepted\")").pluck(:user_id)
79
- when "school-waterloo"
80
- Questionnaire.where("school_id = 5580 AND (acc_status = \"rsvp_confirmed\" OR acc_status = \"accepted\")").pluck(:user_id)
81
- when "school-toronto"
82
- Questionnaire.where("school_id = 5539 AND (acc_status = \"rsvp_confirmed\" OR acc_status = \"accepted\")").pluck(:user_id)
83
- when "school-umd-collegepark"
84
- Questionnaire.where("school_id = 5543 AND (acc_status = \"rsvp_confirmed\" OR acc_status = \"accepted\")").pluck(:user_id)
53
+ when /(.*)::(\d*)/
54
+ user_ids_from_query(type)
55
+ else
56
+ raise "Unknown recipient type: #{type.inspect}"
57
+ end
58
+ end
59
+ # rubocop:enable CyclomaticComplexity
60
+
61
+ def self.user_ids_from_query(type)
62
+ # Parse the query
63
+ # See app/models/message_recipient_query.rb for how this works
64
+ recipient_query = MessageRecipientQuery.parse(type)
65
+ model = recipient_query.model
66
+
67
+ # Build the recipients query
68
+ case recipient_query.type
69
+ when "bus-list"
70
+ model.passengers.pluck(:user_id)
71
+ when "bus-list--eligible"
72
+ Questionnaire.joins(:school).where("schools.bus_list_id = ? AND riding_bus != 1 AND (acc_status = 'accepted' OR acc_status = 'rsvp_confirmed')", model.id).pluck(:user_id)
73
+ when "bus-list--applied"
74
+ Questionnaire.joins(:school).where("schools.bus_list_id = ? AND (acc_status != 'accepted' AND acc_status != 'rsvp_confirmed' AND acc_status != 'rsvp_denied')", model.id).pluck(:user_id)
75
+ when "school"
76
+ Questionnaire.where("school_id = ? AND (acc_status = 'rsvp_confirmed' OR acc_status = 'accepted')", model.id).pluck(:user_id)
77
+ else
78
+ raise "Unknown recipient query type: #{recipient_query.type.inspect} (in message recipient query: #{type.inspect}"
85
79
  end
86
80
  end
87
81
  end
@@ -0,0 +1,5 @@
1
+ class AddTriggerToMessage < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :messages, :trigger, :string
4
+ end
5
+ end
@@ -0,0 +1,36 @@
1
+ class ConvertMessageRecipientsToRecipientQueries < ActiveRecord::Migration[5.1]
2
+ CONVERSION_MAPPING = {
3
+ "bus-list-cornell-bing" => ["bus-list", 1],
4
+ "bus-list-buffalo" => ["bus-list", 2],
5
+ "bus-list-albany" => ["bus-list", 3],
6
+ "bus-list-cornell-bing-eligible" => ["bus-list--eligible", 1],
7
+ "bus-list-buffalo-eligible" => ["bus-list--eligible", 2],
8
+ "bus-list-albany-eligible" => ["bus-list--eligible", 3],
9
+ "bus-list-cornell-bing-applied" => ["bus-list--applied", 1],
10
+ "bus-list-buffalo-applied" => ["bus-list--applied", 2],
11
+ "bus-list-albany-applied" => ["bus-list--applied", 3],
12
+ "school-rit" => ["school", 2304],
13
+ "school-cornell" => ["school", 2164],
14
+ "school-binghamton" => ["school", 5526],
15
+ "school-buffalo" => ["school", 2345],
16
+ "school-waterloo" => ["school", 5580],
17
+ "school-toronto" => ["school", 5539],
18
+ "school-umd-collegepark" => ["school", 5543]
19
+ }.freeze
20
+
21
+ def up
22
+ Message.all.each do |message|
23
+ CONVERSION_MAPPING.to_a.each do |mapping|
24
+ old = mapping[0]
25
+ new_type = mapping[1][0]
26
+ new_id = mapping[1][1]
27
+
28
+ index = message.recipients.index(old)
29
+ next if index.nil?
30
+
31
+ message.recipients[index] = "#{new_type}::#{new_id}"
32
+ message.save!
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,3 @@
1
1
  module HackathonManager
2
- VERSION = '0.5.11'.freeze
2
+ VERSION = '0.6.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hackathon_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.11
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stuart Olivera
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-16 00:00:00.000000000 Z
11
+ date: 2018-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -517,6 +517,7 @@ files:
517
517
  - app/models/deletable_attachment.rb
518
518
  - app/models/fips.rb
519
519
  - app/models/message.rb
520
+ - app/models/message_recipient_query.rb
520
521
  - app/models/questionnaire.rb
521
522
  - app/models/school.rb
522
523
  - app/models/school_name_duplicate.rb
@@ -524,6 +525,7 @@ files:
524
525
  - app/views/application/_bus_list_info.html.haml
525
526
  - app/views/application/_bus_list_stats.html.haml
526
527
  - app/views/application/_questionnaire_summary.html.haml
528
+ - app/views/application/_triggered_email_summary.html.haml
527
529
  - app/views/bus_lists/show.html.haml
528
530
  - app/views/devise/passwords/_form.html.haml
529
531
  - app/views/devise/passwords/edit.html.haml
@@ -638,7 +640,9 @@ files:
638
640
  - db/migrate/20170107210122_create_school_name_duplicates.rb
639
641
  - db/migrate/20170128063020_install_blazer.rb
640
642
  - db/migrate/20171220042158_add_why_attend_to_questionnaires.rb
643
+ - db/migrate/20180108231420_add_trigger_to_message.rb
641
644
  - db/migrate/20180116022530_set_default_count_on_schools.rb
645
+ - db/migrate/20180118035548_convert_message_recipients_to_recipient_queries.rb
642
646
  - db/schools.csv
643
647
  - db/seeds.rb
644
648
  - lib/hackathon_manager.rb