Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: modules/statsmaster/files/usercounts.html

Issue 5143068516810752: Added new stats server and user counter page (Closed)
Patch Set: Centralized stats processing Created Dec. 20, 2013, 3:47 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title>User counter</title>
6 <style type="text/css">
7 form
8 {
9 display: inline;
10 }
11
12 .spacer
13 {
14 margin: 0 10px;
15 }
16
17 table
18 {
19 border-collapse: collapse;
20 }
21
22 td, th
23 {
24 padding: 5px;
25 border: 1px solid black;
26 }
27
28 td
29 {
30 text-align: right;
31 }
32 </style>
33 <script type="text/javascript">
34 const MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
35
36 function zeroPad(number, digits)
37 {
38 var result = number.toString(10);
39 while (result.length < digits)
40 result = "0" + result;
41 return result;
42 }
43
44 function formatDate(date)
45 {
46 return zeroPad(date.getFullYear(), 4) + "-" + zeroPad(date.getMonth() + 1, 2) + "-" + zeroPad(date.getDate(), 2);
47 }
48
49 var curMonth = null;
50 var curMonthFilters = null;
51 var curMonthNotifications = null;
52
53 function checkStats(data, fromDate, offset, callback)
54 {
55 var date = new Date(fromDate.getTime() + offset * MILLIS_IN_DAY);
56
57 function doDownload(url, descr, callback)
58 {
59 var request = new XMLHttpRequest();
60 request.open("GET", url);
61 request.addEventListener("load", function()
62 {
63 if (request.status != 200)
64 alert("Failed to download " + descr + ", got response " + request.st atus);
65 else
66 callback(JSON.parse(request.responseText));
67 }, false);
68 request.addEventListener("error", function()
69 {
70 alert("Failed to download " + descr);
71 }, false);
72 request.send(null);
73 }
74
75 function doCheckStats(source, type)
76 {
77 if (!(date.getDate() in source.day))
78 return;
79
80 var intervals = source.day[date.getDate()].previousDownload;
81 for (var interval in intervals)
82 {
83 if (!intervals.hasOwnProperty(interval))
84 continue;
85
86 var days;
87 if (/^\d+ day/.test(interval))
88 days = parseInt(interval, 10);
89 else if (/^\d+ month/.test(interval))
90 days = 31;
91 else
92 continue;
93
94 // Most requests claiming that last download was long ago are bots or broken installations
95 if (days > 30)
96 continue;
97
98 var hits = intervals[interval].hits;
99 for (var i = Math.max(offset, 0); i < data.length; i++)
100 {
101 if (i - offset < 30 && i - offset + days >= 30)
102 data[i]["30day " + type] += hits;
103 if (i - offset < 7 && i - offset + days >= 7)
104 data[i]["7day " + type] += hits;
105 }
106 }
107 }
108
109 var month = zeroPad(date.getFullYear(), 4) + zeroPad(date.getMonth() + 1, 2);
110 if (month != curMonth)
111 {
112 doDownload("/raw/subscription/" + month + "/exceptionrules.txt.json", "a cceptable ads data for month " + month, function(json)
113 {
114 curMonthFilters = json;
115 doDownload("/raw/notification/" + month + "/notification.json.json", " notification data for month " + month, function(json)
116 {
117 curMonthNotifications = json;
118 curMonth = month;
119 doCheckStats(curMonthFilters, "acceptable");
120 doCheckStats(curMonthNotifications, "total");
121 callback();
122 });
123 });
124 }
125 else
126 {
127 doCheckStats(curMonthFilters, "acceptable");
128 doCheckStats(curMonthNotifications, "total");
129 callback();
130 }
131 }
132
133 function showResults(fromDate, data)
134 {
135 function createCell(row, text)
136 {
137 var cell = document.createElement("td");
138 cell.appendChild(document.createTextNode(text));
139 row.appendChild(cell);
140 }
141
142 var resultBody = document.getElementById("resultBody");
143 while (resultBody.lastChild)
144 resultBody.removeChild(resultBody.lastChild);
145
146 for (var i = 0; i < data.length; i++)
147 {
148 var date = new Date(fromDate.getTime() + i * MILLIS_IN_DAY);
149
150 var row = document.createElement("tr");
151 createCell(row, date.toLocaleDateString());
152 createCell(row, data[i]["7day total"].toLocaleString());
153 createCell(row, data[i]["7day acceptable"].toLocaleString());
154 createCell(row, (100 - data[i]["7day acceptable"] / data[i]["7day total" ] * 100).toFixed(2) + "%");
155 createCell(row, data[i]["30day total"].toLocaleString());
156 createCell(row, data[i]["30day acceptable"].toLocaleString());
157 createCell(row, (100 - data[i]["30day acceptable"] / data[i]["30day tota l"] * 100).toFixed(2) + "%");
158 resultBody.appendChild(row);
159 }
160
161 document.getElementById("result").removeAttribute("hidden");
162 document.getElementById("wait").setAttribute("hidden", "hidden");
163 }
164
165 function calculate()
166 {
167 document.getElementById("result").setAttribute("hidden", "hidden");
168 document.getElementById("wait").removeAttribute("hidden");
169
170 var fromDate = document.getElementById("from").value.replace(/^\s+/, "").r eplace(/\s+$/, "");
171 var match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(fromDate);
172 if (!match)
173 {
174 alert("Wrong start date format.");
175 document.getElementById("from").focus();
176 return;
177 }
178 fromDate = new Date(parseInt(match[1], 10), parseInt(match[2], 10) - 1, pa rseInt(match[3], 10), 12);
179
180 var toDate = document.getElementById("to").value.replace(/^\s+/, "").repla ce(/\s+$/, "");
181 var match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(toDate);
182 if (!match)
183 {
184 alert("Wrong end date format.");
185 document.getElementById("to").focus();
186 return;
187 }
188 toDate = new Date(parseInt(match[1], 10), parseInt(match[2], 10) - 1, pars eInt(match[3], 10), 12);
189
190 if (fromDate > toDate)
191 {
192 var tmp = fromDate;
193 fromDate = toDate;
194 toDate = tmp;
195 }
196
197 var data = [];
198 for (var d = fromDate; formatDate(d) <= formatDate(toDate); d = new Date(d .getTime() + MILLIS_IN_DAY))
199 data.push({"7day total": 0, "7day acceptable": 0, "30day total": 0, "30d ay acceptable": 0});
200
201 var offset = -30;
202 var callback = function()
203 {
204 offset++;
205 if (offset >= data.length)
206 {
207 showResults(fromDate, data);
208 return;
209 }
210 checkStats(data, fromDate, offset, callback);
211 };
212 callback();
213 }
214
215 window.onload = function()
216 {
217 document.getElementById("from").value = formatDate(new Date(Date.now() - 7 * MILLIS_IN_DAY));
218 document.getElementById("to").value = formatDate(new Date(Date.now() - MIL LIS_IN_DAY));
219 }
220 </script>
221 </head>
222 <body>
223 <p>
224 Please enter the date range that you need user statistics for:
225 </p>
226 <p>
227 <form action="javascript://" onsubmit="calculate();">
228 Start date (yyyy-mm-dd format): <input id="from" type="text">
229 <span class="spacer"></span>
230 End date (yyyy-mm-dd format): <input id="to" type="text">
231 <span class="spacer"></span>
232 <button type="submit">Calculate</button>
233 </form>
234 </p>
235 <p id="wait" hidden>
236 Please wait, data is being fetched...
237 </p>
238 <table id="result" hidden>
239 <tr>
240 <th rowspan="2">Date</th>
241 <th colspan="3">7-day active users</th>
242 <th colspan="3">30-day active users</th>
243 </tr>
244 <tr>
245 <th>Total</th>
246 <th>Acceptable ads</th>
247 <th>Opt-out rate</th>
248 <th>Total</th>
249 <th>Acceptable ads</th>
250 <th>Opt-out rate</th>
251 </tr>
252 <tbody id="resultBody">
253 </tbody>
254 </table>
255 </body>
256 </html>
OLDNEW

Powered by Google App Engine
This is Rietveld