Skip to content

Commit

Permalink
Merge pull request #48 from openpolitics/emails
Browse files Browse the repository at this point in the history
Add email notification for new votes
  • Loading branch information
Floppy committed Feb 13, 2017
2 parents d3709b0 + a5801e4 commit 8c61bef
Show file tree
Hide file tree
Showing 25 changed files with 287 additions and 626 deletions.
13 changes: 12 additions & 1 deletion .travis.yml
Expand Up @@ -7,5 +7,16 @@ before_script:
- bundle exec bin/rails db:migrate RAILS_ENV=test
env:
global:
- GITHUB_OAUTH_TOKEN: abc123
- GITHUB_APP_ID: abc123
- GITHUB_APP_SECRET: abc123
- GITHUB_REPO: openpolitics/manifesto
- GITHUB_OAUTH_TOKEN: abc123
- SITE_URL: https://votebot.example.com
- EMAIL_DOMAIN: example.com
- DOWNVOTE_WEIGHT: -1000
- UPVOTE_WEIGHT: 1
- ABSTAIN_WEIGHT: -1
- MIN_AGE: 7
- MAX_AGE: 90
- PASS_THRESHOLD: 2
- BLOCK_THRESHOLD: -1
4 changes: 4 additions & 0 deletions Gemfile
Expand Up @@ -58,6 +58,9 @@ group :development, :test do
gem "rspec-rails"
gem "dotenv-rails"
gem "database_cleaner"
gem "email_spec"
gem "factory_girl_rails"
gem "faker"
end

