aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoachim Filip Ignacy Bartosik <jbartosik@gmail.com>2011-06-06 20:50:58 +0200
committerJoachim Filip Ignacy Bartosik <jbartosik@gmail.com>2011-06-10 18:11:42 +0200
commita79293f652cedf63f9f3cd88107e98b9b4da99f3 (patch)
treee4621b126b132c31c6d65e250f962fd1d7a6eb24
parentBot obtains voters, agenda items and voting options lists from webapp (diff)
downloadcouncil-webapp-a79293f652cedf63f9f3cd88107e98b9b4da99f3.tar.gz
council-webapp-a79293f652cedf63f9f3cd88107e98b9b4da99f3.tar.bz2
council-webapp-a79293f652cedf63f9f3cd88107e98b9b4da99f3.zip
Send email reminders about meetings using delayed_job
-rw-r--r--site/Gemfile2
-rw-r--r--site/Gemfile.lock8
-rw-r--r--site/app/mailers/user_mailer.rb6
-rw-r--r--site/app/models/agenda.rb43
-rw-r--r--site/app/views/user_mailer/meeting_reminder.erb5
-rw-r--r--site/config/environments/test.rb2
-rw-r--r--site/config/initializers/custom_configs.rb4
-rw-r--r--site/db/schema.rb21
-rw-r--r--site/doc/sample_configs/reminders.yml1
-rwxr-xr-xsite/script/delayed_job5
-rw-r--r--site/spec/models/agenda_spec.rb41
-rw-r--r--site/spec/models/user_mailer_spec.rb14
-rw-r--r--site/spec/spec_helper.rb5
-rw-r--r--site/spec/support/delayed_should_receive.rb11
-rw-r--r--site/spec/support/should_have_text.rb8
15 files changed, 170 insertions, 6 deletions
diff --git a/site/Gemfile b/site/Gemfile
index fa841ac..a95faa0 100644
--- a/site/Gemfile
+++ b/site/Gemfile
@@ -3,11 +3,13 @@ gem 'rails', '3.0.3'
gem 'sqlite3-ruby', :require => 'sqlite3'
gem 'devise'
gem 'hobo_devise', '>=0.0.2'
+gem 'delayed_job'
group :development, :test do
gem 'ruby-debug'
gem 'rspec-rails'
gem 'shoulda'
+ gem 'email_spec'
gem 'cucumber-rails'
gem 'capybara'
diff --git a/site/Gemfile.lock b/site/Gemfile.lock
index 4313a4a..aca3ead 100644
--- a/site/Gemfile.lock
+++ b/site/Gemfile.lock
@@ -57,7 +57,11 @@ GEM
nokogiri (>= 1.4.4)
rack-test (>= 0.5.7)
culerity (0.2.15)
+ daemons (1.1.0)
database_cleaner (0.6.6)
+ delayed_job (2.1.2)
+ activesupport (~> 3.0)
+ daemons
devise (1.3.4)
bcrypt-ruby (~> 2.1.2)
orm_adapter (~> 0.0.3)
@@ -69,6 +73,8 @@ GEM
dryml (1.3.0.pre28)
actionpack (>= 3.0.0)
hobo_support (= 1.3.0.pre28)
+ email_spec (1.1.1)
+ rspec (~> 2.0)
erubis (2.6.6)
abstract (>= 1.0.0)
factory_girl (1.3.3)
@@ -207,7 +213,9 @@ DEPENDENCIES
capybara
cucumber-rails
database_cleaner
+ delayed_job
devise
+ email_spec
factory_girl
fuubar-cucumber
hobo (>= 1.3.0.pre28)
diff --git a/site/app/mailers/user_mailer.rb b/site/app/mailers/user_mailer.rb
index c5c18f8..d9515f3 100644
--- a/site/app/mailers/user_mailer.rb
+++ b/site/app/mailers/user_mailer.rb
@@ -7,4 +7,10 @@ class UserMailer < ActionMailer::Base
:to => user.email )
end
+ def meeting_reminder(user, agenda)
+ @user = user
+ @agenda = agenda
+ mail(:subject => "Upcoming meeting reminder - #{agenda.meeting_time.to_s}",
+ :to => user.email)
+ end
end
diff --git a/site/app/models/agenda.rb b/site/app/models/agenda.rb
index 22414d7..ea58041 100644
--- a/site/app/models/agenda.rb
+++ b/site/app/models/agenda.rb
@@ -3,7 +3,8 @@ class Agenda < ActiveRecord::Base
hobo_model # Don't put anything above this
fields do
- meeting_time :datetime
+ meeting_time :datetime
+ email_reminder_sent :boolean, :null => false, :default => false
timestamps
end
@@ -101,7 +102,12 @@ class Agenda < ActiveRecord::Base
end
end
- def self.voters
+ def time_for_reminders
+ offset = CustomConfig['Reminders']['hours_before_meeting_to_send_email_reminders'].hours
+ meeting_time - offset
+ end
+
+ def self.voters_users
# It's possible to rewrite this as SQL, but
# * this method is rarely called
# * it fetches little data
@@ -109,7 +115,38 @@ class Agenda < ActiveRecord::Base
# Joachim
council = ::User.council_member_is(true)
proxies = Agenda.current.proxies
- [council - proxies.*.council_member + proxies.*.proxy].flatten.*.irc_nick
+ [council - proxies.*.council_member + proxies.*.proxy].flatten
+ end
+
+ def self.voters
+ Agenda.voters_users.*.irc_nick
+ end
+
+ def self.send_current_agenda_reminders
+ agenda = Agenda.current
+
+ return if agenda.email_reminder_sent?
+ return if Time.now < agenda.time_for_reminders
+
+ for user in Agenda.voters_users
+ UserMailer.delay.deliver_meeting_reminder(user, agenda)
+ end
+
+ agenda.email_reminder_sent = true
+ agenda.save!
+ end
+
+ before_save do |a|
+ return true if a.new_record?
+ return true unless a.meeting_time_changed?
+ a.email_reminder_sent = false
+ true
+ end
+
+ after_save do |a|
+ if a.new_record? or a.meeting_time_changed?
+ Agenda.delay(:run_at => a.time_for_reminders).send_current_agenda_reminders
+ end
end
protected
diff --git a/site/app/views/user_mailer/meeting_reminder.erb b/site/app/views/user_mailer/meeting_reminder.erb
new file mode 100644
index 0000000..b49e7e6
--- /dev/null
+++ b/site/app/views/user_mailer/meeting_reminder.erb
@@ -0,0 +1,5 @@
+<%= @user %>,
+
+meeting will take place on <%= @agenda.meeting_time.to_s %>. You can view agenda for the meeting on:
+
+ <%= agenda_url(@agenda) %>
diff --git a/site/config/environments/test.rb b/site/config/environments/test.rb
index c2fc237..944a63f 100644
--- a/site/config/environments/test.rb
+++ b/site/config/environments/test.rb
@@ -32,4 +32,6 @@ Council::Application.configure do
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
+
+ config.action_mailer.default_url_options = { :host => 'localhost', :port => '3000' }
end
diff --git a/site/config/initializers/custom_configs.rb b/site/config/initializers/custom_configs.rb
index 599646f..dca866f 100644
--- a/site/config/initializers/custom_configs.rb
+++ b/site/config/initializers/custom_configs.rb
@@ -1,2 +1,4 @@
CustomConfig = {}
-CustomConfig['Bot'] = YAML.load open('config/bot.yml').read
+for conf in ['bot', 'reminders']
+ CustomConfig[conf.camelize] = YAML.load open("config/#{conf}.yml").read
+end
diff --git a/site/db/schema.rb b/site/db/schema.rb
index fec65de..071ff84 100644
--- a/site/db/schema.rb
+++ b/site/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20110603133359) do
+ActiveRecord::Schema.define(:version => 20110606170332) do
create_table "agenda_items", :force => true do |t|
t.string "title"
@@ -30,12 +30,29 @@ ActiveRecord::Schema.define(:version => 20110603133359) do
t.datetime "meeting_time"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "state", :default => "open"
+ t.string "state", :default => "open"
t.datetime "key_timestamp"
+ t.boolean "email_reminder_sent", :default => false, :null => false
end
add_index "agendas", ["state"], :name => "index_agendas_on_state"
+ create_table "delayed_jobs", :force => true do |t|
+ t.integer "priority", :default => 0
+ t.integer "attempts", :default => 0
+ t.text "handler"
+ t.text "last_error"
+ t.datetime "run_at"
+ t.datetime "locked_at"
+ t.datetime "failed_at"
+ t.string "locked_by"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "delayed_jobs", ["locked_by"], :name => "delayed_jobs_locked_by"
+ add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority"
+
create_table "participations", :force => true do |t|
t.string "irc_nick"
t.datetime "created_at"
diff --git a/site/doc/sample_configs/reminders.yml b/site/doc/sample_configs/reminders.yml
new file mode 100644
index 0000000..edf937a
--- /dev/null
+++ b/site/doc/sample_configs/reminders.yml
@@ -0,0 +1 @@
+hours_before_meeting_to_send_email_reminders: 24
diff --git a/site/script/delayed_job b/site/script/delayed_job
new file mode 100755
index 0000000..edf1959
--- /dev/null
+++ b/site/script/delayed_job
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
+require 'delayed/command'
+Delayed::Command.new(ARGV).daemonize
diff --git a/site/spec/models/agenda_spec.rb b/site/spec/models/agenda_spec.rb
index 2a08210..83b31a3 100644
--- a/site/spec/models/agenda_spec.rb
+++ b/site/spec/models/agenda_spec.rb
@@ -147,4 +147,45 @@ describe Agenda do
(voters - nicks).should be_empty
(nicks - voters).should be_empty
end
+
+ it 'should add Agenda.send_current_agenda_reminders to delayed jobs when created' do
+ Agenda.should_receive_delayed(:send_current_agenda_reminders)
+ Factory(:agenda)
+ end
+
+ it 'should add Agenda.send_current_agenda_reminders to delayed jobs when meeting time changes' do
+ a = Factory(:agenda)
+ Agenda.should_receive_delayed(:send_current_agenda_reminders)
+ a.meeting_time = Time.now + 24.hours
+ a.save!
+ end
+
+ it 'should set email_reminder_sent to false when time changes' do
+ a = Factory(:agenda, :email_reminder_sent => true)
+ lambda {
+ a.meeting_time = Time.now + 24.hours
+ a.save!
+ }.should change(a, :email_reminder_sent?).from(true).to(false)
+ end
+
+ it 'should send reminders properly with send_current_agenda_reminders using delayed jobs' do
+ agenda = Factory(:agenda)
+ users = users_factory([:user] * 2)
+ council = users_factory([:council] * 2)
+ Factory(:proxy, :proxy => users.first, :council_member => council.first, :agenda => agenda)
+ UserMailer.should_receive_delayed(:deliver_meeting_reminder, council.last, agenda)
+ UserMailer.should_receive_delayed(:deliver_meeting_reminder, users.first, agenda)
+
+ Agenda.send_current_agenda_reminders
+
+ agenda.reload
+ agenda.email_reminder_sent.should be_true
+ end
+
+ it 'should not send reminders with send_current_agenda_reminders if Agenda.current.email_reminder_sent is true' do
+ a = Factory(:agenda, :email_reminder_sent => true)
+ users = users_factory([:user] * 2)
+ UserMailer.should_not_receive(:delay)
+ Agenda.send_current_agenda_reminders
+ end
end
diff --git a/site/spec/models/user_mailer_spec.rb b/site/spec/models/user_mailer_spec.rb
new file mode 100644
index 0000000..ea2c2bc
--- /dev/null
+++ b/site/spec/models/user_mailer_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+describe UserMailer do
+ it 'should send proper meeting reminders' do
+ user = Factory(:user)
+ agenda = Factory(:agenda)
+ reminder = UserMailer.meeting_reminder(user, agenda)
+ reminder.should deliver_to(user.email)
+ reminder.should deliver_from("no-reply@localhost")
+ reminder.should have_text(/meeting will take place on #{agenda.meeting_time.to_s}./)
+ reminder.should have_text(/You can view agenda for the meeting on:/)
+ reminder.should have_text(/http:\/\/localhost:3000\/agendas\/#{agenda.id}/)
+ reminder.should have_subject("Upcoming meeting reminder - #{agenda.meeting_time.to_s}")
+ end
+end
diff --git a/site/spec/spec_helper.rb b/site/spec/spec_helper.rb
index a8b8aea..47ed37e 100644
--- a/site/spec/spec_helper.rb
+++ b/site/spec/spec_helper.rb
@@ -15,3 +15,8 @@ RSpec.configure do |config|
config.mock_with :rspec
config.use_transactional_fixtures = true
end
+
+RSpec.configure do |config|
+ config.include(EmailSpec::Helpers)
+ config.include(EmailSpec::Matchers)
+end
diff --git a/site/spec/support/delayed_should_receive.rb b/site/spec/support/delayed_should_receive.rb
new file mode 100644
index 0000000..0fbbe27
--- /dev/null
+++ b/site/spec/support/delayed_should_receive.rb
@@ -0,0 +1,11 @@
+class Object
+ def should_receive_delayed(method, *args)
+ m = RSpec::Mocks::Mock.new('proxy')
+ if args.empty?
+ m.should_receive(method)
+ else
+ m.should_receive(method).with(*args)
+ end
+ self.should_receive(:delay).and_return(m)
+ end
+end
diff --git a/site/spec/support/should_have_text.rb b/site/spec/support/should_have_text.rb
new file mode 100644
index 0000000..3016c13
--- /dev/null
+++ b/site/spec/support/should_have_text.rb
@@ -0,0 +1,8 @@
+module Mail
+ class Message
+ # emailspec doesn't add this, so we have to
+ def has_text?(text)
+ not body.to_s.match(text).nil?
+ end
+ end
+end