changeset 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 9a0cc6d9b27b
children e8a2153f6ce1
files core/src/luan/LuanFunction.java dist/jars/luan-core-trunk.jar dist/jars/luan-logging-trunk.jar dist/jars/luan-lucene-trunk.jar dist/jars/luan-mail-trunk.jar dist/jars/luan-web-trunk.jar lucene/src/luan/modules/lucene/Ab_testing.luan lucene/src/luan/modules/lucene/LuceneSearcher.java
diffstat 8 files changed, 130 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/core/src/luan/LuanFunction.java	Tue Dec 09 04:39:18 2014 +0000
+++ b/core/src/luan/LuanFunction.java	Tue Dec 09 23:24:07 2014 +0000
@@ -12,7 +12,7 @@
 	}
 
 	@Override public String repr() {
-		return "<function>";
+		return "<" + toString() + ">";
 	}
 
 }
Binary file dist/jars/luan-core-trunk.jar has changed
Binary file dist/jars/luan-logging-trunk.jar has changed
Binary file dist/jars/luan-lucene-trunk.jar has changed
Binary file dist/jars/luan-mail-trunk.jar has changed
Binary file dist/jars/luan-web-trunk.jar has changed
--- a/lucene/src/luan/modules/lucene/Ab_testing.luan	Tue Dec 09 04:39:18 2014 +0000
+++ b/lucene/src/luan/modules/lucene/Ab_testing.luan	Tue Dec 09 23:24:07 2014 +0000
@@ -1,5 +1,7 @@
 import "luan:Math"
 import "luan:Table"
+import "luan:Io"
+import "luan:web/Http"
 
 
 function of(index)
@@ -12,6 +14,13 @@
 	function ab_testing.test(test)
 		test.name or error "name not defined"
 		test.values or error "values not defined"
+
+		-- list of event names
+		test.events or error "events not defined"
+
+		-- map of event name to aggregator factory
+		test.aggregator_factories or error "aggregator_factories not defined"
+
 		-- test.date_field is optional
 
 		local field = "ab_test_" .. test.name
@@ -19,22 +28,21 @@
 		index.fields[field] = field .. " index"
 		test.field = field
 