group :development do
Expand All @@ -67,6 +70,7 @@ group :development do
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0'
gem "letter_opener"
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
Expand Down
20 changes: 20 additions & 0 deletions Gemfile.lock
Expand Up @@ -75,8 +75,19 @@ GEM
dotenv-rails (2.2.0)
dotenv (= 2.2.0)
railties (>= 3.2, < 5.1)
email_spec (2.1.0)
htmlentities (~> 4.3.3)
launchy (~> 2.1)
mail (~> 2.6.3)
erubis (2.7.0)
execjs (2.7.0)
factory_girl (4.8.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.8.0)
factory_girl (~> 4.8.0)
railties (>= 3.0.0)
faker (1.7.2)
i18n (~> 0.5)
faraday (0.10.1)
multipart-post (>= 1.2, < 3)
ffi (1.9.17)
Expand All @@ -86,6 +97,7 @@ GEM
activesupport (>= 4.1.0)
hashdiff (0.3.2)
hashie (3.5.1)
htmlentities (4.3.4)
i18n (0.8.0)
jbuilder (2.6.1)
activesupport (>= 3.0.0, < 5.1)
Expand All @@ -96,6 +108,10 @@ GEM
thor (>= 0.14, < 2.0)
json (2.0.3)
jwt (1.5.6)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.4.1)
launchy (~> 2.2)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
Expand Down Expand Up @@ -259,9 +275,13 @@ DEPENDENCIES
database_cleaner
devise
dotenv-rails
email_spec
factory_girl_rails
faker
font-awesome-rails
jbuilder (~> 2.5)
jquery-rails
letter_opener
listen (~> 3.0)
memoist
octokit (~> 4.0)
Expand Down
15 changes: 15 additions & 0 deletions app.json
Expand Up @@ -70,6 +70,21 @@
"required": true,
"value": "-1",
"description": "Proposals with votes below this threshold will be considered 'blocked' when calculating status."
},
"SMTP_SERVER": {
"required": true,
"value": "smtp.sendgrid.net",
"description": "The name of your SMTP server, for sending mail"
},
"SMTP_USERNAME": {
"required": true,
"value": "username",
"description": "The login username for your SMTP server, for sending mail"
},
"SMTP_PASSWORD": {
"required": true,
"value": "password",
"description": "The password for your SMTP server, for sending mail"
}
},
"formation": {
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/proposals_controller.rb
Expand Up @@ -44,16 +44,16 @@ def on_pull_request(json)
def on_issue_comment_created(json)
issue = json['issue']
if issue['state'] == 'open' && issue['pull_request']
Proposal.create_from_github!(issue['number'])
Proposal.find_by(number: issue['number']).try(:update_from_github!)
end
end

def on_pull_request_opened(json)
Proposal.create_from_github!(json['number'])
proposal = Proposal.create(number: json['number'])
end

def on_pull_request_closed(json)
Proposal.find_or_create_by(number: json['number']).try(:close!)
Proposal.find_by(number: json['number']).try(:close!)
end

end
8 changes: 8 additions & 0 deletions app/mailers/proposals_mailer.rb
@@ -0,0 +1,8 @@
class ProposalsMailer < ApplicationMailer

def new_proposal(recipient, proposal)
@recipient = recipient
@proposal = proposal
mail to: recipient.email, subject: t("proposals_mailer.new.subject")
end
end
32 changes: 19 additions & 13 deletions app/models/proposal.rb
Expand Up @@ -11,26 +11,25 @@ class Proposal < ApplicationRecord
validates :title, presence: true
validates :proposer, presence: true

before_validation(on: :create) do
load_from_github
end
before_validation :load_from_github, on: :create
after_create :count_votes!
after_create :notify_voters

def self.recreate_all_from_github!
Rails.logger.info "Removing all proposals"
Proposal.delete_all
Rails.logger.info "Loading proposals"
def self.update_all_from_github!
Rails.logger.info "Updating proposals"
Octokit.pull_requests(ENV['GITHUB_REPO'], state: "all").each do |pr|
Rails.logger.info " - #{pr["number"]}: #{pr["title"]}"
create_from_github!(pr["number"])
pr = Proposal.find(number: pr["number"])
pr.update_from_github!
end
end

def self.create_from_github!(number)
pr = Proposal.find_or_create_by!(number: number)
pr.count_votes! unless pr.closed?
pr
def update_from_github!
load_from_github
count_votes! unless closed?
save!
end

def github_pr
@github_pr ||= Octokit.pull_request(ENV['GITHUB_REPO'], number)
end
Expand Down Expand Up @@ -79,4 +78,11 @@ def url
"https://github.com/#{ENV['GITHUB_REPO']}/pull/#{number}"
end

def notify_voters
# Notify users that there is a new proposal to vote on
User.where.not(email: nil).where(notify_new: true, contributor: true).all.each do |user|
ProposalsMailer.new_proposal(user, self) unless user == proposer
end
end

end
4 changes: 1 addition & 3 deletions app/models/user.rb
Expand Up @@ -8,9 +8,7 @@ class User < ApplicationRecord
validates :login, presence: true, uniqueness: true
validates :avatar_url, presence: true

before_validation(on: :create) do
load_from_github
end
before_validation :load_from_github, on: :create

def self.from_omniauth(auth)
# Find by oauth details, or if not available, by login only as some may have been created before.
Expand Down
26 changes: 26 additions & 0 deletions app/views/proposals_mailer/new_proposal.html.erb
@@ -0,0 +1,26 @@
Hi,

<p>
A new proposal has been submitted to the OpenPolitics Manifesto, and as a
previous contributor you can vote on whether or not it's accepted.
</p>

<h2>
<strong>Proposal:</strong> <%= link_to @proposal.title, @proposal.url %>
</h2>

<p>
Up-to-date voting instructions can be found in the comments.
</p>

<p>
thanks,<br/>
The OpenPolitics Team
</p>

<p>
P.S. You are receiving this email because you are a contributor to the OpenPolitics
Manifesto. If you don't want to receive notifications like this in future,
please update your email preferences on
<a href="<%= ENV['SITE_URL'] %>/users/<%= @recipient.login %>/edit">your settings page</a>.
</p>
16 changes: 16 additions & 0 deletions app/views/proposals_mailer/new_proposal.text.erb
@@ -0,0 +1,16 @@
Hi,

A new proposal has been submitted to the OpenPolitics Manifesto, and as a
previous contributor you can vote on whether or not it's accepted.

<%= @proposal.title %>: <%= @proposal.url %>

Up-to-date voting instructions can be found in the comments.

thanks,
The OpenPolitics Team

P.S. You are receiving this email because you are a contributor to the
OpenPolitics Manifesto. If you don't want to receive notifications like this
in future, please update your email settings at
<%= ENV['SITE_URL'] %>/users/<%= @recipient.login %>/edit
6 changes: 4 additions & 2 deletions config/environments/development.rb
Expand Up @@ -26,11 +26,13 @@
config.cache_store = :null_store
end

config.action_mailer.delivery_method = :letter_opener

# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

config.action_mailer.perform_caching = false

# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log

Expand Down
10 changes: 10 additions & 0 deletions config/environments/production.rb
Expand Up @@ -62,6 +62,16 @@
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: ENV['SMTP_SERVER'],
port: 587,
domain: ENV['DOMAIN'],
user_name: ENV['SMTP_USERNAME'],
password: ENV['SMTP_PASSWORD'],
authentication: 'plain',
enable_starttls_auto: true }

# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
Expand Down
5 changes: 4 additions & 1 deletion config/locales/en.yml
Expand Up @@ -36,4 +36,7 @@ en:
summary: Write a short summary that describes what you changed. For instance, "Fixed spelling mistakes", or "Added a new section about ducks".
description: If you want to, you can add a more detailed description that describes the background to your change, or explains to reviewers why it should be accepted.
success_title: Thanks! Your change has been submitted.
success: Your change will now go into a queue to be reviewed before it's merged in. If you'd like to look at the queue, and see the votes and comments on your submission, press the "View all proposals" button.
success: Your change will now go into a queue to be reviewed before it's merged in. If you'd like to look at the queue, and see the votes and comments on your submission, press the "View all proposals" button.
proposals_mailer:
new:
subject: "OpenPolitics Manifesto: new proposal ready for your vote"
2 changes: 1 addition & 1 deletion lib/tasks/update.rake
@@ -1,5 +1,5 @@
task :update => :environment do
Rails.logger = ActiveSupport::Logger.new(STDOUT)
User.update_all_from_github!
Proposal.recreate_all_from_github!
Proposal.update_all_from_github!
end
2 changes: 1 addition & 1 deletion spec/controllers/proposals_controller_spec.rb
Expand Up @@ -15,7 +15,7 @@
# Load a user
User.create(login: 'Floppy')
# Load a proposal
Proposal.create_from_github!(405) # proposed by this user
Proposal.create(number: 405) # proposed by this user
# Test show page
get :show, params: {id: 405}
expect(response).to be_ok
Expand Down
4 changes: 2 additions & 2 deletions spec/controllers/users_controller_spec.rb
Expand Up @@ -15,8 +15,8 @@
# Load a user
User.create(login: 'Floppy')
# Load a few proposals
Proposal.create_from_github!(405) # proposed by this user
Proposal.create_from_github!(100) # voted on by this user
Proposal.create(number: 405) # proposed by this user
Proposal.create(number: 100) # voted on by this user
# Test show page
get :show, params: {id: 'Floppy'}
expect(response).to be_ok
Expand Down
21 changes: 21 additions & 0 deletions spec/factories/proposal.rb
@@ -0,0 +1,21 @@
FactoryGirl.define do

factory :proposal do
number { Faker::Number.number(3) }
state "waiting"
title { Faker::Book.title }
#proposer - send this in
opened_at { Time.now }

# Don't run load_from_github callback when creating from factory, but put it back after
after(:build) do |x|
x.class.skip_callback(:validation, :before, :load_from_github)
x.class.skip_callback(:create, :after, :count_votes!)
end
after(:create) do |x|
x.class.set_callback(:create, :after, :count_votes!)
x.class.set_callback(:validation, :before, :load_from_github, on: :create)
end
end

end
15 changes: 15 additions & 0 deletions spec/factories/user.rb
@@ -0,0 +1,15 @@
FactoryGirl.define do

factory :user do
login { Faker::Internet.user_name }
contributor true
email { Faker::Internet.email }
avatar_url { Faker::Internet.url }
provider "github"

# Don't run load_from_github callback when creating from factory, but put it back after
after(:build) { |x| x.class.skip_callback(:validation, :before, :load_from_github) }
after(:create) { |x| x.class.set_callback(:validation, :before, :load_from_github, on: :create)}
end

end

0 comments on commit 8c61bef

Please sign in to comment.