Skip to content

Commit 6316853

Browse files
committed
[WIP#335] Update old digest email to send monthly popular protips
Generated a protips mailer WIP: query for popular protips directly and bypass Tire and our search implementation Added mail_view WIP: wiring up the popular protips WIP: cleaning up the old weekly digest so I can merge the styles and structure WIP: formatting a popular protip email Re-re-add MailView Pretty close to complete on the popular protips email
1 parent cf49296 commit 6316853

24 files changed

+565
-187
lines changed

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ gem 'awesome_print'
9797
gem 'faraday', '~> 0.8.1'
9898
gem 'metamagic'
9999

100+
gem "mail_view", "~> 2.0.4"
101+
100102
# ----------------
101103

102104

Gemfile.lock

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
GIT
2-
remote: git://github.com/emberjs/ember-rails.git
3-
revision: 5e5a398f3c67c3a3b84b7513b93b22bf81055cc9
4-
specs:
5-
ember-rails (0.15.0)
6-
active_model_serializers
7-
barber (>= 0.4.1)
8-
ember-data-source (>= 1.0.0.beta.5)
9-
ember-source (>= 1.1.0)
10-
execjs (>= 1.2)
11-
handlebars-source (> 1.0.0)
12-
jquery-rails (>= 1.0.17)
13-
railties (>= 3.1)
14-
151
GIT
162
remote: git://github.com/nixme/jazz_hands.git
173
revision: 5e4b48f145883ecb14b55bf04eacc28ac9662676
@@ -38,6 +24,20 @@ GIT
3824
mime-types (>= 1.25, < 3.0)
3925
rest-client (~> 1.4)
4026

27+
GIT
28+
remote: git://github.com/emberjs/ember-rails.git
29+
revision: 5e5a398f3c67c3a3b84b7513b93b22bf81055cc9
30+
specs:
31+
ember-rails (0.15.0)
32+
active_model_serializers
33+
barber (>= 0.4.1)
34+
ember-data-source (>= 1.0.0.beta.5)
35+
ember-source (>= 1.1.0)
36+
execjs (>= 1.2)
37+
handlebars-source (> 1.0.0)
38+
jquery-rails (>= 1.0.17)
39+
railties (>= 3.1)
40+
4141
GEM
4242
remote: https://rubygems.org/
4343
remote: https://rails-assets.org/
Loading

app/helpers/protips_helper.rb

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -294,36 +294,35 @@ def user_upvoted?(protip)
294294
current_user && current_user_upvotes.include?(protip.public_id)
295295
end
296296

297-
def protip_stat_class(protip)
298-
class_name = best_stat_name(protip)
299-
#class_name << " " << (user_upvoted?(protip) ? "upvoted" : "")
300-
end
301-
302297
def formatted_best_stat_value(protip)
303-
value =
304-
case best_stat_name(protip).to_sym
305-
when :views
306-
views_stat_value(protip)
307-
else
308-
best_stat_value(protip)
309-
end
310-
number_to_human(value, units: {unit: "", thousand: "k"})
298+
value = case best_stat_name(protip).to_sym
299+
when :views
300+
views_stat_value(protip)
301+
else
302+
best_stat_value(protip)
303+
end
304+
305+
number_to_human(value, units: { unit: '', thousand: 'k' })
311306
end
312307

313-
def blur_protips?
314-
params[:show_all].nil? && !signed_in?
308+
def best_stat_name(protip)
309+
protip.best_stat.is_a?(Tire::Results::Item) ? protip.best_stat.name : protip.best_stat[0]
315310
end
316311

317-
def followings_fragment_cache_key(user_id)
318-
['v1', 'followings_panel', user_id]
312+
def views_stat_value(protip)
313+
best_stat_value(protip) * Protip::COUNTABLE_VIEWS_CHUNK
319314
end
320315

321316
def best_stat_value(protip)
322317
protip.best_stat.is_a?(Tire::Results::Item) ? protip.best_stat.value.to_i : protip.best_stat[1]
323318
end
324319

325-
def best_stat_name(protip)
326-
protip.best_stat.is_a?(Tire::Results::Item) ? protip.best_stat.name : protip.best_stat[0]
320+
def blur_protips?
321+
params[:show_all].nil? && !signed_in?
322+
end
323+
324+
def followings_fragment_cache_key(user_id)
325+
['v1', 'followings_panel', user_id]
327326
end
328327

329328
def protip_networks(protip)
@@ -354,10 +353,6 @@ def protip_display_mode
354353
mobile_device? ? "fullpage" : "popup"
355354
end
356355

357-
def views_stat_value(protip)
358-
best_stat_value(protip) * Protip::COUNTABLE_VIEWS_CHUNK
359-
end
360-
361356
def display_protip_stats?(protip)
362357
stat_name = best_stat_name(protip)
363358
# if stat is present, and the stat we're displaying is views over 50, display.

app/helpers/users_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def users_image_tag(user, options = {})
3030

3131
#TODO Remove
3232
def users_image_path(user)
33+
return ''
3334
user.avatar.url
3435
end
3536

app/mailers/mail_preview.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class MailPreview < MailView
2+
USERNAME = 'just3ws'
3+
4+
def popular_protips
5+
from = 60.days.ago
6+
to = 0.days.ago
7+
user = User.with_username(USERNAME)
8+
protips = ProtipMailer::Queries.popular_protips(from, to)
9+
ProtipMailer.popular_protips(user, protips, from, to).deliver
10+
end
11+
12+
def old_weekly_digest
13+
WeeklyDigestMailer.weekly_digest(USERNAME)
14+
end
15+
end

app/mailers/protip_mailer.rb

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
class ProtipMailer < ActionMailer::Base
2+
include ActionView::Helpers::TextHelper
3+
4+
add_template_helper(UsersHelper)
5+
add_template_helper(ProtipsHelper)
6+
add_template_helper(ApplicationHelper)
7+
8+
default_url_options[:host] = 'coderwall.com'
9+
default_url_options[:only_path] = false
10+
default from: '"Coderwall" <support@coderwall.com>'
11+
12+
SPAM_NOTICE = "You're receiving this email because you signed up for Coderwall. We hate spam and make an effort to keep notifications to a minimum. To change your notification preferences, you can update your email settings here: http://coderwall.com/settings#email or immediately unsubscribe by clicking this link %unsubscribe_url%"
13+
STARS = {
14+
protip_upvotes: 'pro tip upvotes',
15+
followers: 'followers',
16+
endorsements: 'endorsements',
17+
protips_count: 'protips'
18+
}
19+
20+
#################################################################################
21+
def popular_protips(user, protips, from, to)
22+
fail "Protips are required." if protips.nil? || protips.empty?
23+
headers['X-Mailgun-Campaign-Id'] = 'coderwall-popular_protips'
24+
25+
@user = user
26+
@protips = protips
27+
@team, @job = get_team_and_job_for(@user)
28+
@issue = campaign_params
29+
30+
stars = @user.following_users.where('last_request_at > ?', 1.month.ago)
31+
@star_stat = star_stat_for_this_week
32+
@star_stat_string = STARS[@star_stat]
33+
34+
@most = star_stats(stars).sort_by { |star| -star[@star_stat] }.first
35+
@most = nil if @most && (@most[@star_stat] <= 0)
36+
37+
mail to: 'mike@just3ws.com', subject: 'Popular Protips on Coderwall'
38+
end
39+
#################################################################################
40+
41+
def campaign_params
42+
{
43+
utm_campaign: 'coderwall-popular_protips',
44+
utm_content: Date.today.midnight,
45+
utm_medium: 'email'
46+
}
47+
end
48+
49+
def star_stat_for_this_week
50+
STARS.keys[week_of_the_month % 4]
51+
end
52+
53+
def star_stats(stars, since=1.week.ago)
54+
stars.collect { |star| star.activity_stats(since, true) }.each_with_index.map { |stat, index| stat.merge(user: stars[index]) }
55+
end
56+
57+
def week_of_the_month
58+
Date.today.cweek - Date.today.at_beginning_of_month.cweek
59+
end
60+
61+
def get_team_and_job_for(user)
62+
if user.team.try(:hiring?)
63+
[user.team, user.team.jobs.sample]
64+
else
65+
teams = teams_for_user(user)
66+
teams.each do |team|
67+
best_job = team.best_positions_for(user).detect do |job|
68+
(job.team_document_id == user.team_document_id) || !already_sent?(job, user)
69+
end
70+
return [team, best_job] unless best_job.nil?
71+
end
72+
end
73+
[nil, nil]
74+
end
75+
76+
def teams_for_user(user)
77+
Team.most_relevant_featured_for(user).select do |team|
78+
team.hiring?
79+
end
80+
end
81+
82+
def already_sent?(mailable, user)
83+
SentMail.where(user_id: user.id, mailable_id: mailable.id, mailable_type: mailable.class.name).exists?
84+
end
85+
86+
module Queries
87+
def self.popular_protips(from, to)
88+
search_results = ProtipMailer::Queries.search_for_popular_protips(from, to)
89+
public_ids = search_results.map { |protip| protip['public_id'] }
90+
91+
Protip.eager_load(:user, :comments).where("public_id in (?)", public_ids)
92+
end
93+
94+
def self.search_for_popular_protips(from, to, max_results=10)
95+
url = "#{ENV['ELASTICSEARCH_URL']}/#{ENV['ELASTICSEARCH_PROTIPS_INDEX']}/_search"
96+
query = {
97+
'query' => {
98+
'bool' => {
99+
'must' => [
100+
{
101+
'range' => {
102+
'protip.created_at' => {
103+
'from' => from.strftime('%Y-%m-%d'),
104+
'to' => to.strftime('%Y-%m-%d')
105+
}
106+
}
107+
}
108+
]
109+
}
110+
},
111+
'size' => max_results,
112+
'sort' => [
113+
{
114+
'protip.popular_score' => {
115+
'order' => 'desc'
116+
}
117+
}
118+
]
119+
}
120+
response = RestClient.post(url, MultiJson.dump(query), content_type: :json, accept: :json)
121+
# TODO: check for response code
122+
MultiJson.load(response.body)['hits']['hits'].map { |protip| protip['_source'] }
123+
end
124+
end
125+
end

app/mailers/weekly_digest_mailer.rb

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
class WeeklyDigestMailer < ActionMailer::Base
44
include ActionView::Helpers::TextHelper
55
include ActiveSupport::Benchmarkable
6+
67
add_template_helper(UsersHelper)
78
add_template_helper(ProtipsHelper)
89
add_template_helper(ApplicationHelper)
@@ -17,54 +18,52 @@ def self.queue
1718

1819
SPAM_NOTICE = "You're receiving this email because you signed up for Coderwall. We hate spam and make an effort to keep notifications to a minimum. To change your notification preferences, you can update your email settings here: http://coderwall.com/settings#email or immediately unsubscribe by clicking this link %unsubscribe_url%"
1920

20-
2121
WEEKLY_DIGEST_EVENT = 'weekly_digest'
2222
ACTIVITY_SUBJECT_PREFIX = "[Coderwall]"
2323

24+
#################################################################################
2425
def weekly_digest(username)
2526
headers['X-Mailgun-Variables'] = {email_type: WEEKLY_DIGEST_EVENT}.to_json
2627
track_campaign(WEEKLY_DIGEST_EVENT)
2728

2829
@user = User.find_by_username(username)
2930
since = [@user.last_request_at || Time.at(0), 1.week.ago].min
3031

31-
benchmark "digest:stats" do
32-
@stats = @user.activity_stats(since, true).sort_by { |stat, count| -(count || 0) }
33-
end
32+
# benchmark "digest:stats" do
33+
@stats = @user.activity_stats(since, true).sort_by { |stat, count| -(count || 0) }
3434

3535
#@networks = @user.following_networks.most_protips
3636
@user.touch(:last_email_sent)
3737
@issue = weekly_digest_utm
38-
benchmark "digest:protips" do
39-
@protips = protips_for(@user, 6)
40-
end
38+
#
39+
# benchmark "digest:protips" do
40+
@protips = protips_for(@user, 6)
4141

4242
abort_delivery if @protips.blank? || @protips.count < 4
4343

44-
benchmark "digest:stars" do
45-
@stars = @user.following_users.where('last_request_at > ?', 1.month.ago)
46-
@star_stat = star_stat_for_this_week
47-
@star_stat_string = STARS[@star_stat]
48-
@most = star_stats(@stars).sort_by { |star| -star[@star_stat] }.first
49-
@most = nil if @most && (@most[@star_stat] <= 0)
50-
end
44+
# benchmark "digest:stars" do
45+
stars = @user.following_users.where('last_request_at > ?', 1.month.ago)
46+
@star_stat = star_stat_for_this_week
47+
@star_stat_string = STARS[@star_stat]
48+
@most = star_stats(stars).sort_by { |star| -star[@star_stat] }.first
49+
@most = nil if @most && (@most[@star_stat] <= 0)
5150

52-
benchmark "digest:team" do
53-
@team, @job = get_team_and_job_for(@user)
54-
end
51+
# benchmark "digest:team" do
52+
@team, @job = get_team_and_job_for(@user)
5553

56-
benchmark "digest:mark_sent" do
57-
mark_sent(@job) unless @job.nil?
58-
end
54+
# benchmark "digest:mark_sent" do
55+
mark_sent(@job) unless @job.nil?
5956

6057
mail to: @user.email, subject: "#{ACTIVITY_SUBJECT_PREFIX} #{weekly_digest_subject_for(@user, @stats, @most)}"
58+
6159
rescue Exception => e
62-
abort_delivery(e.message)
60+
abort_delivery(e)
6361
end
62+
#################################################################################
6463

65-
def abort_delivery(message="")
64+
def abort_delivery(error=nil)
6665
#self.perform_deliveries = false
67-
Rails.logger.error "sending bad email:#{message}"
66+
Rails.logger.error "sending bad email:#{error.message}"
6867
end
6968

7069
private
@@ -91,7 +90,7 @@ def protips_for(user, how_many=6)
9190
protips = Protip.trending_for_user(user).first(how_many)
9291
protips += Protip.trending.first(how_many-protips.count) if protips.count < how_many
9392
else
94-
protips =Protip.hawt_for_user(user).results.first(how_many)
93+
protips = Protip.hawt_for_user(user).results.first(how_many)
9594
protips +=Protip.hawt.results.first(how_many) if protips.count < how_many
9695
end
9796
protips

app/models/protip.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,9 @@ def best_stat
551551
upvotes: self.upvotes,
552552
comments: self.comments.count,
553553
hawt: self.hawt? ? 100 : 0
554-
}.sort_by { |k, v| -v }.first
554+
}.sort_by do |k, v|
555+
-v
556+
end.first
555557
end
556558

557559
def upvotes

app/views/layouts/email.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,4 @@
144144
</table>
145145

146146
</body>
147-
</html>
147+
</html>

0 commit comments

Comments
 (0)