summaryrefslogtreecommitdiff
path: root/misc/dashboard/godashboard
diff options
context:
space:
mode:
Diffstat (limited to 'misc/dashboard/godashboard')
-rw-r--r--misc/dashboard/godashboard/package.py184
-rw-r--r--misc/dashboard/godashboard/project-edit.html10
-rw-r--r--misc/dashboard/godashboard/project-notify.txt2
-rw-r--r--misc/dashboard/godashboard/toutf8.py14
4 files changed, 153 insertions, 57 deletions
diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py
index 6c3bd9995..cf59bf3e8 100644
--- a/misc/dashboard/godashboard/package.py
+++ b/misc/dashboard/godashboard/package.py
@@ -17,6 +17,7 @@ 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 binascii
import datetime
import hashlib
@@ -29,6 +30,11 @@ import time
import urllib2
import sets
+# local imports
+import toutf8
+
+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):
@@ -50,36 +56,91 @@ re_bitbucket = re.compile(r'^bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$')
re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)$')
re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$')
+def vc_to_web(path):
+ if re_bitbucket.match(path):
+ check_url = 'http://' + path + '/?cmd=heads'
+ web = 'http://' + path + '/'
+ elif re_github.match(path):
+ # github doesn't let you fetch the .git directory anymore.
+ # fetch .git/info/refs instead, like git clone would.
+ check_url = 'http://'+path+'.git/info/refs'
+ web = 'http://' + path
+ elif re_googlecode.match(path):
+ check_url = 'http://'+path
+ web = 'http://code.google.com/p/' + path[:path.index('.')]
+ else:
+ return False, False
+ return web, check_url
+
+re_bitbucket_web = re.compile(r'bitbucket\.org/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)')
+re_googlecode_web = re.compile(r'code.google.com/p/([a-z0-9\-]+)')
+re_github_web = re.compile(r'github\.com/([a-z0-9A-Z_.\-]+)/([a-z0-9A-Z_.\-]+)')
+re_striphttp = re.compile(r'http://(www\.)?')
+
+def web_to_vc(url):
+ url = re_striphttp.sub('', url)
+ m = re_bitbucket_web.match(url)
+ if m:
+ return 'bitbucket.org/'+m.group(1)+'/'+m.group(2)
+ m = re_github_web.match(url)
+ if m:
+ return 'github.com/'+m.group(1)+'/'+m.group(2)
+ m = re_googlecode_web.match(url)
+ if m:
+ path = m.group(1)+'.googlecode.com/'
+ # perform http request to path/hg to check if they're using mercurial
+ vcs = 'svn'
+ try:
+ response = urlfetch.fetch('http://'+path+'hg', deadline=1)
+ if response.status_code == 200:
+ vcs = 'hg'
+ except: pass
+ return path + vcs
+ return False
+
MaxPathLength = 100
+CacheTimeout = 3600
class PackagePage(webapp.RequestHandler):
def get(self):
if self.request.get('fmt') == 'json':
return self.json()
- q = Package.all()
- q.order('-last_install')
- by_time = q.fetch(100)
+ html = memcache.get('view-package')
+ if not html:
+ q = Package.all()
+ q.order('-last_install')
+ by_time = q.fetch(100)
- q = Package.all()
- q.order('-count')
- by_count = q.fetch(100)
+ q = Package.all()
+ q.order('-count')
+ 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')
- self.response.out.write(template.render(path, {"by_time": by_time, "by_count": by_count}))
+ 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}
+ )
+ memcache.set('view-package', html, time=CacheTimeout)
+
+ self.response.out.write(html)
def json(self):
- self.response.set_status(200)
- self.response.headers['Content-Type'] = 'text/plain; charset=utf-8'
- q = Package.all()
- s = '{"packages": ['
- sep = ''
- for r in q.fetch(1000):
- s += '%s\n\t{"path": "%s", "last_install": "%s", "count": "%s"}' % (sep, r.path, r.last_install, r.count)
- sep = ','
- s += '\n]}\n'
- self.response.out.write(s)
+ 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 = ''
+ for r in q.fetch(1000):
+ s += '%s\n\t{"path": "%s", "last_install": "%s", "count": "%s"}' % (sep, r.path, r.last_install, r.count)
+ sep = ','
+ s += '\n]}\n'
+ json = s
+ memcache.set('view-package-json', json, time=CacheTimeoout)
+ self.response.out.write(json)
def can_get_url(self, url):
try:
@@ -104,18 +165,8 @@ class PackagePage(webapp.RequestHandler):
p = Package.get_by_key_name(key)
if p is None:
# not in datastore - verify URL before creating
- if re_bitbucket.match(path):
- check_url = 'http://' + path + '/?cmd=heads'
- web = 'http://' + path + '/'
- elif re_github.match(path):
- # github doesn't let you fetch the .git directory anymore.
- # fetch .git/info/refs instead, like git clone would.
- check_url = 'http://'+path+'.git/info/refs'
- web = 'http://' + path
- elif re_googlecode.match(path):
- check_url = 'http://'+path
- web = 'http://code.google.com/p/' + path[:path.index('.')]
- else:
+ web, check_url = vc_to_web(path)
+ if not web:
logging.error('unrecognized path: %s', path)
return False
if not self.can_get_url(check_url):
@@ -150,9 +201,27 @@ class ProjectPage(webapp.RequestHandler):
self.redirect(users.create_logout_url("/project"))
elif self.request.path == "/project/edit" and admin:
self.edit()
+ elif self.request.path == "/project/assoc" and admin:
+ self.assoc()
else:
self.list()
+ def assoc(self):
+ projects = Project.all()
+ for p in projects:
+ if p.package:
+ continue
+ path = web_to_vc(p.web_url)
+ if not path:
+ continue
+ pkg = Package.get_by_key_name("pkg-"+path)
+ if not pkg:
+ self.response.out.write('no: %s %s<br>' % (p.web_url, path))
+ continue
+ p.package = pkg
+ p.put()
+ self.response.out.write('yes: %s %s<br>' % (p.web_url, path))
+
def post(self):
if self.request.path == "/project/edit":
self.edit(True)
@@ -177,30 +246,40 @@ class ProjectPage(webapp.RequestHandler):
self.list({"submitMsg": "Your project has been submitted."})
- def list(self, data={}):
- projects = Project.all().order('category').order('name')
-
- admin = users.is_current_user_admin()
- if not admin:
- projects = projects.filter('approved =', True)
-
- projects = list(projects)
-
- tags = sets.Set()
- for p in projects:
- for t in p.tags:
- tags.add(t)
-
- tag = self.request.get("tag", None)
+ def list(self, additional_data={}):
+ cache_key = 'view-project-data'
+ tag = self.request.get('tag', None)
if tag:
- projects = filter(lambda x: tag in x.tags, projects)
+ cache_key += '-'+tag
+ data = memcache.get(cache_key)
+ admin = users.is_current_user_admin()
+ if admin or not data:
+ projects = Project.all().order('category').order('name')
+ if not admin:
+ projects = projects.filter('approved =', True)
+ projects = list(projects)
+
+ tags = sets.Set()
+ for p in projects:
+ for t in p.tags:
+ tags.add(t)
+
+ if tag:
+ projects = filter(lambda x: tag in x.tags, projects)
+
+ data = {}
+ data['tag'] = tag
+ data['tags'] = tags
+ data['projects'] = projects
+ data['admin']= admin
+ if not admin:
+ memcache.set(cache_key, data, time=CacheTimeout)
+
+ for k, v in additional_data.items():
+ data[k] = v
self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
path = os.path.join(os.path.dirname(__file__), 'project.html')
- data["tag"] = tag
- data["tags"] = tags
- data["projects"] = projects
- data["admin"] = admin
self.response.out.write(template.render(path, data))
def edit(self, save=False):
@@ -228,7 +307,8 @@ class ProjectPage(webapp.RequestHandler):
p.approved = self.request.get("approved") == "1"
p.tags = filter(lambda x: x, self.request.get("tags", "").split(","))
p.put()
- self.redirect("/project")
+ memcache.delete('view-project-data')
+ self.redirect('/project')
return
# get all project categories and tags
diff --git a/misc/dashboard/godashboard/project-edit.html b/misc/dashboard/godashboard/project-edit.html
index 5f1ca3b11..ce18fb3fb 100644
--- a/misc/dashboard/godashboard/project-edit.html
+++ b/misc/dashboard/godashboard/project-edit.html
@@ -1,11 +1,11 @@
<html>
<head>
+<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1");
+google.load("jqueryui", "1.8.2");
</script>
-<script type="text/javascript" src="/static/jquery.autocomplete.min.js"></script>
-<link rel="stylesheet" type="text/css" href="/static/jquery.autocomplete.css" />
</head>
<body>
<form action="/project/edit?orig_name={{p.name}}" method="POST">
@@ -38,8 +38,10 @@ var cats = [
{% endfor %}
];
-$('#tags').autocomplete(tags);
-$('#cats').autocomplete(cats);
+google.setOnLoadCallback(function() {
+ $('#tags').autocomplete({source:tags});
+ $('#cats').autocomplete({source:cats});
+});
</script>
</body>
</html>
diff --git a/misc/dashboard/godashboard/project-notify.txt b/misc/dashboard/godashboard/project-notify.txt
index 3a165908c..f55bf6421 100644
--- a/misc/dashboard/godashboard/project-notify.txt
+++ b/misc/dashboard/godashboard/project-notify.txt
@@ -5,5 +5,5 @@ Description: {{project.descr}}
URL: {{project.web_url}}
To edit/approve/delete:
-http://godashboard.appspot.com/project/edit?name={{project.name|urlencode}}
+http://godashboard.appspot.com/project/edit?name={{project.name|toutf8|urlencode}}
diff --git a/misc/dashboard/godashboard/toutf8.py b/misc/dashboard/godashboard/toutf8.py
new file mode 100644
index 000000000..544c681b6
--- /dev/null
+++ b/misc/dashboard/godashboard/toutf8.py
@@ -0,0 +1,14 @@
+# Copyright 2010 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# This is a Django custom template filter to work around the
+# fact that GAE's urlencode filter doesn't handle unicode strings.
+
+from google.appengine.ext import webapp
+
+register = webapp.template.create_template_register()
+
+@register.filter
+def toutf8(value):
+ return value.encode("utf-8")