Display results

This commit is contained in:
Vojtěch Káně
2021-04-01 16:36:38 +02:00
parent de28e7ab8c
commit 688f87922d
6 changed files with 142 additions and 6 deletions

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -111,6 +111,10 @@
questionSection.appendChild(questionClone);
}
}
if ('results' in data && data.results === true) {
window.location.pathname = "/results/" + encodeURIComponent(playerId);
}
});
</script>
{{ end -}}

View 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 -}}