mirror of https://github.com/dirtbags/moth.git
Everything but puzzle content serving
This commit is contained in:
parent
7942f4fc5a
commit
5075bb601d
|
@ -32,6 +32,10 @@ input {
|
||||||
padding: 0.6em;
|
padding: 0.6em;
|
||||||
margin: 0.2em;
|
margin: 0.2em;
|
||||||
}
|
}
|
||||||
|
#scoreboard .category {
|
||||||
|
border: solid white 1px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
nav {
|
nav {
|
||||||
border: solid black 2px;
|
border: solid black 2px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Open Puzzles</title>
|
<title>Scoreboard</title>
|
||||||
<link rel="stylesheet" href="basic.css">
|
<link rel="stylesheet" href="basic.css">
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
<link rel="icon" href="res/icon.svg" type="image/svg+xml">
|
<link rel="icon" href="res/icon.svg" type="image/svg+xml">
|
||||||
|
@ -97,6 +97,7 @@ function scoreboard(element, continuous) {
|
||||||
var width = maxWidth * catPct;
|
var width = maxWidth * catPct;
|
||||||
|
|
||||||
var bar = document.createElement("span");
|
var bar = document.createElement("span");
|
||||||
|
bar.classList.add("category");
|
||||||
bar.classList.add("cat" + ncat);
|
bar.classList.add("cat" + ncat);
|
||||||
bar.style.width = width + "%";
|
bar.style.width = width + "%";
|
||||||
bar.textContent = category + ": " + catTeam;
|
bar.textContent = category + ": " + catTeam;
|
||||||
|
|
|
@ -17,7 +17,7 @@ type Award struct {
|
||||||
func ParseAward(s string) (*Award, error) {
|
func ParseAward(s string) (*Award, error) {
|
||||||
ret := Award{}
|
ret := Award{}
|
||||||
|
|
||||||
s = strings.Trim(s, " \t\n")
|
s = strings.TrimSpace(s)
|
||||||
|
|
||||||
var whenEpoch int64
|
var whenEpoch int64
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,9 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"rand"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -287,19 +285,37 @@ func (ctx Instance) puzzlesHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx Instance) pointsHandler(w http.ResponseWriter, req *http.Request) {
|
func (ctx Instance) pointsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
plog := ctx.PointsLog()
|
var ret struct {
|
||||||
jlog, err := json.Marshal(plog)
|
Teams map[string]string `json:"teams"`
|
||||||
|
Points []*Award `json:"points"`
|
||||||
|
}
|
||||||
|
ret.Teams = map[string]string{}
|
||||||
|
ret.Points = ctx.PointsLog()
|
||||||
|
|
||||||
|
teamNumbersById := map[string]int{}
|
||||||
|
for nr, a := range ret.Points {
|
||||||
|
teamNumber, ok := teamNumbersById[a.TeamId]
|
||||||
|
if !ok {
|
||||||
|
teamName, err := ctx.TeamName(a.TeamId)
|
||||||
|
if err != nil {
|
||||||
|
teamName = "[unregistered]"
|
||||||
|
}
|
||||||
|
teamNumber = nr
|
||||||
|
teamNumbersById[a.TeamId] = teamNumber
|
||||||
|
ret.Teams[strconv.FormatInt(int64(teamNumber), 16)] = teamName
|
||||||
|
}
|
||||||
|
a.TeamId = strconv.FormatInt(int64(teamNumber), 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
jret, err := json.Marshal(ret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: go through plog, building an array of teams, so we can anonymize team IDs
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(jret)
|
||||||
w.Write(jlog)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx Instance) staticHandler(w http.ResponseWriter, req *http.Request) {
|
func (ctx Instance) staticHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
|
@ -159,3 +159,9 @@ func (ctx *Instance) OpenCategoryFile(category string, parts ...string) (io.Read
|
||||||
f, err := mb.Open(filename)
|
f, err := mb.Open(filename)
|
||||||
return f, err
|
return f, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *Instance) TeamName(teamId string) (string, error) {
|
||||||
|
teamNameBytes, err := ioutil.ReadFile(ctx.StatePath("teams", teamId))
|
||||||
|
teamName := strings.TrimSpace(string(teamNameBytes))
|
||||||
|
return teamName, err
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,16 @@ func (ctx *Instance) Tidy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh all current categories
|
||||||
|
for categoryName, mb := range ctx.Categories {
|
||||||
|
if err := mb.Refresh(); err != nil {
|
||||||
|
// Backing file vanished: remove this category
|
||||||
|
log.Printf("Removing category: %s: %s", categoryName, err)
|
||||||
|
mb.Close()
|
||||||
|
delete(ctx.Categories, categoryName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Any new categories?
|
// Any new categories?
|
||||||
files, err := ioutil.ReadDir(ctx.MothballPath())
|
files, err := ioutil.ReadDir(ctx.MothballPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,9 +68,6 @@ func (ctx *Instance) Tidy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any old categories?
|
|
||||||
log.Print("XXX: Check for and reap old categories")
|
|
||||||
|
|
||||||
ctx.CollectPoints()
|
ctx.CollectPoints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,10 @@ func ShowHtml(w http.ResponseWriter, status Status, title string, body string) {
|
||||||
fmt.Fprintf(w, "<html><head>")
|
fmt.Fprintf(w, "<html><head>")
|
||||||
fmt.Fprintf(w, "<title>%s</title>", title)
|
fmt.Fprintf(w, "<title>%s</title>", title)
|
||||||
fmt.Fprintf(w, "<link rel=\"stylesheet\" href=\"basic.css\">")
|
fmt.Fprintf(w, "<link rel=\"stylesheet\" href=\"basic.css\">")
|
||||||
fmt.Fprintf(w, "<meta name=\"viewport\" content=\"width=device-width\"></head>")
|
fmt.Fprintf(w, "<meta name=\"viewport\" content=\"width=device-width\">")
|
||||||
fmt.Fprintf(w, "<link rel=\"icon\" href=\"res/icon.svg\" type=\"image/svg+xml\">")
|
fmt.Fprintf(w, "<link rel=\"icon\" href=\"res/icon.svg\" type=\"image/svg+xml\">")
|
||||||
fmt.Fprintf(w, "<link rel=\"icon\" href=\"res/icon.png\" type=\"image/png\">")
|
fmt.Fprintf(w, "<link rel=\"icon\" href=\"res/icon.png\" type=\"image/png\">")
|
||||||
fmt.Fprintf(w, "<body><h1 class=\"%s\">%s</h1>", statusStr, title)
|
fmt.Fprintf(w, "</head><body><h1 class=\"%s\">%s</h1>", statusStr, title)
|
||||||
fmt.Fprintf(w, "<section>%s</section>", body)
|
fmt.Fprintf(w, "<section>%s</section>", body)
|
||||||
fmt.Fprintf(w, "<nav>")
|
fmt.Fprintf(w, "<nav>")
|
||||||
fmt.Fprintf(w, "<ul>")
|
fmt.Fprintf(w, "<ul>")
|
||||||
|
@ -159,9 +159,7 @@ func staticScoreboard(w http.ResponseWriter) {
|
||||||
w, Success,
|
w, Success,
|
||||||
"Scoreboard",
|
"Scoreboard",
|
||||||
`
|
`
|
||||||
<section>
|
|
||||||
<div id="scoreboard"></div>
|
<div id="scoreboard"></div>
|
||||||
</section>
|
|
||||||
<script>
|
<script>
|
||||||
function loadJSON(url, callback) {
|
function loadJSON(url, callback) {
|
||||||
function loaded(e) {
|
function loaded(e) {
|
||||||
|
|
Loading…
Reference in New Issue