comparison lucene/src/luan/modules/lucene/Ab_testing.luan @ 291:a35d1177bbf0

implement Ab_testing.web_page() git-svn-id: https://luan-java.googlecode.com/svn/trunk@292 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Tue, 09 Dec 2014 23:24:07 +0000
parents 8afe9f2fdfec
children e8a2153f6ce1
comparison
equal deleted inserted replaced
290:9a0cc6d9b27b 291:a35d1177bbf0
1 import "luan:Math" 1 import "luan:Math"
2 import "luan:Table" 2 import "luan:Table"
3 import "luan:Io"
4 import "luan:web/Http"
3 5
4 6
5 function of(index) 7 function of(index)
6 8
7 local ab_testing = {} 9 local ab_testing = {}
10 ab_testing.test_list = {} 12 ab_testing.test_list = {}
11 13
12 function ab_testing.test(test) 14 function ab_testing.test(test)
13 test.name or error "name not defined" 15 test.name or error "name not defined"
14 test.values or error "values not defined" 16 test.values or error "values not defined"
17
18 -- list of event names
19 test.events or error "events not defined"
20
21 -- map of event name to aggregator factory
22 test.aggregator_factories or error "aggregator_factories not defined"
23
15 -- test.date_field is optional 24 -- test.date_field is optional
16 25
17 local field = "ab_test_" .. test.name 26 local field = "ab_test_" .. test.name
18 index.fields[field] == nil or error("test "+test.name+" already defined") 27 index.fields[field] == nil or error("test "+test.name+" already defined")
19 index.fields[field] = field .. " index" 28 index.fields[field] = field .. " index"
20 test.field = field 29 test.field = field
21 30
22 -- pass in map of name to aggregator factory 31 -- returns map of event name to (map of value to result) and "start_date"
23 -- returns map of name to (map of value to result) and "start_date" 32 function test.results()
24 function test.results(aggregator_factories)
25 return index.Searcher( function(searcher) 33 return index.Searcher( function(searcher)
26 local results = {} 34 local results = {}
27 for name in pairs(aggregator_factories) do 35 for name in pairs(test.aggregator_factories) do
28 results[name] = {} 36 results[name] = {}
29 end 37 end
30 local date_field = test.date_field 38 local date_field = test.date_field
31 local start_date = nil 39 local start_date = nil
32 for _, value in ipairs(test.values) do 40 for _, value in ipairs(test.values) do
33 local aggregators = {} 41 local aggregators = {}
34 for name, factory in pairs(aggregator_factories) do 42 for name, factory in pairs(test.aggregator_factories) do
35 aggregators[name] = factory() 43 aggregators[name] = factory()
36 end 44 end
37 local query = { [field] = value } 45 local query = index.Query.term{ [field] = value }
38 searcher.search(query, function(doc) 46 searcher.search(query, function(doc)
39 for _, aggregator in pairs(aggregators) do 47 for _, aggregator in pairs(aggregators) do
40 aggregator.aggregate(doc) 48 aggregator.aggregate(doc)
41 end 49 end
42 if date_field ~= nil then 50 if date_field ~= nil then
53 results.start_date = start_date 61 results.start_date = start_date
54 return results 62 return results
55 end ) 63 end )
56 end 64 end
57 65
66 function test.fancy_results()
67 local events = test.events
68 local results = test.results()
69 local fancy = {}
70 fancy.start_date = results.start_date
71 local event = events[1]
72 fancy[event] = {}
73 for value, count in pairs(results[event]) do
74 fancy[event][value] = {}
75 fancy[event][value].count = count
76 fancy[event][value].pct_of_total = 100
77 fancy[event][value].pct_of_prev = 100
78 end
79 local all = results[event]
80 local prev = all
81 for i in range(2,#events) do
82 event = events[i]
83 fancy[event] = {}
84 for value, count in pairs(results[event]) do
85 fancy[event][value] = {}
86 fancy[event][value].count = count
87 fancy[event][value].pct_of_total = percent(count,all[value])
88 fancy[event][value].pct_of_prev = percent(count,prev[value])
89 end
90 prev = results[event]
91 end
92 return fancy
93 end
94
95
58 ab_testing.test_map[test.name] = test 96 ab_testing.test_map[test.name] = test
59 ab_testing.test_list[#ab_testing.test_list + 1] = test 97 ab_testing.test_list[#ab_testing.test_list + 1] = test
60 98
61 return test 99 return test
62 end 100 end
84 else 122 else
85 for _, test in ipairs(tests) do 123 for _, test in ipairs(tests) do
86 doc[test.field] = values[test.name] 124 doc[test.field] = values[test.name]
87 end 125 end
88 end 126 end
127 end
128
129 function ab_testing.web_page(test_names)
130 return { service = function()
131 local results = {}
132 for _, name in ipairs(test_names) do
133 local test = ab_testing.test_map[name]
134 test or error("test not found: "..name)
135 results[name] = test.fancy_results()
136 end
137 Io.stdout = Http.response.text_writer()
138 html(test_names,ab_testing.test_map,results)
139 end }
89 end 140 end
90 141
91 return ab_testing 142 return ab_testing
92 end 143 end
93 144
106 end 157 end
107 return aggregator 158 return aggregator
108 end 159 end
109 end 160 end
110 161
111 count_all = count( function() return true end ) 162 count_all = count( function(doc) return true end )
112 163
113 -- fn(doc) should return number to add to result, return 0 for nothing 164 -- fn(doc) should return number to add to result, return 0 for nothing
114 function sum(fn) 165 function sum(fn)
115 return function() 166 return function()
116 local aggregator = {} 167 local aggregator = {}
122 end 173 end
123 end 174 end
124 175
125 176
126 177
127 local function percent(x,total) 178 function percent(x,total)
128 if total==0 then 179 if total==0 then
129 return 0 180 return 0
130 else 181 else
131 return 100 * x / total 182 return 100 * x / total
132 end 183 end
133 end 184 end
134 185
135 function fancy(results,names) 186
136 local fancy = {} 187 function html(test_names,tests,results) %>
137 fancy.start_date = results.start_date 188 <html>
138 local name = names[1] 189 <body>
139 fancy[name] = {} 190 <h2>A/B Tests</h2>
140 for value, count in pairs(result[name]) do 191 <%
141 fancy[name][value] = {} 192 for _, test_name in ipairs(test_names) do
142 fancy[name][value].count = count 193 local test = tests[test_name]
143 fancy[name][value].pct_of_total = 100 194 local result = results[test_name]
144 fancy[name][value].pct_of_prev = 100 195 local n = #test.values
145 end 196 %>
146 local all = result[name] 197 <h3><%=test_name%></h3>
147 local prev = all 198 <table>
148 for i in range(2,#names) do 199 <tr>
149 name = names[i] 200 <th>Event</th>
150 fancy[name] = {} 201 <th class="top" colspan="<%=n%>">Count</th>
151 for value, count in pairs(result[name]) do 202 <th class="top" colspan="<%=n%>">% of total</th>
152 fancy[name][value] = {} 203 <th class="top" colspan="<%=n%>">% of prev</th>
153 fancy[name][value].count = count 204 </tr>
154 fancy[name][value].pct_of_total = percent(count,all[value]) 205 <tr>
155 fancy[name][value].pct_of_prev = percent(count,prev[value]) 206 <th></th>
156 end 207 <%
157 prev = result[name] 208 for _ in range(1,3) do
158 end 209 for _, value in ipairs(test.values) do
159 return fancy 210 %>
160 end 211 <th class="top"><%=value%></th>
212 <%
213 end
214 end
215 %>
216 </tr>
217 <%
218 for _, event in ipairs(test.events) do
219 local event_values = result[event]
220 %>
221 <tr>
222 <td><%=event%></td>
223 <%
224 for _, value in ipairs(test.values) do
225 %>
226 <td><%=event_values[value].count%></th>
227 <%
228 end
229 for _, value in ipairs(test.values) do
230 %>
231 <td><%=event_values[value].pct_of_total%></th>
232 <%
233 end
234 for _, value in ipairs(test.values) do
235 %>
236 <td><%=event_values[value].pct_of_prev%></th>
237 <%
238 end
239 %>
240 </tr>
241 <%
242 end
243 %>
244 </table>
245 <%
246 end
247 %>
248 </body>
249 </html>
250 <% end