analysis: show recent uploads on /

Change-Id: I8b4b85a2c38deb53bd30a26447aef8c6ce1b49d3
Reviewed-on: https://go-review.googlesource.com/36117
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/analysis/app/app.go b/analysis/app/app.go
index d8cb399..5ab9459 100644
--- a/analysis/app/app.go
+++ b/analysis/app/app.go
@@ -38,8 +38,3 @@
 	//q := r.Form.Get("q")
 	a.compare(w, r)
 }
-
-// index redirects / to /search.
-func (a *App) index(w http.ResponseWriter, r *http.Request) {
-	http.Redirect(w, r, "/search", http.StatusSeeOther)
-}
diff --git a/analysis/app/appengine.go b/analysis/app/appengine.go
new file mode 100644
index 0000000..c5700a1
--- /dev/null
+++ b/analysis/app/appengine.go
@@ -0,0 +1,23 @@
+// Copyright 2017 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.
+
+// +build appengine
+
+package app
+
+import (
+	"net/http"
+
+	"golang.org/x/net/context"
+	"google.golang.org/appengine"
+	"google.golang.org/appengine/log"
+)
+
+// requestContext returns the Context object for a given request.
+func requestContext(r *http.Request) context.Context {
+	return appengine.NewContext(r)
+}
+
+var infof = log.Infof
+var errorf = log.Errorf
diff --git a/analysis/app/index.go b/analysis/app/index.go
new file mode 100644
index 0000000..99b5af3
--- /dev/null
+++ b/analysis/app/index.go
@@ -0,0 +1,46 @@
+// Copyright 2017 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.
+
+package app
+
+import (
+	"html/template"
+	"io/ioutil"
+	"net/http"
+
+	"golang.org/x/perf/storage"
+)
+
+// index redirects / to /search.
+func (a *App) index(w http.ResponseWriter, r *http.Request) {
+	ctx := requestContext(r)
+
+	tmpl, err := ioutil.ReadFile("template/index.html")
+	if err != nil {
+		http.Error(w, err.Error(), 500)
+		return
+	}
+
+	t, err := template.New("main").Parse(string(tmpl))
+	if err != nil {
+		http.Error(w, err.Error(), 500)
+		return
+	}
+
+	var uploads []storage.UploadInfo
+	ul := a.StorageClient.ListUploads("", []string{"by", "upload-time"}, 16)
+	defer ul.Close()
+	for ul.Next() {
+		uploads = append(uploads, ul.Info())
+	}
+	if err := ul.Err(); err != nil {
+		errorf(ctx, "failed to fetch recent uploads: %v", err)
+	}
+
+	w.Header().Set("Content-Type", "text/html; charset=utf-8")
+	if err := t.Execute(w, struct{ RecentUploads []storage.UploadInfo }{uploads}); err != nil {
+		http.Error(w, err.Error(), 500)
+		return
+	}
+}
diff --git a/analysis/app/local.go b/analysis/app/local.go
new file mode 100644
index 0000000..a4b45fd
--- /dev/null
+++ b/analysis/app/local.go
@@ -0,0 +1,25 @@
+// Copyright 2017 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.
+
+// +build !appengine
+
+package app
+
+import (
+	"log"
+	"net/http"
+
+	"golang.org/x/net/context"
+)
+
+// requestContext returns the Context object for a given request.
+func requestContext(r *http.Request) context.Context {
+	return r.Context()
+}
+
+func infof(_ context.Context, format string, args ...interface{}) {
+	log.Printf(format, args...)
+}
+
+var errorf = infof
diff --git a/analysis/appengine/template/index.html b/analysis/appengine/template/index.html
new file mode 100644
index 0000000..7497b51
--- /dev/null
+++ b/analysis/appengine/template/index.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Go Performance Result Dashboard</title>
+    <style type="text/css">
+#header h1 {
+  display: inline;
+}
+#search {
+  padding: 1em .5em;
+  width: 100%;
+}
+input[type="text"] {
+  font-size: 100%;
+}
+#results {
+  border-top: 1px solid black;
+}
+    </style>
+  </head>
+  <body>
+    <div id="header">
+      <h1>Go Performance Dashboard</h1>
+      <a href="/">about</a>
+    </div>
+    <div id="search">
+      <form action="/search">
+        <input type="text" name="q" size="120">
+        <input type="submit" value="Search">
+      </form>
+    </div>
+    <div id="results">
+      <p>The Go Performance Dashboard provides a centralized
+        resource for sharing and analyzing benchmark results. To get
+        started, upload benchmark results using
+        <code>go get -u golang.org/x/perf/cmd/benchsave</code>
+        and
+        <code>benchsave old.txt new.txt</code>
+        or upload via the web at
+        <a href="https://perfdata-dot-golang-org.appspot.com/upload">https://perfdata-dot-golang-org.appspot.com/upload</a>.</p>
+      {{with .RecentUploads}}
+        <h2>Recent Uploads</h2>
+        <table>
+          <thead>
+            <tr><th>Records</th><th>Upload</th><th>By</th></tr>
+          </thead>
+          <tbody>
+            {{range .}}
+              <tr>
+                <td>{{.Count}}</td>
+                <td><a href="/search?q={{.UploadID}}">{{index .LabelValues "upload-time"}}</a></td>
+                <td>{{.LabelValues.by}}</td>
+              </tr>
+            {{end}}
+          </tbody>
+        </table>
+      {{end}}
+    </div>
+  </body>
+</html>