hackathon_manager 0.5.11 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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