Commit 7a18fc3b authored by Andrew Gerrand's avatar Andrew Gerrand

dashboard: list "most installed this week" with rolling count

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/4631085
parent 95117d30
......@@ -11,9 +11,13 @@ handlers:
- url: /static
static_dir: static
- url: /package.*
- url: /package
script: package.py
- url: /package/daily
script: package.py
login: admin
- url: /project.*
script: package.py
......
cron:
- description: daily package maintenance
url: /package/daily
schedule: every 24 hours
......@@ -32,6 +32,20 @@
<a href="http://blog.golang.org/2011/03/godoc-documenting-go-code.html">package doc comment</a>.
</p>
<h2>Most Installed Packages (this week)</h2>
<table class="alternate" cellpadding="0" cellspacing="0">
<tr><th>last install</th><th>count</th><th>build</th><th>path</th><th>info</th></tr>
{% for r in by_week_count %}
<tr>
<td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td>
<td class="count">{{r.week_count}}</td>
<td class="ok">{% if r.ok %}<a title="{{r.last_ok|date:"Y-M-d H:i"}}">ok</a>{% else %}&nbsp;{% endif %}</td>
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
<td class="info">{% if r.info %}{{r.info|escape}}{% endif %}</td>
</tr>
{% endfor %}
</table>
<h2>Recently Installed Packages</h2>
<table class="alternate" cellpadding="0" cellspacing="0">
<tr><th>last install</th><th>count</th><th>build</th><th>path</th><th>info</th></tr>
......@@ -41,12 +55,12 @@
<td class="count">{{r.count}}</td>
<td class="ok">{% if r.ok %}<a title="{{r.last_ok|date:"Y-M-d H:i"}}">ok</a>{% else %}&nbsp;{% endif %}</td>
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
<td class="info">{% if r.info %}{{r.info|escape}}{% endif %}</td>
<td class="info">{% if r.info %}{{r.info|escape}}{% endif %}</td>
</tr>
{% endfor %}
</table>
<h2>Most Installed Packages</h2>
<h2>Most Installed Packages (all time)</h2>
<table class="alternate" cellpadding="0" cellspacing="0">
<tr><th>last install</th><th>count</th><th>build</th><th>path</th><th>info</th></tr>
{% for r in by_count %}
......@@ -55,7 +69,7 @@
<td class="count">{{r.count}}</td>
<td class="ok">{% if r.ok %}<a title="{{r.last_ok|date:"Y-M-d H:i"}}">ok</a>{% else %}&nbsp;{% endif %}</td>
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
<td class="info">{% if r.info %}{{r.info|escape}}{% endif %}</td>
<td class="info">{% if r.info %}{{r.info|escape}}{% endif %}</td>
</tr>
{% endfor %}
</table>
......
......@@ -5,34 +5,36 @@
# This is the server part of the package dashboard.
# It must be run by App Engine.
from google.appengine.api import mail
from google.appengine.api import memcache
from google.appengine.api import taskqueue
from google.appengine.api import urlfetch
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import users
from google.appengine.api import mail
from google.appengine.api import urlfetch
import datetime
import logging
import os
import re
import urllib2
import sets
import urllib2
# local imports
from auth import auth
import toutf8
import const
from auth import auth
template.register_template_library('toutf8')
# Storage model for package info recorded on server.
# Just path, count, and time of last install.
class Package(db.Model):
path = db.StringProperty()
web_url = db.StringProperty() # derived from path
count = db.IntegerProperty()
web_url = db.StringProperty() # derived from path
count = db.IntegerProperty() # grand total
week_count = db.IntegerProperty() # rolling weekly count
day_count = db.TextProperty(default='') # daily count
last_install = db.DateTimeProperty()
# data contributed by gobuilder
......@@ -40,6 +42,67 @@ class Package(db.Model):
ok = db.BooleanProperty()
last_ok = db.DateTimeProperty()
def get_day_count(self):
counts = {}
if not self.day_count:
return counts
for d in str(self.day_count).split('\n'):
date, count = d.split(' ')
counts[date] = int(count)
return counts
def set_day_count(self, count):
days = []
for day, count in count.items():
days.append('%s %d' % (day, count))
days.sort(reverse=True)
days = days[:28]
self.day_count = '\n'.join(days)
def inc(self):
count = self.get_day_count()
today = str(datetime.date.today())
count[today] = count.get(today, 0) + 1
self.set_day_count(count)
self.update_week_count(count)
self.count += 1
def update_week_count(self, count=None):
if count is None:
count = self.get_day_count()
total = 0
today = datetime.date.today()
for i in range(7):
day = str(today - datetime.timedelta(days=i))
if day in count:
total += count[day]
self.week_count = total
# PackageDaily kicks off the daily package maintenance cron job
# and serves the associated task queue.
class PackageDaily(webapp.RequestHandler):
def get(self):
# queue a task to update each package with a week_count > 0
keys = Package.all(keys_only=True).filter('week_count >', 0)
for key in keys:
taskqueue.add(url='/package/daily', params={'key': key.name()})
def post(self):
# update a single package (in a task queue)
def update(key):
p = Package.get_by_key_name(key)
if not p:
return
p.update_week_count()
p.put()
key = self.request.get('key')
if not key:
return
db.run_in_transaction(update, key)
class Project(db.Model):
name = db.StringProperty(indexed=True)
descr = db.StringProperty()
......@@ -49,6 +112,7 @@ class Project(db.Model):
tags = db.ListProperty(str)
approved = db.BooleanProperty(indexed=True)
re_bitbucket = re.compile(r'^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-zA-Z0-9_.\-]+)(/[a-z0-9A-Z_.\-/]+)?$')
re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)(/[a-z0-9A-Z_.\-/]+)?$')
re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)+$')
......@@ -115,29 +179,30 @@ class PackagePage(webapp.RequestHandler):
html = memcache.get('view-package')
if not html:
tdata = {}
q = Package.all().filter('week_count >', 0)
q.order('-week_count')
tdata['by_week_count'] = q.fetch(50)
q = Package.all()
q.order('-last_install')
by_time = q.fetch(100)
tdata['by_time'] = q.fetch(20)
q = Package.all()
q.order('-count')
by_count = q.fetch(100)
tdata['by_count'] = q.fetch(100)
self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
path = os.path.join(os.path.dirname(__file__), 'package.html')
html = template.render(
path,
{"by_time": by_time, "by_count": by_count}
)
html = template.render(path, tdata)
memcache.set('view-package', html, time=CacheTimeout)
self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
self.response.out.write(html)
def json(self):
json = memcache.get('view-package-json')
if not json:
self.response.set_status(200)
self.response.headers['Content-Type'] = 'text/plain; charset=utf-8'
q = Package.all()
s = '{"packages": ['
sep = ''
......@@ -147,6 +212,8 @@ class PackagePage(webapp.RequestHandler):
s += '\n]}\n'
json = s
memcache.set('view-package-json', json, time=CacheTimeout)
self.response.set_status(200)
self.response.headers['Content-Type'] = 'text/plain; charset=utf-8'
self.response.out.write(json)
def can_get_url(self, url):
......@@ -181,14 +248,15 @@ class PackagePage(webapp.RequestHandler):
return False
p = Package(key_name = key, path = path, count = 0, web_url = web)
# is this the builder updating package metadata?
if auth(self.request):
# builder updating package metadata
p.info = self.request.get('info')
p.ok = self.request.get('ok') == "true"
if p.ok:
p.last_ok = datetime.datetime.utcnow()
else:
p.count += 1
# goinstall reporting an install
p.inc()
p.last_install = datetime.datetime.utcnow()
# update package object
......@@ -197,7 +265,7 @@ class PackagePage(webapp.RequestHandler):
def post(self):
path = self.request.get('path')
ok = db.run_in_transaction(self.record_pkg, path)
ok = db.run_in_transaction(self.record_pkg, path)
if ok:
self.response.set_status(200)
self.response.out.write('ok')
......@@ -347,6 +415,7 @@ class ProjectPage(webapp.RequestHandler):
def main():
app = webapp.WSGIApplication([
('/package', PackagePage),
('/package/daily', PackageDaily),
('/project.*', ProjectPage),
], debug=True)
run_wsgi_app(app)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment