// Copyright 2012 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 dashboard // This file handles the front page. import ( "bytes" "html/template" "io" "net/http" "strings" "sync" "time" "appengine" "appengine/datastore" "appengine/user" ) func init() { http.HandleFunc("/", handleFront) http.HandleFunc("/favicon.ico", http.NotFound) } // maximum number of active CLs to show in person-specific tables. const maxCLs = 100 func handleFront(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) data := &frontPageData{ Reviewers: personList, User: user.Current(c).Email, IsAdmin: user.IsAdmin(c), } var currentPerson string u := data.User you := "you" if e := r.FormValue("email"); e != "" { u = e you = e } currentPerson, data.UserIsReviewer = emailToPerson[u] var wg sync.WaitGroup errc := make(chan error, 10) activeCLs := datastore.NewQuery("CL"). Filter("Closed =", false). Order("-Modified") tableFetch := func(index int, f func(tbl *clTable) error) { wg.Add(1) go func() { defer wg.Done() start := time.Now() if err := f(&data.Tables[index]); err != nil { errc <- err } data.Timing[index] = time.Now().Sub(start) }() } if data.UserIsReviewer { tableFetch(0, func(tbl *clTable) error { q := activeCLs.Filter("Reviewer =", currentPerson).Limit(maxCLs) tbl.Title = "CLs assigned to " + you + " for review" tbl.Assignable = true _, err := q.GetAll(c, &tbl.CLs) return err }) } tableFetch(1, func(tbl *clTable) error { q := activeCLs.Filter("Author =", currentPerson).Limit(maxCLs) tbl.Title = "CLs sent by " + you tbl.Assignable = true _, err := q.GetAll(c, &tbl.CLs) return err }) tableFetch(2, func(tbl *clTable) error { q := activeCLs.Limit(50) tbl.Title = "Other active CLs" tbl.Assignable = true if _, err := q.GetAll(c, &tbl.CLs); err != nil { return err } // filter if data.UserIsReviewer { for i := len(tbl.CLs) - 1; i >= 0; i-- { cl := tbl.CLs[i] if cl.Author == currentPerson || cl.Reviewer == currentPerson { // Preserve order. copy(tbl.CLs[i:], tbl.CLs[i+1:]) tbl.CLs = tbl.CLs[:len(tbl.CLs)-1] } } } return nil }) tableFetch(3, func(tbl *clTable) error { q := datastore.NewQuery("CL"). Filter("Closed =", true). Order("-Modified"). Limit(10) tbl.Title = "Recently closed CLs" tbl.Assignable = false _, err := q.GetAll(c, &tbl.CLs) return err }) // Not really a table fetch. tableFetch(0, func(_ *clTable) error { var err error data.LogoutURL, err = user.LogoutURL(c, "/") return err }) wg.Wait() select { case err := <-errc: c.Errorf("%v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return default: } var b bytes.Buffer if err := frontPage.ExecuteTemplate(&b, "front", &data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } io.Copy(w, &b) } type frontPageData struct { Tables [4]clTable Timing [4]time.Duration Reviewers []string UserIsReviewer bool User, LogoutURL string // actual logged in user IsAdmin bool } type clTable struct { Title string Assignable bool CLs []*CL } var frontPage = template.Must(template.New("front").Funcs(template.FuncMap{ "selected": func(a, b string) string { if a == b { return "selected" } return "" }, "shortemail": func(s string) string { if i := strings.Index(s, "@"); i >= 0 { s = s[:i] } return s }, }).Parse(` Go code reviews

Go code reviews

{{range $i, $tbl := .Tables}} {{if .CLs}} {{range $cl := .CLs}} {{end}} {{else}} {{end}} {{end}}

{{$tbl.Title}}

{{if $tbl.Assignable}} {{end}} {{.Number}}: {{.FirstLineHTML}} {{if and .LGTMs $tbl.Assignable}}
LGTMs: {{.LGTMHTML}}{{end}} {{if and .NotLGTMs $tbl.Assignable}}
NOT LGTMs: {{.NotLGTMHTML}}{{end}} {{if .LastUpdateBy}}
({{.LastUpdateBy | shortemail}}) {{.LastUpdate}}{{end}}
{{.ModifiedAgo}} {{if $.IsAdmin}}{{end}}
none

You are {{.User}} · logout
datastore timing: {{range .Timing}} {{.}}{{end}}
`))