Display results
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
"vkane.cz/tinyquiz/pkg/model"
|
||||
"vkane.cz/tinyquiz/pkg/model/ent"
|
||||
"vkane.cz/tinyquiz/pkg/rtcomm"
|
||||
)
|
||||
|
||||
func (app *application) home(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
@@ -148,6 +149,10 @@ func (app *application) nextQuestion(w http.ResponseWriter, r *http.Request, par
|
||||
app.serverError(w, err)
|
||||
return
|
||||
}
|
||||
} else if errors.Is(err, model.NoNextQuestion) {
|
||||
app.rtClients.SendToAll(sessionId, rtcomm.StateUpdate{Results: true})
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
} else {
|
||||
app.serverError(w, err)
|
||||
return
|
||||
@@ -190,3 +195,36 @@ func (app *application) answer(w http.ResponseWriter, r *http.Request, params ht
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (app *application) resultsGeneral(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
type resultsData struct {
|
||||
Results []model.PlayerResult
|
||||
Session *ent.Session
|
||||
Player *ent.Player
|
||||
templateData
|
||||
}
|
||||
td := &resultsData{}
|
||||
setDefaultTemplateData(&td.templateData)
|
||||
|
||||
var playerUid uuid.UUID
|
||||
if uid, err := uuid.Parse(params.ByName("playerUid")); err == nil {
|
||||
playerUid = uid
|
||||
} else {
|
||||
app.clientError(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if results, session, player, err := app.model.GetResults(playerUid, r.Context()); err == nil {
|
||||
td.Results = results
|
||||
td.Session = session
|
||||
td.Player = player
|
||||
} else if errors.Is(err, model.NoSuchEntity) {
|
||||
app.clientError(w, http.StatusNotFound)
|
||||
return
|
||||
} else {
|
||||
app.serverError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
app.render(w, r, "results.page.tmpl.html", td)
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ func main() {
|
||||
mux.GET("/game/:playerUid", app.game)
|
||||
mux.POST("/game/:playerUid/rpc/next", app.nextQuestion)
|
||||
mux.POST("/game/:playerUid/answers/:choiceUid", app.answer)
|
||||
mux.GET("/results/:playerUid", app.resultsGeneral)
|
||||
|
||||
mux.GET("/ws/:playerUid", app.processWebSocket)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"github.com/google/uuid"
|
||||
"sort"
|
||||
"time"
|
||||
"vkane.cz/tinyquiz/pkg/codeGenerator"
|
||||
"vkane.cz/tinyquiz/pkg/model/ent"
|
||||
@@ -305,6 +306,75 @@ func (m *Model) SaveAnswer(playerId uuid.UUID, choiceId uuid.UUID, now time.Time
|
||||
}
|
||||
}
|
||||
|
||||
type PlayerResult struct {
|
||||
Player *ent.Player
|
||||
place uint64
|
||||
correct int64
|
||||
}
|
||||
|
||||
func (r PlayerResult) Points() int64 {
|
||||
return r.correct
|
||||
}
|
||||
|
||||
func (r PlayerResult) Place() uint64 {
|
||||
return r.place
|
||||
}
|
||||
|
||||
func (m *Model) GetResults(playerId uuid.UUID, c context.Context) ([]PlayerResult, *ent.Session, *ent.Player, error) {
|
||||
tx, err := m.c.BeginTx(c, &sql.TxOptions{
|
||||
Isolation: sql.LevelRepeatableRead,
|
||||
ReadOnly: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
defer tx.Commit()
|
||||
|
||||
s, err := tx.Session.Query().WithGame().Where(session.HasPlayersWith(player.ID(playerId))).Only(c)
|
||||
if ent.IsNotFound(err) {
|
||||
return nil, nil, nil, NoSuchEntity
|
||||
} else if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
p, err := tx.Player.Query().Where(player.ID(playerId)).Only(c)
|
||||
if ent.IsNotFound(err) {
|
||||
return nil, nil, nil, NoSuchEntity
|
||||
} else if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if players, err := tx.Player.Query().Where(player.HasSessionWith(session.ID(s.ID))).Where(player.Organiser(false)).Order(ent.Asc(player.FieldName)).WithAnswers(func(q *ent.AnswerQuery) { q.WithChoice() }).All(c); err == nil {
|
||||
var results = make([]PlayerResult, 0, len(players))
|
||||
for _, p := range players {
|
||||
var res PlayerResult
|
||||
res.Player = p
|
||||
for _, a := range p.Edges.Answers {
|
||||
if a.Edges.Choice.Correct {
|
||||
res.correct++
|
||||
}
|
||||
}
|
||||
results = append(results, res)
|
||||
}
|
||||
sort.SliceStable(results, func(i, j int) bool { return results[i].correct > results[j].correct }) // sort in reverse
|
||||
if len(results) > 0 {
|
||||
results[0].place = 1
|
||||
}
|
||||
var place uint64 = 2
|
||||
for i := 1; i < len(results); i++ {
|
||||
if results[i].Points() == results[i-1].Points() {
|
||||
results[i].place = results[i-1].place
|
||||
} else {
|
||||
results[i].place = place
|
||||
}
|
||||
place++
|
||||
}
|
||||
return results, s, p, nil
|
||||
} else {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
const codeRandomPartLength uint8 = 3
|
||||
|
||||
func (m *Model) getCodeIncremental(c context.Context) (uint64, error) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package rtcomm
|
||||
|
||||
type StateUpdate struct {
|
||||
Players []Player `json:"players"`
|
||||
Players []Player `json:"players,omitempty"`
|
||||
Question *QuestionUpdate `json:"question,omitempty"`
|
||||
Results bool `json:"results,omitempty"`
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
|
||||
@@ -111,6 +111,10 @@
|
||||
questionSection.appendChild(questionClone);
|
||||
}
|
||||
}
|
||||
|
||||
if ('results' in data && data.results === true) {
|
||||
window.location.pathname = "/results/" + encodeURIComponent(playerId);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{ end -}}
|
||||
|
||||
22
ui/html/results.page.tmpl.html
Normal file
22
ui/html/results.page.tmpl.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{{- template "base" . -}}
|
||||
|
||||
{{- define "additional-css" -}}
|
||||
{{ end -}}
|
||||
|
||||
{{- define "additional-js" -}}
|
||||
{{ end -}}
|
||||
|
||||
{{- define "header" }}
|
||||
<h1>{{ .Session.Edges.Game.Name }} hrána {{ .Session.Started.Format "2006.01.02 15:04:05 MST" }}</h1>
|
||||
<h2>{{ if .Player.Organiser }}Organizátor{{ else }}Hráč{{ end }} {{ .Player.Name }}</h2>
|
||||
{{ end -}}
|
||||
|
||||
{{- define "main" }}
|
||||
<table>
|
||||
<tbody>
|
||||
{{ range .Results }}
|
||||
<tr><td>{{ .Place }}. místo</td><td>{{ .Player.Name }}</td><td>{{ .Points }}b</td></tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end -}}
|
||||
Reference in New Issue
Block a user