Skip to content

Commit d464ecd

Browse files
tranngocsamandrew
authored andcommitted
Add ab_record_extra_info to allow record extra info to alternative and display on dashboard. (#460)
1 parent fa24686 commit d464ecd

4 files changed

Lines changed: 111 additions & 2 deletions

File tree

lib/split/alternative.rb

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Alternative
88
attr_accessor :name
99
attr_accessor :experiment_name
1010
attr_accessor :weight
11+
attr_accessor :recorded_info
1112

1213
include Zscore
1314

@@ -127,10 +128,37 @@ def z_score(goal = nil)
127128
z_score = Split::Zscore.calculate(p_a, n_a, p_c, n_c)
128129
end
129130

131+
def extra_info
132+
data = Split.redis.hget(key, 'recorded_info')
133+
if data && data.length > 1
134+
begin
135+
JSON.parse(data)
136+
rescue
137+
{}
138+
end
139+
else
140+
{}
141+
end
142+
end
143+
144+
def record_extra_info(k, value = 1)
145+
@recorded_info = self.extra_info || {}
146+
147+
if value.kind_of?(Numeric)
148+
@recorded_info[k] ||= 0
149+
@recorded_info[k] += value
150+
else
151+
@recorded_info[k] = value
152+
end
153+
154+
Split.redis.hset key, 'recorded_info', (@recorded_info || {}).to_json
155+
end
156+
130157
def save
131158
Split.redis.hsetnx key, 'participant_count', 0
132159
Split.redis.hsetnx key, 'completed_count', 0
133160
Split.redis.hsetnx key, 'p_winner', p_winner
161+
Split.redis.hsetnx key, 'recorded_info', (@recorded_info || {}).to_json
134162
end
135163

136164
def validate!
@@ -140,7 +168,7 @@ def validate!
140168
end
141169

142170
def reset
143-
Split.redis.hmset key, 'participant_count', 0, 'completed_count', 0
171+
Split.redis.hmset key, 'participant_count', 0, 'completed_count', 0, 'recorded_info', nil
144172
unless goals.empty?
145173
goals.each do |g|
146174
field = "completed_count:#{g}"

lib/split/dashboard/views/_experiment.erb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@
55
<% end %>
66

77
<% experiment.calc_winning_alternatives %>
8+
<%
9+
extra_columns = []
10+
experiment.alternatives.each do |alternative|
11+
extra_info = alternative.extra_info || {}
12+
extra_columns += extra_info.keys
13+
end
14+
15+
extra_columns.uniq!
16+
summary_texts = {}
17+
extra_columns.each do |column|
18+
extra_infos = experiment.alternatives.map(&:extra_info).select{|extra_info| extra_info && extra_info[column] }
19+
if extra_infos[0][column].kind_of?(Numeric)
20+
summary_texts[column] = extra_infos.inject(0){|sum, extra_info| sum += extra_info[column]}
21+
else
22+
summary_texts[column] = "N/A"
23+
end
24+
end
25+
%>
26+
827

928
<div class="<%= experiment_class %>" data-name="<%= experiment.name %>" data-complete="<%= experiment.has_winner? %>">
1029
<div class="experiment-header">
@@ -32,6 +51,9 @@
3251
<th>Non-finished</th>
3352
<th>Completed</th>
3453
<th>Conversion Rate</th>
54+
<% extra_columns.each do |column| %>
55+
<th><%= column %></th>
56+
<% end %>
3557
<th>
3658
<form>
3759
<select id="dropdown-<%=experiment.jstring(goal)%>" name="dropdown-<%=experiment.jstring(goal)%>">
@@ -82,6 +104,9 @@
82104
});
83105
});
84106
</script>
107+
<% extra_columns.each do |column| %>
108+
<td><%= alternative.extra_info && alternative.extra_info[column] %></td>
109+
<% end %>
85110
<td>
86111
<div class="box-<%=experiment.jstring(goal)%> confidence-<%=experiment.jstring(goal)%>">
87112
<span title='z-score: <%= round(alternative.z_score(goal), 3) %>'><%= confidence_level(alternative.z_score(goal)) %></span>
@@ -90,7 +115,7 @@
90115
<div class="box-<%=experiment.jstring(goal)%> probability-<%=experiment.jstring(goal)%>">
91116
<span title="p_winner: <%= round(alternative.p_winner(goal), 3) %>"><%= number_to_percentage(round(alternative.p_winner(goal), 3)) %>%</span>
92117
</div>
93-
</td>
118+
</td>
94119
<td>
95120
<% if experiment.has_winner? %>
96121
<% if experiment.winner.name == alternative.name %>
@@ -118,6 +143,11 @@
118143
<td><%= total_unfinished %></td>
119144
<td><%= total_completed %></td>
120145
<td>N/A</td>
146+
<% extra_columns.each do |column| %>
147+
<td>
148+
<%= summary_texts[column] %>
149+
</td>
150+
<% end %>
121151
<td>N/A</td>
122152
<td>N/A</td>
123153
</tr>

lib/split/helper.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@ def ab_finished(metric_descriptor, options = {:reset => true})
7676
Split.configuration.db_failover_on_db_error.call(e)
7777
end
7878

79+
def ab_record_extra_info(metric_descriptor, key, value = 1)
80+
return if exclude_visitor? || Split.configuration.disabled?
81+
metric_descriptor, goals = normalize_metric(metric_descriptor)
82+
experiments = Metric.possible_experiments(metric_descriptor)
83+
84+
if experiments.any?
85+
experiments.each do |experiment|
86+
alternative_name = ab_user[experiment.key]
87+
88+
if alternative_name
89+
alternative = experiment.alternatives.find{|alt| alt.name == alternative_name}
90+
alternative.record_extra_info(key, value) if alternative
91+
end
92+
end
93+
end
94+
rescue => e
95+
raise unless Split.configuration.db_failover
96+
Split.configuration.db_failover_on_db_error.call(e)
97+
end
98+
7999
def override_present?(experiment_name)
80100
override_alternative(experiment_name)
81101
end

spec/alternative_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,35 @@
274274
expect(control.z_score(goal2)).to eq('N/A')
275275
end
276276
end
277+
278+
describe "extra_info" do
279+
it "reads saved value of recorded_info in redis" do
280+
saved_recorded_info = {"key_1" => 1, "key_2" => "2"}
281+
Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}", 'recorded_info', saved_recorded_info.to_json
282+
extra_info = alternative.extra_info
283+
284+
expect(extra_info).to eql(saved_recorded_info)
285+
end
286+
end
287+
288+
describe "record_extra_info" do
289+
it "saves key" do
290+
alternative.record_extra_info("signup", 1)
291+
expect(alternative.extra_info["signup"]).to eql(1)
292+
end
293+
294+
it "adds value to saved key's value second argument is number" do
295+
alternative.record_extra_info("signup", 1)
296+
alternative.record_extra_info("signup", 2)
297+
expect(alternative.extra_info["signup"]).to eql(3)
298+
end
299+
300+
it "sets saved's key value to the second argument if it's a string" do
301+
alternative.record_extra_info("signup", "Value 1")
302+
expect(alternative.extra_info["signup"]).to eql("Value 1")
303+
304+
alternative.record_extra_info("signup", "Value 2")
305+
expect(alternative.extra_info["signup"]).to eql("Value 2")
306+
end
307+
end
277308
end

0 commit comments

Comments
 (0)