179 lines
7.8 KiB
Ruby
179 lines
7.8 KiB
Ruby
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
|