mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Added team list setup page.
This commit is contained in:
@@ -7,13 +7,13 @@ package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/gofpdf"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"io"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Generates a CSV-formatted report of the qualification rankings.
|
||||
|
||||
247
setup_teams.go
Normal file
247
setup_teams.go
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Web routes for configuring the team list.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var officialTeamInfoUrl = "https://my.usfirst.org/frc/scoring/index.lasso?page=teamlist"
|
||||
var officialTeamInfo map[int][]string
|
||||
|
||||
// Shows the team list.
|
||||
func TeamsGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
renderTeams(w, r, false)
|
||||
}
|
||||
|
||||
// Adds teams to the team list.
|
||||
func TeamsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !canModifyTeamList() {
|
||||
renderTeams(w, r, true)
|
||||
return
|
||||
}
|
||||
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
var teamNumbers []int
|
||||
for _, teamNumberString := range strings.Split(r.PostFormValue("teamNumbers"), "\r\n") {
|
||||
teamNumber, err := strconv.Atoi(teamNumberString)
|
||||
if err == nil {
|
||||
teamNumbers = append(teamNumbers, teamNumber)
|
||||
}
|
||||
}
|
||||
|
||||
for _, teamNumber := range teamNumbers {
|
||||
team, err := getOfficialTeamInfo(teamNumber)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
err = db.CreateTeam(team)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
renderTeams(w, r, false)
|
||||
}
|
||||
|
||||
// Clears the team list.
|
||||
func TeamsClearHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !canModifyTeamList() {
|
||||
renderTeams(w, r, true)
|
||||
return
|
||||
}
|
||||
|
||||
err := db.TruncateTeams()
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
renderTeams(w, r, false)
|
||||
}
|
||||
|
||||
// Shows the page to edit a team's fields.
|
||||
func TeamEditGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
teamId, _ := strconv.Atoi(vars["id"])
|
||||
team, err := db.GetTeamById(teamId)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
if team == nil {
|
||||
http.Error(w, fmt.Sprintf("Error: No such team: %d", teamId), 400)
|
||||
return
|
||||
}
|
||||
|
||||
template, err := template.ParseFiles("templates/edit_team.html", "templates/base.html")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
err = template.ExecuteTemplate(w, "base", team)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Updates a team's fields.
|
||||
func TeamEditPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
teamId, _ := strconv.Atoi(vars["id"])
|
||||
team, err := db.GetTeamById(teamId)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
if team == nil {
|
||||
http.Error(w, fmt.Sprintf("Error: No such team: %d", teamId), 400)
|
||||
return
|
||||
}
|
||||
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
team.Name = r.PostFormValue("name")
|
||||
team.Nickname = r.PostFormValue("nickname")
|
||||
team.City = r.PostFormValue("city")
|
||||
team.StateProv = r.PostFormValue("stateProv")
|
||||
team.Country = r.PostFormValue("country")
|
||||
rookieYear, _ := strconv.Atoi(r.PostFormValue("rookieYear"))
|
||||
team.RookieYear = rookieYear
|
||||
team.RobotName = r.PostFormValue("robotName")
|
||||
err = db.SaveTeam(team)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
renderTeams(w, r, false)
|
||||
}
|
||||
|
||||
// Removes a team from the team list.
|
||||
func TeamDeletePostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !canModifyTeamList() {
|
||||
renderTeams(w, r, true)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
teamId, _ := strconv.Atoi(vars["id"])
|
||||
team, err := db.GetTeamById(teamId)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
if team == nil {
|
||||
http.Error(w, fmt.Sprintf("Error: No such team: %d", teamId), 400)
|
||||
return
|
||||
}
|
||||
err = db.DeleteTeam(team)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
renderTeams(w, r, false)
|
||||
}
|
||||
|
||||
func renderTeams(w http.ResponseWriter, r *http.Request, showErrorMessage bool) {
|
||||
teams, err := db.GetAllTeams()
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
template, err := template.ParseFiles("templates/teams.html", "templates/base.html")
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
data := struct {
|
||||
Teams []Team
|
||||
ShowErrorMessage bool
|
||||
}{teams, showErrorMessage}
|
||||
err = template.ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it is safe to change the team list (i.e. no matches/results exist yet).
|
||||
func canModifyTeamList() bool {
|
||||
matches, err := db.GetMatchesByType("qualification")
|
||||
if err != nil || len(matches) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns the data for the given team number.
|
||||
func getOfficialTeamInfo(teamId int) (*Team, error) {
|
||||
if officialTeamInfo == nil {
|
||||
// Download all team info from the FIRST website if it is not cached.
|
||||
resp, err := http.Get(officialTeamInfoUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
re := regexp.MustCompile("(?s).*<PRE>(.*)</PRE>.*")
|
||||
teamsCsv := re.FindStringSubmatch(string(body))[1]
|
||||
|
||||
reader := csv.NewReader(strings.NewReader(teamsCsv))
|
||||
reader.Comma = '\t'
|
||||
reader.FieldsPerRecord = -1
|
||||
officialTeamInfo = make(map[int][]string)
|
||||
reader.Read() // Ignore header line.
|
||||
for {
|
||||
fields, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
teamNumber, err := strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
officialTeamInfo[teamNumber] = fields
|
||||
}
|
||||
}
|
||||
|
||||
teamData, ok := officialTeamInfo[teamId]
|
||||
var team Team
|
||||
if ok {
|
||||
rookieYear, _ := strconv.Atoi(teamData[8])
|
||||
team = Team{teamId, html.UnescapeString(teamData[2]), html.UnescapeString(teamData[7]),
|
||||
html.UnescapeString(teamData[4]), html.UnescapeString(teamData[5]), html.UnescapeString(teamData[6]),
|
||||
rookieYear, html.UnescapeString(teamData[9])}
|
||||
} else {
|
||||
// If no team data exists, just fill in the team number.
|
||||
team = Team{Id: teamId}
|
||||
}
|
||||
return &team, nil
|
||||
}
|
||||
130
setup_teams_test.go
Normal file
130
setup_teams_test.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2014 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetupTeams(t *testing.T) {
|
||||
clearDb()
|
||||
defer clearDb()
|
||||
var err error
|
||||
db, err = OpenDatabase(testDbPath)
|
||||
assert.Nil(t, err)
|
||||
defer db.Close()
|
||||
|
||||
// Check that there are no teams to start.
|
||||
recorder := getHttpResponse("/setup/teams")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "0 teams")
|
||||
|
||||
// Mock the URL to download team info from.
|
||||
teamInfoBody := "<PRE>\nID_team\tteam_number\tteam_name\tteam_name_short\tteam_city\tteam_stateprov\t" +
|
||||
"team_country\tteam_nickname team_rookieyear robot_name\n1\t254\tNASA\tChezy\tThe Cheesy Poofs\t" +
|
||||
"San Jose\tCA\tUSA\t1999\tBarrage\n</PRE>"
|
||||
teamInfoServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, teamInfoBody)
|
||||
}))
|
||||
defer teamInfoServer.Close()
|
||||
officialTeamInfoUrl = teamInfoServer.URL
|
||||
|
||||
// Add some teams.
|
||||
recorder = postHttpResponse("/setup/teams", "teamNumbers=254\r\nnotateam\r\n1114\r\n")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "2 teams")
|
||||
assert.Contains(t, recorder.Body.String(), "The Cheesy Poofs")
|
||||
assert.Contains(t, recorder.Body.String(), "1114")
|
||||
|
||||
// Add another team.
|
||||
recorder = postHttpResponse("/setup/teams", "teamNumbers=33")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "3 teams")
|
||||
assert.Contains(t, recorder.Body.String(), "33")
|
||||
|
||||
// Edit a team.
|
||||
recorder = getHttpResponse("/setup/teams/254/edit")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "The Cheesy Poofs")
|
||||
recorder = postHttpResponse("/setup/teams/254/edit", "nickname=Teh Chezy Pofs")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "Teh Chezy Pofs")
|
||||
|
||||
// Delete a team.
|
||||
recorder = postHttpResponse("/setup/teams/1114/delete", "")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "2 teams")
|
||||
|
||||
// Test clearing all teams.
|
||||
recorder = postHttpResponse("/setup/teams/clear", "")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "0 teams")
|
||||
}
|
||||
|
||||
func TestSetupTeamsDisallowModification(t *testing.T) {
|
||||
clearDb()
|
||||
defer clearDb()
|
||||
var err error
|
||||
db, err = OpenDatabase(testDbPath)
|
||||
assert.Nil(t, err)
|
||||
defer db.Close()
|
||||
db.CreateTeam(&Team{Id: 254, Nickname: "The Cheesy Poofs"})
|
||||
db.CreateMatch(&Match{Type: "qualification"})
|
||||
|
||||
// Disallow adding teams.
|
||||
recorder := postHttpResponse("/setup/teams", "teamNumbers=33")
|
||||
assert.Contains(t, recorder.Body.String(), "can't modify")
|
||||
assert.Contains(t, recorder.Body.String(), "1 teams")
|
||||
assert.Contains(t, recorder.Body.String(), "The Cheesy Poofs")
|
||||
|
||||
// Disallow deleting team.
|
||||
recorder = postHttpResponse("/setup/teams/254/delete", "")
|
||||
assert.Contains(t, recorder.Body.String(), "can't modify")
|
||||
assert.Contains(t, recorder.Body.String(), "1 teams")
|
||||
assert.Contains(t, recorder.Body.String(), "The Cheesy Poofs")
|
||||
|
||||
// Disallow clearing all teams.
|
||||
recorder = postHttpResponse("/setup/teams/clear", "")
|
||||
assert.Contains(t, recorder.Body.String(), "can't modify")
|
||||
assert.Contains(t, recorder.Body.String(), "1 teams")
|
||||
assert.Contains(t, recorder.Body.String(), "The Cheesy Poofs")
|
||||
|
||||
// Allow editing a team.
|
||||
recorder = postHttpResponse("/setup/teams/254/edit", "nickname=Teh Chezy Pofs")
|
||||
assert.NotContains(t, recorder.Body.String(), "can't modify")
|
||||
assert.Contains(t, recorder.Body.String(), "1 teams")
|
||||
assert.Contains(t, recorder.Body.String(), "Teh Chezy Pofs")
|
||||
}
|
||||
|
||||
func TestSetupTeamsBadReqest(t *testing.T) {
|
||||
clearDb()
|
||||
defer clearDb()
|
||||
var err error
|
||||
db, err = OpenDatabase(testDbPath)
|
||||
assert.Nil(t, err)
|
||||
defer db.Close()
|
||||
|
||||
recorder := getHttpResponse("/setup/teams/254/edit")
|
||||
assert.Equal(t, 400, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "No such team")
|
||||
recorder = postHttpResponse("/setup/teams/254/edit", "")
|
||||
assert.Equal(t, 400, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "No such team")
|
||||
recorder = postHttpResponse("/setup/teams/254/delete", "")
|
||||
assert.Equal(t, 400, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "No such team")
|
||||
}
|
||||
|
||||
func postHttpResponse(path string, body string) *httptest.ResponseRecorder {
|
||||
recorder := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", path, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
|
||||
newHandler().ServeHTTP(recorder, req)
|
||||
return recorder
|
||||
}
|
||||
2
static/css/bootstrap.min.css
vendored
2
static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<title>{{template "title" .}}</title>
|
||||
<link rel="shortcut icon" href="/static/img/favicon32.png">
|
||||
<link href="/static/lib/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-default navbar-static-top" role="navigation">
|
||||
@@ -19,7 +19,7 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">Event Wizard</a></li>
|
||||
<li><a href="#">Settings</a></li>
|
||||
<li><a href="#">Team List</a></li>
|
||||
<li><a href="/setup/teams">Team List</a></li>
|
||||
<li><a href="#">Match Scheduling</a></li>
|
||||
<li><a href="#">Alliance Selection</a></li>
|
||||
</ul>
|
||||
|
||||
62
templates/edit_team.html
Normal file
62
templates/edit_team.html
Normal file
@@ -0,0 +1,62 @@
|
||||
{{define "title"}}Cheesy Arena{{end}}
|
||||
{{define "body"}}
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-lg-offset-3">
|
||||
<div class="well">
|
||||
<form class="form-horizontal" action="/setup/teams/{{.Id}}/edit" method="POST">
|
||||
<fieldset>
|
||||
<legend>Edit Team {{.Id}}</legend>
|
||||
<div class="form-group">
|
||||
<label for="textArea" class="col-lg-3 control-label">Name</label>
|
||||
<div class="col-lg-9">
|
||||
<textarea class="form-control" rows="5" name="name">{{.Name}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail" class="col-lg-3 control-label">Nickname</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" class="form-control" name="nickname" value="{{.Nickname}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail" class="col-lg-3 control-label">City</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" class="form-control" name="city" value="{{.City}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail" class="col-lg-3 control-label">State/Province</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" class="form-control" name="stateProv" value="{{.StateProv}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail" class="col-lg-3 control-label">Country</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" class="form-control" name="country" value="{{.Country}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail" class="col-lg-3 control-label">Rookie Year</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" class="form-control" name="rookieYear" value="{{.RookieYear}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputEmail" class="col-lg-3 control-label">Robot Name</label>
|
||||
<div class="col-lg-9">
|
||||
<input type="text" class="form-control" name="robotName" value="{{.RobotName}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-9 col-lg-offset-3">
|
||||
<a href="/setup/teams"><button type="button" class="btn btn-default">Cancel</button></a>
|
||||
<button type="submit" class="btn btn-info">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
89
templates/teams.html
Normal file
89
templates/teams.html
Normal file
@@ -0,0 +1,89 @@
|
||||
{{define "title"}}Cheesy Arena{{end}}
|
||||
{{define "body"}}
|
||||
{{if .ShowErrorMessage}}
|
||||
<div class="alert alert-dismissable alert-danger">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
You can't modify the team list once the qualification schedule has been generated. If you need to change the
|
||||
team list, clear all other data first on the Settings page.
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="row">
|
||||
<div class="col-lg-2">
|
||||
<form class="form-horizontal" action="/setup/teams" method="POST">
|
||||
<fieldset>
|
||||
<legend>Import Teams</legend>
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="10" name="teamNumbers"
|
||||
placeholder="One team number per line"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-info">Add Teams</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary" onclick="$('#confirmClearTeams').modal('show'); return false;">
|
||||
Clear Team List
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-lg-10">
|
||||
<table class="table table-striped table-hover ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>Nickname</th>
|
||||
<th>Location</th>
|
||||
<th>Rookie Year</th>
|
||||
<th>Robot Name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $team := .Teams}}
|
||||
<tr>
|
||||
<td>{{$team.Id}}</td>
|
||||
<td>{{$team.Name}}</td>
|
||||
<td>{{$team.Nickname}}</td>
|
||||
<td>{{$team.City}}, {{$team.StateProv}}, {{$team.Country}}</td>
|
||||
<td>{{$team.RookieYear}}</td>
|
||||
<td>{{$team.RobotName}}</td>
|
||||
<td class="text-center" style="white-space: nowrap;">
|
||||
<form action="/setup/teams/{{$team.Id}}/delete" method="POST">
|
||||
<a href="/setup/teams/{{$team.Id}}/edit">
|
||||
<button type="button" class="btn btn-info btn-xs">
|
||||
<i class="glyphicon glyphicon-edit"></i>
|
||||
</button>
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary btn-xs">
|
||||
<i class="glyphicon glyphicon-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<b>{{len .Teams}} teams</b>
|
||||
</div>
|
||||
<div id="confirmClearTeams" class="modal" style="top: 20%;">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Confirm</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to clear the team list?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<form class="form-horizontal" action="/setup/teams/clear" method="POST">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Clear Team List</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
30
web.go
30
web.go
@@ -38,21 +38,21 @@ func ServeWebInterface() {
|
||||
|
||||
// Open in Default Web Browser
|
||||
// Necessary to Authenticate
|
||||
url := "http://localhost:"+strconv.Itoa(httpPort)
|
||||
url := "http://localhost:" + strconv.Itoa(httpPort)
|
||||
var err error
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
case "windows":
|
||||
err = exec.Command(`rundll32.exe`, "url.dll,FileProtocolHandler", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
case "windows":
|
||||
err = exec.Command(`rundll32.exe`, "url.dll,FileProtocolHandler", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
|
||||
// Start Server
|
||||
http.ListenAndServe(fmt.Sprintf(":%d", httpPort), nil)
|
||||
@@ -60,6 +60,12 @@ func ServeWebInterface() {
|
||||
|
||||
func newHandler() http.Handler {
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/setup/teams", TeamsGetHandler).Methods("GET")
|
||||
router.HandleFunc("/setup/teams", TeamsPostHandler).Methods("POST")
|
||||
router.HandleFunc("/setup/teams/clear", TeamsClearHandler).Methods("POST")
|
||||
router.HandleFunc("/setup/teams/{id}/edit", TeamEditGetHandler).Methods("GET")
|
||||
router.HandleFunc("/setup/teams/{id}/edit", TeamEditPostHandler).Methods("POST")
|
||||
router.HandleFunc("/setup/teams/{id}/delete", TeamDeletePostHandler).Methods("POST")
|
||||
router.HandleFunc("/reports/csv/rankings", RankingsCsvReportHandler)
|
||||
router.HandleFunc("/reports/pdf/rankings", RankingsPdfReportHandler)
|
||||
router.HandleFunc("/reports/json/rankings", RankingsJSONReportHandler)
|
||||
|
||||
Reference in New Issue
Block a user