-		-- pass in map of name to aggregator factory
-		-- returns map of name to (map of value to result) and "start_date"
-		function test.results(aggregator_factories)
+		-- returns map of event name to (map of value to result) and "start_date"
+		function test.results()
 			return index.Searcher( function(searcher)
 				local results = {}
-				for name in pairs(aggregator_factories) do
+				for name in pairs(test.aggregator_factories) do
 					results[name] = {}
 				end
 				local date_field = test.date_field
 				local start_date = nil
 				for _, value in ipairs(test.values) do
 					local aggregators = {}
-					for name, factory in pairs(aggregator_factories) do
+					for name, factory in pairs(test.aggregator_factories) do
 						aggregators[name] = factory()
 					end
-					local query = { [field] = value }
+					local query = index.Query.term{ [field] = value }
 					searcher.search(query, function(doc)
 						for _, aggregator in pairs(aggregators) do
 							aggregator.aggregate(doc)
@@ -55,6 +63,36 @@
 			end )
 		end
 
+		function test.fancy_results()
+			local events = test.events
+			local results = test.results()
+			local fancy = {}
+			fancy.start_date = results.start_date
+			local event = events[1]
+			fancy[event] = {}
+			for value, count in pairs(results[event]) do
+				fancy[event][value] = {}
+				fancy[event][value].count = count
+				fancy[event][value].pct_of_total = 100
+				fancy[event][value].pct_of_prev = 100
+			end
+			local all = results[event]
+			local prev = all
+			for i in range(2,#events) do
+				event = events[i]
+				fancy[event] = {}
+				for value, count in pairs(results[event]) do
+					fancy[event][value] = {}
+					fancy[event][value].count = count
+					fancy[event][value].pct_of_total = percent(count,all[value])
+					fancy[event][value].pct_of_prev = percent(count,prev[value])
+				end
+				prev = results[event]
+			end
+			return fancy
+		end
+
+
 		ab_testing.test_map[test.name] = test
 		ab_testing.test_list[#ab_testing.test_list + 1] = test
 
@@ -88,6 +126,19 @@
 		end
 	end
 
+	function ab_testing.web_page(test_names)
+		return { service = function()
+			local results = {}
+			for _, name in ipairs(test_names) do
+				local test = ab_testing.test_map[name]
+				test or error("test not found: "..name)
+				results[name] = test.fancy_results()
+			end
+			Io.stdout = Http.response.text_writer()
+			html(test_names,ab_testing.test_map,results)
+		end }
+	end
+
 	return ab_testing
 end
 
@@ -108,7 +159,7 @@
 	end
 end
 
-count_all = count( function() return true end )
+count_all = count( function(doc) return true end )
 
 -- fn(doc) should return number to add to result, return 0 for nothing
 function sum(fn)
@@ -124,7 +175,7 @@
 
 
 
-local function percent(x,total)
+function percent(x,total)
 	if total==0 then
 		return 0
 	else
@@ -132,29 +183,68 @@
 	end
 end
 
-function fancy(results,names)
-	local fancy = {}
-	fancy.start_date = results.start_date
-	local name = names[1]
-	fancy[name] = {}
-	for value, count in pairs(result[name]) do
-		fancy[name][value] = {}
-		fancy[name][value].count = count
-		fancy[name][value].pct_of_total = 100
-		fancy[name][value].pct_of_prev = 100
+
+function html(test_names,tests,results) %>
+<html>
+<body>
+<h2>A/B Tests</h2>
+<%
+	for _, test_name in ipairs(test_names) do
+		local test = tests[test_name]
+		local result = results[test_name]
+		local n = #test.values
+		%>
+		<h3><%=test_name%></h3>
+		<table>
+			<tr>
+				<th>Event</th>
+				<th class="top" colspan="<%=n%>">Count</th>
+				<th class="top" colspan="<%=n%>">% of total</th>
+				<th class="top" colspan="<%=n%>">% of prev</th>
+			</tr>
+			<tr>
+				<th></th>
+				<%
+				for _ in range(1,3) do
+					for _, value in ipairs(test.values) do
+						%>
+						<th class="top"><%=value%></th>
+						<%
+					end
+				end
+				%>
+			</tr>
+			<%
+			for _, event in ipairs(test.events) do
+				local event_values = result[event]
+				%>
+				<tr>
+					<td><%=event%></td>
+					<%
+					for _, value in ipairs(test.values) do
+						%>
+						<td><%=event_values[value].count%></th>
+						<%
+					end
+					for _, value in ipairs(test.values) do
+						%>
+						<td><%=event_values[value].pct_of_total%></th>
+						<%
+					end
+					for _, value in ipairs(test.values) do
+						%>
+						<td><%=event_values[value].pct_of_prev%></th>
+						<%
+					end
+					%>
+				</tr>
+				<%
+			end
+			%>
+		</table>
+		<%
 	end
-	local all = result[name]
-	local prev = all
-	for i in range(2,#names) do
-		name = names[i]
-		fancy[name] = {}
-		for value, count in pairs(result[name]) do
-			fancy[name][value] = {}
-			fancy[name][value].count = count
-			fancy[name][value].pct_of_total = percent(count,all[value])
-			fancy[name][value].pct_of_prev = percent(count,prev[value])
-		end
-		prev = result[name]
-	end
-	return fancy
-end
+%>
+</body>
+</html>
+<% end
--- a/lucene/src/luan/modules/lucene/LuceneSearcher.java	Tue Dec 09 04:39:18 2014 +0000
+++ b/lucene/src/luan/modules/lucene/LuceneSearcher.java	Tue Dec 09 23:24:07 2014 +0000
@@ -65,8 +65,12 @@
 	};
 
 	private static abstract class MyCollector extends Collector {
+		int docBase;
+
 		@Override public void setScorer(Scorer scorer) {}
-		@Override public void setNextReader(AtomicReaderContext context) {}
+		@Override public void setNextReader(AtomicReaderContext context) {
+			this.docBase = context.docBase;
+		}
 		@Override public boolean acceptsDocsOutOfOrder() {
 			return true;
 		}
@@ -78,7 +82,7 @@
 			Collector col = new MyCollector() {
 				@Override public void collect(int doc) {
 					try {
-						LuanTable docTbl = doc(luan,doc);
+						LuanTable docTbl = doc(luan,docBase+doc);
 						luan.call(fn,new Object[]{docTbl});
 					} catch(LuanException e) {
 						throw new LuanRuntimeException(e);