summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Gerrand <adg@golang.org>2010-06-23 15:27:51 +0100
committerAndrew Gerrand <adg@golang.org>2010-06-23 15:27:51 +0100
commit989fa5b9944feb496a068d0fb0e49c72cc2d6f11 (patch)
tree2fbf5d908bc087b9574d10fe26e8dc874fe601ba
parent7304fb85984af7dd6cc590e2cdeb4c0e45cd77a6 (diff)
downloadgolang-989fa5b9944feb496a068d0fb0e49c72cc2d6f11.tar.gz
godashboard: add Projects page
R=rsc, r, gri CC=golang-dev http://codereview.appspot.com/1476041
-rw-r--r--misc/dashboard/godashboard/app.yaml5
-rw-r--r--misc/dashboard/godashboard/benchmark1.html1
-rw-r--r--misc/dashboard/godashboard/benchmarks.html1
-rw-r--r--misc/dashboard/godashboard/index.yaml11
-rw-r--r--misc/dashboard/godashboard/main.html1
-rw-r--r--misc/dashboard/godashboard/package.html15
-rw-r--r--misc/dashboard/godashboard/package.py128
-rw-r--r--misc/dashboard/godashboard/project-edit.html45
-rw-r--r--misc/dashboard/godashboard/project-notify.txt9
-rw-r--r--misc/dashboard/godashboard/project.html86
-rw-r--r--misc/dashboard/godashboard/static/style.css23
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>&nbsp;<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;