diff options
author | Andrew Gerrand <adg@golang.org> | 2010-06-23 15:27:51 +0100 |
---|---|---|
committer | Andrew Gerrand <adg@golang.org> | 2010-06-23 15:27:51 +0100 |
commit | 989fa5b9944feb496a068d0fb0e49c72cc2d6f11 (patch) | |
tree | 2fbf5d908bc087b9574d10fe26e8dc874fe601ba /misc | |
parent | 7304fb85984af7dd6cc590e2cdeb4c0e45cd77a6 (diff) | |
download | golang-989fa5b9944feb496a068d0fb0e49c72cc2d6f11.tar.gz |
godashboard: add Projects page
R=rsc, r, gri
CC=golang-dev
http://codereview.appspot.com/1476041
Diffstat (limited to 'misc')
-rw-r--r-- | misc/dashboard/godashboard/app.yaml | 5 | ||||
-rw-r--r-- | misc/dashboard/godashboard/benchmark1.html | 1 | ||||
-rw-r--r-- | misc/dashboard/godashboard/benchmarks.html | 1 | ||||
-rw-r--r-- | misc/dashboard/godashboard/index.yaml | 11 | ||||
-rw-r--r-- | misc/dashboard/godashboard/main.html | 1 | ||||
-rw-r--r-- | misc/dashboard/godashboard/package.html | 15 | ||||
-rw-r--r-- | misc/dashboard/godashboard/package.py | 128 | ||||
-rw-r--r-- | misc/dashboard/godashboard/project-edit.html | 45 | ||||
-rw-r--r-- | misc/dashboard/godashboard/project-notify.txt | 9 | ||||
-rw-r--r-- | misc/dashboard/godashboard/project.html | 86 | ||||
-rw-r--r-- | misc/dashboard/godashboard/static/style.css | 23 |
11 files changed, 319 insertions, 6 deletions
diff --git a/misc/dashboard/godashboard/app.yaml b/misc/dashboard/godashboard/app.yaml index fb742d8ed..aec559dcc 100644 --- a/misc/dashboard/godashboard/app.yaml +++ b/misc/dashboard/godashboard/app.yaml @@ -1,5 +1,5 @@ application: godashboard -version: 4 +version: 5 runtime: python api_version: 1 @@ -10,5 +10,8 @@ handlers: - url: /package.* script: package.py +- url: /project.* + script: package.py + - url: /.* script: gobuild.py diff --git a/misc/dashboard/godashboard/benchmark1.html b/misc/dashboard/godashboard/benchmark1.html index 66e9830a2..2d49e7204 100644 --- a/misc/dashboard/godashboard/benchmark1.html +++ b/misc/dashboard/godashboard/benchmark1.html @@ -9,6 +9,7 @@ <ul class="menu"> <li><a href="/">Build Status</a></li> <li><a href="/package">Packages</a></li> + <li><a href="/project">Projects</a></li> <li><a href="/benchmarks">Benchmarks</a></li> <li><a href="http://golang.org/">golang.org</a></li> </ul> diff --git a/misc/dashboard/godashboard/benchmarks.html b/misc/dashboard/godashboard/benchmarks.html index 14026f5da..d42fcfe48 100644 --- a/misc/dashboard/godashboard/benchmarks.html +++ b/misc/dashboard/godashboard/benchmarks.html @@ -9,6 +9,7 @@ <ul class="menu"> <li><a href="/">Build Status</a></li> <li><a href="/package">Packages</a></li> + <li><a href="/project">Projects</a></li> <li>Benchmarks</li> <li><a href="http://golang.org/">golang.org</a></li> </ul> diff --git a/misc/dashboard/godashboard/index.yaml b/misc/dashboard/godashboard/index.yaml index 573abfb09..148824bb6 100644 --- a/misc/dashboard/godashboard/index.yaml +++ b/misc/dashboard/godashboard/index.yaml @@ -23,6 +23,17 @@ indexes: - name: __key__ direction: desc +- kind: Project + properties: + - name: approved + - name: category + - name: name + +- kind: Project + properties: + - name: category + - name: name + # AUTOGENERATED # This index.yaml is automatically updated whenever the dev_appserver diff --git a/misc/dashboard/godashboard/main.html b/misc/dashboard/godashboard/main.html index dba7951b8..8eb27869e 100644 --- a/misc/dashboard/godashboard/main.html +++ b/misc/dashboard/godashboard/main.html @@ -11,6 +11,7 @@ <ul class="menu"> <li>Build Status</li> <li><a href="/package">Packages</a></li> + <li><a href="/project">Projects</a></li> <li><a href="/benchmarks">Benchmarks</a></li> <li><a href="http://golang.org/">golang.org</a></li> </ul> diff --git a/misc/dashboard/godashboard/package.html b/misc/dashboard/godashboard/package.html index 64d86d7b8..08dd6a31d 100644 --- a/misc/dashboard/godashboard/package.html +++ b/misc/dashboard/godashboard/package.html @@ -9,6 +9,7 @@ <ul class="menu"> <li><a href="/">Build Status</a></li> <li>Packages</li> + <li><a href="/project">Projects</a></li> <li><a href="/benchmarks">Benchmarks</a></li> <li><a href="http://golang.org/">golang.org</a></li> </ul> @@ -22,24 +23,34 @@ <h2>Recently Installed Packages</h2> <table class="alternate" cellpadding="0" cellspacing="0"> - <tr><th>last install</th><th>count</th><th>path</th></tr> + <tr><th>last install</th><th>count</th><th>path</th><th>project</th></tr> {% for r in by_time %} <tr> <td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td> <td class="count">{{r.count}}</td> <td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td> + <td class="project"> + {% for p in r.project_set %} + <a href="{{p.web_url}}">{{p.name}}</a> - {{p.descr}} + {% endfor %} + </td> </tr> {% endfor %} </table> <h2>Most Installed Packages</h2> <table class="alternate" cellpadding="0" cellspacing="0"> - <tr><th>last install</th><th>count</th><th>path</th></tr> + <tr><th>last install</th><th>count</th><th>path</th><th>project</th></tr> {% for r in by_count %} <tr> <td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td> <td class="count">{{r.count}}</td> <td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td> + <td class="project"> + {% for p in r.project_set %} + <a href="{{p.web_url}}">{{p.name}}</a> - {{p.descr}} + {% endfor %} + </td> </tr> {% endfor %} </table> diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py index 351a1fadc..6c3bd9995 100644 --- a/misc/dashboard/godashboard/package.py +++ b/misc/dashboard/godashboard/package.py @@ -5,12 +5,18 @@ # This is the server part of the package dashboard. # It must be run by App Engine. +mail_to = "adg@golang.org" +mail_from = "Go Dashboard <adg@golang.org>" +mail_subject = "New Project Submitted" + from google.appengine.api import memcache from google.appengine.runtime import DeadlineExceededError 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 import binascii import datetime import hashlib @@ -21,6 +27,7 @@ import re import struct import time import urllib2 +import sets # Storage model for package info recorded on server. # Just path, count, and time of last install. @@ -30,6 +37,15 @@ class Package(db.Model): count = db.IntegerProperty() last_install = db.DateTimeProperty() +class Project(db.Model): + name = db.StringProperty(indexed=True) + descr = db.StringProperty() + web_url = db.StringProperty() + package = db.ReferenceProperty(Package) + category = db.StringProperty(indexed=True) + tags = db.ListProperty(str) + approved = db.BooleanProperty(indexed=True) + 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_.\-]+$') @@ -124,8 +140,118 @@ class PackagePage(webapp.RequestHandler): self.response.set_status(500) self.response.out.write('not ok') +class ProjectPage(webapp.RequestHandler): + + def get(self): + admin = users.is_current_user_admin() + if self.request.path == "/project/login": + self.redirect(users.create_login_url("/project")) + elif self.request.path == "/project/logout": + self.redirect(users.create_logout_url("/project")) + elif self.request.path == "/project/edit" and admin: + self.edit() + else: + self.list() + + def post(self): + if self.request.path == "/project/edit": + self.edit(True) + else: + data = dict(map(lambda x: (x, self.request.get(x)), ["name","descr","web_url"])) + if reduce(lambda x, y: x or not y, data.values(), False): + data["submitMsg"] = "You must complete all the fields." + self.list(data) + return + p = Project.get_by_key_name("proj-"+data["name"]) + if p is not None: + data["submitMsg"] = "A project by this name already exists." + self.list(data) + return + p = Project(key_name="proj-"+data["name"], **data) + p.put() + + path = os.path.join(os.path.dirname(__file__), 'project-notify.txt') + mail.send_mail( + sender=mail_from, to=mail_to, subject=mail_subject, + body=template.render(path, {'project': p})) + + 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) + if tag: + projects = filter(lambda x: tag in x.tags, projects) + + 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): + if save: + name = self.request.get("orig_name") + else: + name = self.request.get("name") + + p = Project.get_by_key_name("proj-"+name) + if not p: + self.response.out.write("Couldn't find that Project.") + return + + if save: + if self.request.get("do") == "Delete": + p.delete() + else: + pkg_name = self.request.get("package", None) + if pkg_name: + pkg = Package.get_by_key_name("pkg-"+pkg_name) + if pkg: + p.package = pkg.key() + for f in ['name', 'descr', 'web_url', 'category']: + setattr(p, f, self.request.get(f, None)) + p.approved = self.request.get("approved") == "1" + p.tags = filter(lambda x: x, self.request.get("tags", "").split(",")) + p.put() + self.redirect("/project") + return + + # get all project categories and tags + cats, tags = sets.Set(), sets.Set() + for r in Project.all(): + cats.add(r.category) + for t in r.tags: + tags.add(t) + + self.response.headers['Content-Type'] = 'text/html; charset=utf-8' + path = os.path.join(os.path.dirname(__file__), 'project-edit.html') + self.response.out.write(template.render(path, { + "taglist": tags, "catlist": cats, "p": p, "tags": ",".join(p.tags) })) + + def redirect(self, url): + self.response.set_status(302) + self.response.headers.add_header("Location", url) + def main(): - app = webapp.WSGIApplication([('/package', PackagePage)], debug=True) + app = webapp.WSGIApplication([ + ('/package', PackagePage), + ('/project.*', ProjectPage), + ], debug=True) run_wsgi_app(app) if __name__ == '__main__': diff --git a/misc/dashboard/godashboard/project-edit.html b/misc/dashboard/godashboard/project-edit.html new file mode 100644 index 000000000..5f1ca3b11 --- /dev/null +++ b/misc/dashboard/godashboard/project-edit.html @@ -0,0 +1,45 @@ +<html> +<head> +<script type="text/javascript" src="http://www.google.com/jsapi"></script> +<script> +google.load("jquery", "1"); +</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"> +Name:<br/> +<input type="text" name="name" value="{{p.name|escape}}"><br/> +Description:<br/> +<input type="text" name="descr" value="{{p.descr|escape}}"><br/> +Category:<br/> +<input type="text" id="cats" name="category" value="{{p.category|escape}}"><br/> +Tags: (comma-separated)<br/> +<input type="text" id="tags" name="tags" value="{{tags}}"><br/> +Web URL:<br/> +<input type="text" name="web_url" value="{{p.web_url|escape}}"><br/> +Package URL: (to link to a goinstall'd package)<br/> +<input type="text" name="package" value="{{p.package.path|escape}}"><br/> +Approved: <input type="checkbox" name="approved" value="1" {% if p.approved %}checked{% endif %}><br/> +<br/> +<input type="submit" name="do" value="Save"> +<input type="submit" name="do" value="Delete" onClick="javascript:return confirm('Delete this?');"> +</form> +<script> +var tags = [ +{% for t in taglist %} + "{{t}}"{% if not forloop.last %},{% endif %} +{% endfor %} +]; +var cats = [ +{% for c in catlist %} + "{{c}}"{% if not forloop.last %},{% endif %} +{% endfor %} +]; + +$('#tags').autocomplete(tags); +$('#cats').autocomplete(cats); +</script> +</body> +</html> diff --git a/misc/dashboard/godashboard/project-notify.txt b/misc/dashboard/godashboard/project-notify.txt new file mode 100644 index 000000000..3a165908c --- /dev/null +++ b/misc/dashboard/godashboard/project-notify.txt @@ -0,0 +1,9 @@ +A new project has been submitted: + +Name: {{project.name}} +Description: {{project.descr}} +URL: {{project.web_url}} + +To edit/approve/delete: +http://godashboard.appspot.com/project/edit?name={{project.name|urlencode}} + diff --git a/misc/dashboard/godashboard/project.html b/misc/dashboard/godashboard/project.html new file mode 100644 index 000000000..a9363806f --- /dev/null +++ b/misc/dashboard/godashboard/project.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> + <head> + <title>Projects - Go Dashboard</title> + <link rel="stylesheet" type="text/css" href="static/style.css"> + <style> + .unapproved a.name { color: red } + .tag { font-size: 0.8em; color: #666 } + </style> + </head> + + <body> + <ul class="menu"> + <li><a href="/">Build Status</a></li> + <li><a href="/package">Packages</a></li> + <li>Projects</li> + <li><a href="/benchmarks">Benchmarks</a></li> + <li><a href="http://golang.org/">golang.org</a></li> + </ul> + + <h1>Go Dashboard</h1> + + <p> + These are external projects and not endorsed or supported by the Go project. + </p> + + <h2>Projects</h2> + + <div class="submit"> + <h3>Submit a Project</h3> + <p> + Using this form you can submit a project to be included in the list. + </p> + <form action="/project" method="POST"> + <table> + <tr><td>Name:<td><input type="text" name="name"> + <tr><td>Description:<td><input type="text" name="descr"> + <tr><td>URL:<td><input type="text" name="web_url"> + <tr><td> <td><input type="submit" value="Send"> + {% if submitMsg %} + <tr><td class="msg" colspan="2">{{ submitMsg }}</td></tr> + {% endif %} + </table> + </form> + </div> + + <p> + Filter by tag: + {% if tag %} + <a href="/project">all</a> + {% else %} + <b>all</b> + {% endif %} + {% for t in tags %} + {% ifequal t tag %} + <b>{{t}}</b> + {% else %} + <a href="?tag={{t}}">{{t}}</a> + {% endifequal %} + {% endfor %} + </p> + + {% for r in projects %} + {% ifchanged r.category %} + {% if not forloop.first %} + </ul> + {% endif %} + <h3>{{r.category}}</h3> + <ul> + {% endifchanged %} + <li{% if not r.approved %} class="unapproved"{% endif %}> + {% if admin %}[<a href="/project/edit?name={{r.name}}">edit</a>]{% endif %} + <a class="name" href="{{r.web_url}}">{{r.name}}</a> - {{r.descr}} + {% for tag in r.tags %} + <span class="tag">{{tag}}</span> + {% endfor %} + </li> + {% if forloop.last %} + </ul> + {% endif %} + {% endfor %} + </ul> + + + </body> +</html> diff --git a/misc/dashboard/godashboard/static/style.css b/misc/dashboard/godashboard/static/style.css index 882b854ab..481af36d7 100644 --- a/misc/dashboard/godashboard/static/style.css +++ b/misc/dashboard/godashboard/static/style.css @@ -3,8 +3,8 @@ body { margin: 0; padding: 0; } -h1, h2, ul, table, p { - padding: 0 0.2em; +h1, h2, h3, ul.menu, table, p { + padding: 0 0.5em; } h1, h2 { margin: 0; @@ -19,6 +19,25 @@ h1 { } h2 { border-top: 1px solid #ccc; + padding-left: 0.2em; +} +.submit { + float: right; + border: 1px solid #ccc; + width: 350px; + padding-bottom: 1em; + margin: 0.5em; + background: #eee; +} +.submit table { + width: 100%; +} +.submit input[type=text] { + width: 200px; +} +.submit .msg { + text-align: center; + color: red; } table.alternate { white-space: nowrap; |