-
Notifications
You must be signed in to change notification settings - Fork 4k
/
goal_conversion_handler.rb
179 lines (156 loc) · 7.83 KB
/
goal_conversion_handler.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
class AbExperiment
# Responsible for checking if a given :user has "accomplished" the state :goal for any of the
# active :experiments. We scope our tests to events that happened on or after the experiment's
# start date.
#
# @note It is required that each experiment have a start date (in CCYY-MM-DD format).
class GoalConversionHandler
include FieldTest::Helpers
# The constant says "publishes post"; we're not actually concerned with the state change (from
# unpublished to published) but instead counting the number of published articles.
USER_PUBLISHES_POST_GOAL = "user_publishes_post".freeze
USER_CREATES_PAGEVIEW_GOAL = "user_creates_pageview".freeze
USER_CREATES_COMMENT_GOAL = "user_creates_comment".freeze
USER_CREATES_ARTICLE_REACTION_GOAL = "user_creates_article_reaction".freeze
def self.call(...)
new(...).call
end
def initialize(user:, goal:, experiments:, start_date: nil)
@user = user
@goal = goal
@experiments = experiments
@start_date = start_date&.beginning_of_day
end
attr_reader :experiments, :user, :goal
def call
# It's okay that there are no experiments.
return if experiments.nil?
experiments.each do |key, data|
# We've already declared a winner, let's not do any of the processing
next if data.key?("winner")
experiment_start_date = @start_date || data.fetch("started_at").beginning_of_day
experiment = key.to_sym
convert(experiment: experiment, experiment_start_date: experiment_start_date)
end
end
private
def convert(experiment:, experiment_start_date:)
case goal
# We have special conditional goals for some where we look for past events for cummulative wins
# Otherwise we convert the goal as given.
when USER_CREATES_PAGEVIEW_GOAL
convert_pageview_goal(experiment: experiment, experiment_start_date: experiment_start_date)
when USER_CREATES_COMMENT_GOAL # comments goal. Only page views and comments are currently active.
convert_comment_goal(experiment: experiment, experiment_start_date: experiment_start_date)
when USER_PUBLISHES_POST_GOAL
convert_post_goal(experiment: experiment, experiment_start_date: experiment_start_date)
when USER_CREATES_ARTICLE_REACTION_GOAL
convert_reaction_goal(experiment: experiment, experiment_start_date: experiment_start_date)
else
field_test_converted(experiment, participant: user, goal: goal) # base single comment goal.
end
end
def convert_pageview_goal(experiment:, experiment_start_date:)
# TODO: Remove once we know that this test is not over-heating the application. That would be a
# few days after the deploy to DEV of this change.
if FeatureFlag.accessible?(:field_test_event_single_create_pageview)
field_test_converted(experiment, participant: user, goal: goal) # base is someone viewed a page
end
pageview_goal(experiment,
[7.days.ago, experiment_start_date].max,
"DATE(created_at)",
2,
"user_views_pages_on_at_least_two_different_days_within_a_week")
pageview_goal(experiment,
[7.days.ago, experiment_start_date].max,
"DATE(created_at)",
4,
"user_views_pages_on_at_least_four_different_days_within_a_week")
pageview_goal(experiment,
[24.hours.ago, experiment_start_date].max,
"DATE_PART('hour', created_at)",
3,
"user_views_pages_on_at_least_three_different_hours_within_a_day")
pageview_goal(experiment,
[24.hours.ago, experiment_start_date].max,
"DATE_PART('hour', created_at)",
4,
"user_views_pages_on_at_least_four_different_hours_within_a_day")
pageview_goal(experiment,
[14.days.ago, experiment_start_date].max,
"DATE(created_at)",
9,
"user_views_pages_on_at_least_nine_different_days_within_two_weeks")
pageview_goal(experiment,
[5.days.ago, experiment_start_date].max,
"DATE_PART('hour', created_at)",
12,
"user_views_pages_on_at_least_twelve_different_hours_within_five_days")
end
def convert_comment_goal(experiment:, experiment_start_date:)
field_test_converted(experiment, participant: user, goal: goal) # base single comment goal.
comment_goal(experiment,
[7.days.ago, experiment_start_date].max,
"DATE(created_at)",
4,
"user_creates_comment_on_at_least_four_different_days_within_a_week")
end
def convert_post_goal(experiment:, experiment_start_date:)
field_test_converted(experiment, participant: user, goal: goal) # base is we created a post
post_goal_with_group(experiment,
[7.days.ago, experiment_start_date].max,
"DATE(published_at)",
4,
"user_publishes_post_on_four_different_days_within_a_week")
post_goal(experiment,
[7.days.ago, experiment_start_date].max,
2,
"user_publishes_post_at_least_two_times_within_week")
post_goal(experiment,
[14.days.ago, experiment_start_date].max,
2,
"user_publishes_post_at_least_two_times_within_two_weeks")
end
def convert_reaction_goal(experiment:, experiment_start_date:)
field_test_converted(experiment, participant: user, goal: goal) # base is we created a post
reaction_goal(experiment,
[7.days.ago, experiment_start_date].max,
"DATE(created_at)",
4,
"user_creates_article_reaction_on_four_different_days_within_a_week")
end
def pageview_goal(experiment, time_start, group_value, min_count, goal)
page_view_counts = user.page_views.where("created_at > ?", time_start)
.group(group_value).count.values
page_view_counts.delete(0)
return unless page_view_counts.size >= min_count
field_test_converted(experiment, participant: user, goal: goal)
end
def post_goal(experiment, time_start, min_count, goal)
return unless user.articles.published.where("published_at > ?", time_start).count >= min_count
field_test_converted(experiment, participant: user, goal: goal)
end
def post_goal_with_group(experiment, time_start, group_value, min_count, goal)
post_publication_counts = user.articles.published.where("published_at > ?", time_start)
.group(group_value).count.values
return unless post_publication_counts.size >= min_count
field_test_converted(experiment, participant: user, goal: goal)
end
def comment_goal(experiment, time_start, group_value, min_count, goal)
comment_counts = user.comments.where("created_at > ?", time_start)
.group(group_value).count.values
comment_counts.delete(0)
return unless comment_counts.size >= min_count
field_test_converted(experiment, participant: user, goal: goal)
end
def reaction_goal(experiment, time_start, group_value, min_count, goal)
reaction_counts = user.reactions
.only_articles # as per `Reaction#record_field_test_event` we only record reactions to articles
.public_category # as per `Reaction#record_field_test_event` we only record public category reactions
.where("created_at > ?", time_start)
.group(group_value).count.values
return unless reaction_counts.size >= min_count
field_test_converted(experiment, participant: user, goal: goal)
end
end
end