Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Json support, jqplot renderin, repair latest

* Add json support, slightly different format than old code however.
* Use jqplot to visualize results instead of css rendering.
* Repair 'latest' support
  • Loading branch information...
commit e8ad4b9468ef91dcd7e05f5d62c4e05d77ba1d35 1 parent 6aaccae
@abelsson authored
View
46 web/index.py
@@ -14,18 +14,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import urllib
-#import datetime
-#from time import time
import sys
import os
+import math
+import urllib
+import web
+import json
+from web import form
-
-sys.path.extend([os.path.dirname(__file__)]) #, os.path.join(os.path.dirname(__file__), 'templates')])
+sys.path.extend([os.path.dirname(__file__)])
import config
-import web
-from web import form
import model
urls = (
@@ -34,6 +33,8 @@
'/([a-z.-]*)/([0-9]{6})/(.*)', 'result',
'/([a-z.-]*)/latest/(.*)', 'latest_result',
'/([a-z.-]*)/top', 'latest_top',
+'/json/([a-z.-]*)/([0-9]{6})/(.*)', 'json_result',
+'/json/([a-z.-]*)/latest/(.*)', 'json_latest_result',
'/.*', 'notfound'
)
@@ -44,7 +45,8 @@ def __init__(self, *args, **kwargs):
self.render = web.template.render(self.template_dir, base = 'layout',
globals = { 'unquote' : urllib.unquote,
'sum' : sum,
- 'project_link' : project_link})
+ 'project_link' : project_link,
+ 'round_magnitude' : round_magnitude})
def init_form(self, proj = None, date = None, page = None):
years, latest = model.get_dates()
@@ -58,6 +60,9 @@ def init_form(self, proj = None, date = None, page = None):
form.Textbox('inputbox', form.notnull,value=page, id='ib1', description=''),
form.Button('Top'))
+#
+# 404 page
+#
class notfound:
def GET(self):
return web.notfound()
@@ -135,6 +140,25 @@ def GET(self,proj=None, page=None):
return result.GET(self, proj, 'latest', page)
+#
+# json support
+#
+class json_result(result):
+ def GET(self,proj=None, date=None, page=None):
+
+ if self.block_scraper():
+ return self.render.blocked()
+
+ counts, rank, execution_time = self.fetch_results(proj, date, page)
+ #web.header('Content-Type', 'application/json')
+ return json.dumps({ "title" : page,
+ "month" : date,
+ "daily_views" : counts.values(),
+ "rank" : rank })
+
+class json_latest_result(json_result):
+ def GET(self,proj=None, page=None):
+ return result.GET(self, proj, 'latest', page)
#
# Utility functions
@@ -154,6 +178,12 @@ def project_link(proj):
return proj + ".wikipedia.org"
+def round_magnitude(n):
+ ''' Return a nice and round number, divisible by 6 which is larger than n'''
+ x = n/6.0
+ n = math.ceil(x/(10**math.floor(math.log10(x))))
+ return int(6.0*n*10**math.floor(math.log10(x)))
+
if __name__ == '__main__':
if config.DEBUG:
app = web.application(urls, globals())
View
57 web/model.py
@@ -1,3 +1,19 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2011 Henrik Abelsson <henrik@grok.se>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
import datetime, time
import web
import config
@@ -10,7 +26,9 @@ def __init__(self, value = "Old format"):
def __str__(self):
return repr(self.value)
-db = web.database(dbn='mysql', host=config.db_host, user= config.db_user , pw=config.db_password, db='wikistats')
+db = web.database(dbn='mysql', host=config.db_host, user= config.db_user , pw=config.db_password, db='wikistats', charset=None)
+
+
def get_dates(start = None):
@@ -39,7 +57,10 @@ def get_top(proj):
def get_rank(page, proj):
rank = db.query("SELECT rank FROM top_%s WHERE project='%s' AND page='%s'" % (config.LATESTTOP,proj,page))
- return rank
+ try:
+ return rank[0].rank
+ except IndexError:
+ return -1
def get_latest_stats(page, proj):
''' Fetch statistics in a list of view counts, for the latest 30 days '''
@@ -51,16 +72,21 @@ def get_latest_stats(page, proj):
startd=today-datetime.timedelta(days=30)
ix = startd
for i in range(0,31):
- days[i] = ix.day
+ days[i] = "%d-%02d-%02d" % (ix.year, ix.month, ix.day)
ix += datetime.timedelta(days=1)
months = get_dates((startd.year, startd.month))[0]
- page_counts = []
+ page_counts = {}
for m in months:
try:
- page_counts.extend(_getcounts_new(db,m,proj,page).values())
+ page_counts = dict(page_counts.items() + _getcounts_new(db,m,proj,page).items())
except:
- page_counts.extend(_getcounts(db,m,proj,page).values())
- page_counts = page_counts[-30:]
+ page_counts = dict(page_counts.items() + _getcounts(db,m,proj,page).items())
+
+
+ for x in page_counts.keys():
+ if not x in days:
+ del page_counts[x]
+ #page_counts = page_counts[-30:]
end = time.time()
execution_time = end-start
@@ -97,7 +123,11 @@ def _getcounts(c,date,proj,page):
counts = {}
for day in all_days:
count = c.query("SELECT sum(hitcount) FROM "+day+" WHERE page=$page AND project=$project;", vars = {'page' : page, 'project' : proj })
- day = int(day.split("_")[1][-2:])
+ tmp = day.split("_")[1]
+ year = tmp[0:4]
+ month = tmp[4:6]
+ day = tmp[6:]
+ day = year + "-" + month + "-" + day
try:
counts[day] = int(count[0].values()[0])
except TypeError:
@@ -108,16 +138,14 @@ def _getcounts(c,date,proj,page):
def _getcounts_new(c,date,proj,page):
tables = _getalldays_new(c,date)
- counts = {}
- for i in range(0,32):
- counts[i]=0
+ counts = {}
if len(tables) != 1:
raise OldFormatException()
table = tables[0]
- count = c.query("SELECT sum(hitcount),sum(d1), sum(d2), sum(d3), sum(d4), sum(d5), sum(d6), sum(d7), sum(d8), sum(d9), sum(d10), sum(d11), sum(d12), sum(d13), sum(d14), sum(d15), sum(d16), sum(d17), sum(d18), sum(d19), sum(d20), sum(d21), sum(d22), sum(d23), sum(d24), sum(d25), sum(d26), sum(d27), sum(d28), sum(d29), sum(d30), sum(d31) FROM "+table+" WHERE page=$page AND project=$project;", vars = {'page' : page, 'project' : proj })
+ count = c.query("SELECT sum(hitcount),sum(d1), sum(d2), sum(d3), sum(d4), sum(d5), sum(d6), sum(d7), sum(d8), sum(d9), sum(d10), sum(d11), sum(d12), sum(d13), sum(d14), sum(d15), sum(d16), sum(d17), sum(d18), sum(d19), sum(d20), sum(d21), sum(d22), sum(d23), sum(d24), sum(d25), sum(d26), sum(d27), sum(d28), sum(d29), sum(d30), sum(d31) FROM "+table+" WHERE page=$page AND project=$project;", vars = {'page' : page.encode('utf-8'), 'project' : proj })
# One time bug in the database generation script
# caused this months dates to be offset for one
@@ -128,9 +156,10 @@ def _getcounts_new(c,date,proj,page):
fudge = 1
values = count[0]
-
+ year = date[0:4]
+ month = date[4:6]
for x in range(1,32):
- counts[x-fudge]=int(values["sum(d%d)" % x])
+ counts["%s-%s-%02d" % (year, month, x)]=int(values["sum(d%d)" % x])
return counts
View
7 web/static/css/wikistats.css
@@ -6,9 +6,8 @@ body {
background: #FFF;
margin: 0 5%;
padding: 1em;
- border: 3px solid gray;
- border-width: 0 3px;
- font-family: Verdana;
+ border: 0px solid gray;
+ font-family: "Helvetica Neue", Arial, Helvetica, "Nimbus Sans L", sans-serif;
}
h1 {
@@ -17,7 +16,7 @@ h1 {
letter-spacing: 1px;
margin: 1em -0.5em;
padding: 0 0.5em;
- border-bottom: 2px solid gray;
+ border-bottom: px solid gray;
}
#q-graph {
View
4 web/templates/index.html
@@ -22,7 +22,9 @@
<p>
Enter a wikipedia article title and press Go<br />
- $:form.render()
+ $for input in form.inputs:
+ $:input.render()
+
<input type="submit" value="Go"/>
</p>
</form>
View
13 web/templates/layout.html
@@ -31,6 +31,19 @@
})();
/* */
</script>
+
+
+<!--[if lt IE 9]><script language="javascript" type="text/javascript" src="/static/jqplot/excanvas.js"></script><![endif]-->
+<script language="javascript" type="text/javascript" src="/static/jqplot/jquery.min.js"></script>
+<script language="javascript" type="text/javascript" src="/static/jqplot/jquery.jqplot.min.js"></script>
+<link rel="stylesheet" type="text/css" href="/static/jqplot/jquery.jqplot.css" />
+<script type="text/javascript" src="/static/jqplot/plugins/jqplot.barRenderer.min.js"></script>
+<script type="text/javascript" src="/static/jqplot/plugins/jqplot.categoryAxisRenderer.min.js"></script>
+<script type="text/javascript" src="/static/jqplot/plugins/jqplot.pointLabels.min.js"></script>
+<script type="text/javascript" src="/static/jqplot/plugins/jqplot.dateAxisRenderer.min.js"></script>
+<script type="text/javascript" src="/static/jqplot/plugins/jqplot.highlighter.min.js"></script>
+<script type="text/javascript" src="/static/jqplot/plugins/jqplot.cursor.min.js"></script>
+<script type="text/javascript" src="/static/jqplot/plugins/jqplot.dateAxisRenderer.min.js"></script>
</head>
<body onLoad="sf();">
View
103 web/templates/results.html
@@ -7,53 +7,88 @@
<!-- urllib.quote_plus(page) -->
<p><a href="http://$link/wiki/$page">$page</a> has been viewed $total
-$if date == -1:
+$if date == 'latest':
times in the last 30 days.
$else:
times in $date.
$if rank > 0:
- This article ranked $rank in traffic on $proj.
+ This article ranked $rank in traffic on $link.
+
+
-
</p>
-<ul id="q-graph">
- $code:
- x_offset = 0
-
- def humround(n):
- if n < 1000:
- return "%.0f" % n
- if n < 1000000:
- return "%.1fk" % (n/1000.0)
- if n < 1000000000:
- return "%.1fM" % (n/1000000.0)
-
- $for i in range(1,32):
- <li class="qtr" style="width: 25px; left: ${(i-1)*20}px;">$i</li>
-
- $ count = int(counts.get(i,0))
- <li class="sent bar" style="height:${(count/max_count)*280}px; left: ${x_offset}px;">
- <p style="margin-left: -3px;">${humround(count)}</p>
- </li>
- $ x_offset += 20
-
- <li id="ticks">
- $for i in range(5,0,-1):
- <div class="tick" style="height: 59px;">
- $ x = (max_count/5*i)*1.071
- <p>${humround(x)}</p>
- </div>
- </li>
-</ul>
+
+<div id="chartdiv" style="height:400px;width:800px; "></div>
<form method="post" action="/">
-$:form.render()
+$for input in form.inputs:
+ $:input.render()
+
<input type="submit" value="Go"/>
</form>
+$ values = [[a.encode("ascii") + " 10 AM",b] for a,b in counts.items()]
+$ days = [x[0] for x in values]
+$ days.sort()
+$ first_day = days[0].split()[0]
+$ last_day = days[-1].split()[0]
+<script class="code" type="text/javascript">
+$$(document).ready(function(){
+
+ line1 = $:values;
+ var plot1 = $$.jqplot ('chartdiv', [line1], {
+ seriesColors: [ "#909AA9" ],
+ // Give the plot a title.
+ // You can specify options for all axes on the plot at once with
+ // the axesDefaults object. Here, we're using a canvas renderer
+ // to draw the axis label which allows rotated text.
+ axesDefaults: {
+ labelRenderer: $$.jqplot.CanvasAxisLabelRenderer
+ },
+ seriesDefaults : {
+ yaxis: 'y2axis',
+ markerOptions: { style:"circle", size:8 },
+ renderer:$$.jqplot.BarRenderer,
+ rendererOptions: { barWidth: 10},
+
+ trendline: {
+ show: true
+ }
+ },
+ // An axes object holds options for all axes.
+ // Allowable axes are xaxis, x2axis, yaxis, y2axis, y3axis, ...
+ // Up to 9 y axes are supported.
+ axes: {
+ // options for each axis are specified in seperate option objects.
+ xaxis: {
+ renderer:$$.jqplot.DateAxisRenderer,
+ tickOptions:{formatString:'%b %#d, %y'},
+ label: "Date",
+ min: '${first_day}',
+ max: '${last_day} 14 PM',
+ },
+ y2axis: {
+ label: "",
+ min: 0,
+ max: ${round_magnitude(max_count)}
+ }
+ },
+ highlighter: {
+ show: true
+ },
+ grid: {
+ background: "#ffffff"
+ }
+ });
+
+});
+
+</script>
+
+
<div style="float: right">
-<a class="FlattrButton" style="display:none;" rev="flattr;button:compact;" href="http://stats.grok.se"></a>
+<a class="FlattrButton" style="display:none;" rev="flattr" href="http://stats.grok.se"></a>
</div>
<p style="font-size: 0.6em">(took $render_time sec)</p>
View
2  web/templates/top.html
@@ -11,7 +11,7 @@
$for i in rows:
<tr>
<td>${i.rank}</td>
- <td><a href="http://stats.grok.se/${i.project}/$LATESTTOP/${i.page}">${unquote(i.page).replace("_"," ")}</a></td>
+ <td><a href="/${i.project}/$LATESTTOP/${i.page}">${unquote(i.page).replace("_"," ")}</a></td>
<td>${i.hitcount}</td>
</tr>
Please sign in to comment.
Something went wrong with that request. Please try again.