hswaw/oodviewer: init
This brings oodviewer into k0.
oodviewer started as a py2/flask script running on q3k's personal infra,
which is now being turned down.
This is a rewrite of that script into similarly mediocre Go, conforming
to the exact same mediocre JSON API and spartan HTML interface.
This also deploys it into k0 in the oodviewer-prod namespace. It's
already running, but the 'oodviewer.q3k.me' TTL has to expire before it
begins handling traffic.
Change-Id: Ieef1b0f8f0c60e6fa5dbe7701e0a07a4257f99ce
diff --git a/hswaw/oodviewer/views.go b/hswaw/oodviewer/views.go
new file mode 100644
index 0000000..5ed4b5e
--- /dev/null
+++ b/hswaw/oodviewer/views.go
@@ -0,0 +1,138 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "math/rand"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ "github.com/golang/glog"
+
+ "code.hackerspace.pl/hscloud/hswaw/oodviewer/templates"
+)
+
+var (
+ tplBase = template.Must(template.New("base").Parse(string(templates.Data["base.html"])))
+ tplTerm = template.Must(template.Must(tplBase.Clone()).Parse(string(templates.Data["term.html"])))
+ tplTerms = template.Must(template.Must(tplBase.Clone()).Parse(string(templates.Data["terms.html"])))
+)
+
+// handleTermsJson returns a JSON list of all terms.
+func (a *app) handleTermsJson(w http.ResponseWriter, r *http.Request) {
+ terms, err := a.getTerms(r.Context())
+ if err != nil {
+ glog.Errorf("getTerms: %v", err)
+ w.WriteHeader(500)
+ fmt.Fprintf(w, "internal error")
+ return
+ }
+ // Target API from old oodviewer, even if it's terrible.
+ var res [][]interface{}
+ for _, term := range terms {
+ res = append(res, []interface{}{
+ term.Name, term.Entries,
+ })
+ }
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(res)
+}
+
+// handleTerms renders a HTML page containing all terms.
+func (a *app) handleTerms(w http.ResponseWriter, r *http.Request) {
+ terms, err := a.getTerms(r.Context())
+ if err != nil {
+ glog.Errorf("getTerms: %v", err)
+ w.WriteHeader(500)
+ fmt.Fprintf(w, "internal error")
+ return
+ }
+
+ termsData := make([]struct {
+ URL string
+ Name string
+ Count uint64
+ }, len(terms))
+
+ for i, term := range terms {
+ termsData[i].URL = url.QueryEscape(term.Name)
+ termsData[i].Name = term.Name
+ termsData[i].Count = term.Entries
+ }
+
+ tplTerms.Execute(w, map[string]interface{}{
+ "Terms": termsData,
+ })
+}
+
+// handleTermJson returns a JSON list of all entries contained within a term.
+func (a *app) handleTermJson(w http.ResponseWriter, r *http.Request) {
+ parts := strings.Split(r.URL.Path, "/")
+ name := parts[len(parts)-1]
+
+ entries, err := a.getEntries(r.Context(), name)
+ if err != nil {
+ glog.Errorf("getEntries: %v", err)
+ w.WriteHeader(500)
+ fmt.Fprintf(w, "internal error")
+ return
+ }
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(entries)
+}
+
+// handleRandomTermJson returns a JSON serialized randomly chosen entry from a
+// given term.
+func (a *app) handleRandomTermJson(w http.ResponseWriter, r *http.Request) {
+ parts := strings.Split(r.URL.Path, "/")
+ name := parts[len(parts)-1]
+
+ entries, err := a.getEntries(r.Context(), name)
+ if err != nil {
+ glog.Errorf("getEntries: %v", err)
+ w.WriteHeader(500)
+ fmt.Fprintf(w, "internal error")
+ return
+ }
+ if len(entries) < 1 {
+ w.WriteHeader(404)
+ fmt.Fprintf(w, "no such entry")
+ return
+ }
+ entry := entries[rand.Intn(len(entries))]
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(entry)
+}
+
+// handleTerm renders an HTML page of all entries contained within a term.
+func (a *app) handleTerm(w http.ResponseWriter, r *http.Request) {
+ parts := strings.Split(r.URL.Path, "/")
+ name := parts[len(parts)-1]
+
+ entries, err := a.getEntries(r.Context(), name)
+ if err != nil {
+ glog.Errorf("getEntries: %v", err)
+ w.WriteHeader(500)
+ fmt.Fprintf(w, "internal error")
+ return
+ }
+
+ entriesData := make([]struct {
+ Entry string
+ Author string
+ Added string
+ }, len(entries))
+ for i, entry := range entries {
+ entriesData[i].Entry = entry.Entry
+ entriesData[i].Author = entry.Author
+ entriesData[i].Added = time.Unix(entry.Added, 0).String()
+ }
+
+ tplTerm.Execute(w, map[string]interface{}{
+ "Name": name,
+ "Entries": entriesData,
+ })
+}