Added match schedule report.

This commit is contained in:
Patrick Fairbank
2014-05-26 16:39:20 -07:00
parent c6990a5290
commit 834842f1d2
6 changed files with 221 additions and 2 deletions

View File

@@ -57,3 +57,9 @@ func (database *Database) DeleteMatch(match *Match) error {
func (database *Database) TruncateMatches() error {
return database.matchMap.TruncateTables()
}
func (database *Database) GetMatchesByType(matchType string) ([]Match, error) {
var matches []Match
err := database.teamMap.Select(&matches, "SELECT * FROM matches WHERE type = ? ORDER BY id", matchType)
return matches, err
}

View File

@@ -62,3 +62,31 @@ func TestTruncateMatches(t *testing.T) {
assert.Nil(t, err)
assert.Nil(t, match2)
}
func TestGetMatchesByType(t *testing.T) {
clearDb()
defer clearDb()
db, err := OpenDatabase(testDbPath)
assert.Nil(t, err)
defer db.Close()
match := Match{1, "qualification", "1", time.Now().UTC(), 1, false, 2, false, 3, false, 4, false, 5,
false, 6, false, "", time.Now().UTC()}
db.CreateMatch(&match)
match2 := Match{2, "practice", "1", time.Now().UTC(), 1, false, 2, false, 3, false, 4, false, 5,
false, 6, false, "", time.Now().UTC()}
db.CreateMatch(&match2)
match3 := Match{3, "practice", "2", time.Now().UTC(), 1, false, 2, false, 3, false, 4, false, 5,
false, 6, false, "", time.Now().UTC()}
db.CreateMatch(&match3)
matches, err := db.GetMatchesByType("test")
assert.Nil(t, err)
assert.Empty(t, matches)
matches, err = db.GetMatchesByType("practice")
assert.Nil(t, err)
assert.Equal(t, 2, len(matches))
matches, err = db.GetMatchesByType("qualification")
assert.Nil(t, err)
assert.Equal(t, 1, len(matches))
}

View File

@@ -8,11 +8,133 @@ package main
import (
"code.google.com/p/gofpdf"
"fmt"
"github.com/gorilla/mux"
"net/http"
"strconv"
"text/template"
)
// Generates a CSV-formatted report of the match schedule.
func ScheduleCsvReportHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
matches, err := db.GetMatchesByType(vars["type"])
if err != nil {
handleWebErr(w, err)
return
}
// Don't set the content type as "text/csv", as that will trigger an automatic download in the browser.
w.Header().Set("Content-Type", "text/plain")
template, err := template.ParseFiles("templates/schedule.csv")
if err != nil {
handleWebErr(w, err)
return
}
err = template.Execute(w, matches)
if err != nil {
handleWebErr(w, err)
return
}
}
// Generates a PDF-formatted report of the match schedule.
func SchedulePdfReportHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
matches, err := db.GetMatchesByType(vars["type"])
if err != nil {
handleWebErr(w, err)
return
}
teams, err := db.GetAllTeams()
if err != nil {
handleWebErr(w, err)
return
}
matchesPerTeam := 0
if len(teams) > 0 {
matchesPerTeam = len(matches) * teamsPerMatch / len(teams)
}
eventSettings, err := db.GetEventSettings()
if err != nil {
handleWebErr(w, err)
return
}
// The widths of the table columns in mm, stored here so that they can be referenced for each row.
colWidths := map[string]float64{"Time": 35, "Type": 25, "Match": 15, "Team": 20}
rowHeight := 6.5
pdf := gofpdf.New("P", "mm", "Letter", "font")
pdf.AddPage()
// Render table header row.
pdf.SetFont("Arial", "B", 10)
pdf.SetFillColor(220, 220, 220)
pdf.CellFormat(195, rowHeight, "Match Schedule - "+eventSettings.Name, "", 1, "C", false, 0, "")
pdf.CellFormat(colWidths["Time"], rowHeight, "Time", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Type"], rowHeight, "Type", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Match"], rowHeight, "Match", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Team"], rowHeight, "Red 1", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Team"], rowHeight, "Red 2", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Team"], rowHeight, "Red 3", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Team"], rowHeight, "Blue 1", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Team"], rowHeight, "Blue 2", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Team"], rowHeight, "Blue 3", "1", 1, "C", true, 0, "")
pdf.SetFont("Arial", "", 10)
for _, match := range matches {
height := rowHeight
borderStr := "1"
alignStr := "CM"
surrogate := false
if match.Red1IsSurrogate || match.Red2IsSurrogate || match.Red3IsSurrogate ||
match.Blue1IsSurrogate || match.Blue2IsSurrogate || match.Blue3IsSurrogate {
// If the match contains surrogates, the row needs to be taller to fit some text beneath team numbers.
height = 5.0
borderStr = "LTR"
alignStr = "CB"
surrogate = true
}
// Render match info row.
pdf.CellFormat(colWidths["Time"], height, match.Time.Local().Format("Mon 1/02 03:04 PM"), borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Type"], height, match.Type, borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Match"], height, match.DisplayName, borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Team"], height, strconv.Itoa(match.Red1), borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Team"], height, strconv.Itoa(match.Red2), borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Team"], height, strconv.Itoa(match.Red3), borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Team"], height, strconv.Itoa(match.Blue1), borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Team"], height, strconv.Itoa(match.Blue2), borderStr, 0, alignStr, false, 0, "")
pdf.CellFormat(colWidths["Team"], height, strconv.Itoa(match.Blue3), borderStr, 1, alignStr, false, 0, "")
if surrogate {
// Render the text that indicates which teams are surrogates.
height := 4.0
pdf.SetFont("Arial", "", 8)
pdf.CellFormat(colWidths["Time"], height, "", "LBR", 0, "C", false, 0, "")
pdf.CellFormat(colWidths["Type"], height, "", "LBR", 0, "C", false, 0, "")
pdf.CellFormat(colWidths["Match"], height, "", "LBR", 0, "C", false, 0, "")
pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red1IsSurrogate), "LBR", 0, "CT", false, 0, "")
pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red2IsSurrogate), "LBR", 0, "CT", false, 0, "")
pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red3IsSurrogate), "LBR", 0, "CT", false, 0, "")
pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue1IsSurrogate), "LBR", 0, "CT", false, 0, "")
pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue2IsSurrogate), "LBR", 0, "CT", false, 0, "")
pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue3IsSurrogate), "LBR", 1, "CT", false, 0, "")
pdf.SetFont("Arial", "", 10)
}
}
// Render some summary info at the bottom.
pdf.CellFormat(195, 10, fmt.Sprintf("Matches Per Team: %d", matchesPerTeam), "", 1, "L", false, 0, "")
// Write out the PDF file as the HTTP response.
w.Header().Set("Content-Type", "application/pdf")
err = pdf.Output(w)
if err != nil {
handleWebErr(w, err)
return
}
}
// Generates a CSV-formatted report of the team list.
func TeamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
teams, err := db.GetAllTeams()
if err != nil {
@@ -20,6 +142,7 @@ func TeamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Don't set the content type as "text/csv", as that will trigger an automatic download in the browser.
w.Header().Set("Content-Type", "text/plain")
template, err := template.ParseFiles("templates/teams.csv")
if err != nil {
@@ -33,6 +156,7 @@ func TeamsCsvReportHandler(w http.ResponseWriter, r *http.Request) {
}
}
// Generates a PDF-formatted report of the team list.
func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
teams, err := db.GetAllTeams()
if err != nil {
@@ -45,6 +169,7 @@ func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
return
}
// The widths of the table columns in mm, stored here so that they can be referenced for each row.
colWidths := map[string]float64{"Id": 12, "Name": 80, "Location": 80, "RookieYear": 23}
rowHeight := 6.5
@@ -52,6 +177,8 @@ func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
pdf.AddPage()
pdf.SetFont("Arial", "B", 10)
pdf.SetFillColor(220, 220, 220)
// Render table header row.
pdf.CellFormat(195, rowHeight, "Team List - "+eventSettings.Name, "", 1, "C", false, 0, "")
pdf.CellFormat(colWidths["Id"], rowHeight, "Team", "1", 0, "C", true, 0, "")
pdf.CellFormat(colWidths["Name"], rowHeight, "Name", "1", 0, "C", true, 0, "")
@@ -59,12 +186,15 @@ func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
pdf.CellFormat(colWidths["RookieYear"], rowHeight, "Rookie Year", "1", 1, "C", true, 0, "")
pdf.SetFont("Arial", "", 10)
for _, team := range teams {
// Render team info row.
pdf.CellFormat(colWidths["Id"], rowHeight, strconv.Itoa(team.Id), "1", 0, "L", false, 0, "")
pdf.CellFormat(colWidths["Name"], rowHeight, team.Nickname, "1", 0, "L", false, 0, "")
location := fmt.Sprintf("%s, %s, %s", team.City, team.StateProv, team.Country)
pdf.CellFormat(colWidths["Location"], rowHeight, location, "1", 0, "L", false, 0, "")
pdf.CellFormat(colWidths["RookieYear"], rowHeight, strconv.Itoa(team.RookieYear), "1", 1, "L", false, 0, "")
}
// Write out the PDF file as the HTTP response.
w.Header().Set("Content-Type", "application/pdf")
err = pdf.Output(w)
if err != nil {
@@ -72,3 +202,12 @@ func TeamsPdfReportHandler(w http.ResponseWriter, r *http.Request) {
return
}
}
// Returns the text to display if a team is a surrogate.
func surrogateText(isSurrogate bool) string {
if isSurrogate {
return "(surrogate)"
} else {
return ""
}
}

View File

@@ -8,8 +8,49 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestScheduleCsvReport(t *testing.T) {
clearDb()
defer clearDb()
db, _ = OpenDatabase(testDbPath)
match1 := Match{1, "qualification", "1", time.Unix(0, 0), 1, false, 2, false, 3, false, 4, true, 5, true, 6,
true, "", time.Now()}
match2 := Match{2, "qualification", "2", time.Unix(600, 0), 7, true, 8, true, 9, true, 10, false, 11, false,
12, false, "", time.Now()}
match3 := Match{3, "practice", "1", time.Now(), 6, false, 5, false, 4, false, 3, false, 2, false, 1, false,
"", time.Now()}
db.CreateMatch(&match1)
db.CreateMatch(&match2)
db.CreateMatch(&match3)
recorder := getHttpResponse("/reports/csv/schedule/qualification")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0])
expectedBody := "Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1," +
"Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate\n1,qualification," +
"1969-12-31 16:00:00 -0800 PST,1,false,2,false,3,false,4,true,5,true,6,true\n" +
"2,qualification,1969-12-31 16:10:00 -0800 PST,7,true,8,true,9,true,10,false,11,false,12,false\n\n"
assert.Equal(t, expectedBody, recorder.Body.String())
}
func TestSchedulePdfReport(t *testing.T) {
clearDb()
defer clearDb()
db, _ = OpenDatabase(testDbPath)
match := Match{1, "practice", "1", time.Unix(0, 0), 1, false, 2, false, 3, false, 4, true, 5,
true, 6, true, "", time.Now().UTC()}
db.CreateMatch(&match)
team := Team{254, "NASA", "The Cheesy Poofs", "San Jose", "CA", "USA", 1999, "Barrage"}
db.CreateTeam(&team)
// Can't really parse the PDF content and check it, so just check that what's sent back is a PDF.
recorder := getHttpResponse("/reports/pdf/schedule/practice")
assert.Equal(t, 200, recorder.Code)
assert.Equal(t, "application/pdf", recorder.HeaderMap["Content-Type"][0])
}
func TestTeamsCsvReport(t *testing.T) {
clearDb()
defer clearDb()
@@ -32,8 +73,8 @@ func TestTeamsPdfReport(t *testing.T) {
clearDb()
defer clearDb()
db, _ = OpenDatabase(testDbPath)
team1 := Team{254, "NASA", "The Cheesy Poofs", "San Jose", "CA", "USA", 1999, "Barrage"}
db.CreateTeam(&team1)
team := Team{254, "NASA", "The Cheesy Poofs", "San Jose", "CA", "USA", 1999, "Barrage"}
db.CreateTeam(&team)
// Can't really parse the PDF content and check it, so just check that what's sent back is a PDF.
recorder := getHttpResponse("/reports/pdf/teams")

3
templates/schedule.csv Normal file
View File

@@ -0,0 +1,3 @@
Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1,Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate
{{range $match := .}}{{$match.DisplayName}},{{$match.Type}},{{$match.Time.Local}},{{$match.Red1}},{{$match.Red1IsSurrogate}},{{$match.Red2}},{{$match.Red2IsSurrogate}},{{$match.Red3}},{{$match.Red3IsSurrogate}},{{$match.Blue1}},{{$match.Blue1IsSurrogate}},{{$match.Blue2}},{{$match.Blue2IsSurrogate}},{{$match.Blue3}},{{$match.Blue3IsSurrogate}}
{{end}}
1 Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1,Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate
2 {{range $match := .}}{{$match.DisplayName}},{{$match.Type}},{{$match.Time.Local}},{{$match.Red1}},{{$match.Red1IsSurrogate}},{{$match.Red2}},{{$match.Red2IsSurrogate}},{{$match.Red3}},{{$match.Red3IsSurrogate}},{{$match.Blue1}},{{$match.Blue1IsSurrogate}},{{$match.Blue2}},{{$match.Blue2IsSurrogate}},{{$match.Blue3}},{{$match.Blue3IsSurrogate}}
3 {{end}}

2
web.go
View File

@@ -22,6 +22,8 @@ func ServeWebInterface() {
func newHandler() http.Handler {
router := mux.NewRouter()
router.HandleFunc("/reports/csv/schedule/{type}", ScheduleCsvReportHandler)
router.HandleFunc("/reports/pdf/schedule/{type}", SchedulePdfReportHandler)
router.HandleFunc("/reports/csv/teams", TeamsCsvReportHandler)
router.HandleFunc("/reports/pdf/teams", TeamsPdfReportHandler)
return router