lives: Can display results as charts on all screens
This commit is contained in:
parent
5b2fddddc1
commit
e61a8bd51d
74
direct.go
74
direct.go
|
@ -2,8 +2,10 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -280,6 +282,62 @@ func getCorrectionString(qid int64) (ret map[string]int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getResponsesStats(qid int64) map[string]interface{} {
|
||||||
|
q, err := getQuestion(int(qid))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
responses, err := q.GetResponses()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Unable to retrieve responses:", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := []string{}
|
||||||
|
values := []uint{}
|
||||||
|
|
||||||
|
if q.Kind == "mcq" || q.Kind == "ucq" {
|
||||||
|
proposals, err := q.GetProposals()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
proposal_idx := map[string]int{}
|
||||||
|
for _, p := range proposals {
|
||||||
|
proposal_idx[fmt.Sprintf("%d", p.Id)] = len(labels)
|
||||||
|
labels = append(labels, p.Label)
|
||||||
|
values = append(values, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range responses {
|
||||||
|
for _, v := range strings.Split(r.Answer, ",") {
|
||||||
|
values[proposal_idx[v]]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats := map[string]uint{}
|
||||||
|
|
||||||
|
for _, r := range responses {
|
||||||
|
stats[r.Answer]++
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range stats {
|
||||||
|
labels = append(labels, k)
|
||||||
|
values = append(values, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{
|
||||||
|
"labels": labels,
|
||||||
|
"datasets": []map[string][]uint{
|
||||||
|
map[string][]uint{
|
||||||
|
"values": values,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SurveyWSAdmin(c *gin.Context) {
|
func SurveyWSAdmin(c *gin.Context) {
|
||||||
u := c.MustGet("LoggedUser").(*User)
|
u := c.MustGet("LoggedUser").(*User)
|
||||||
survey := c.MustGet("survey").(*Survey)
|
survey := c.MustGet("survey").(*Survey)
|
||||||
|
@ -342,6 +400,7 @@ func SurveyWSAdmin(c *gin.Context) {
|
||||||
|
|
||||||
// Save corrected state for the callback
|
// Save corrected state for the callback
|
||||||
corrected := v.Corrected
|
corrected := v.Corrected
|
||||||
|
with_stats := v.Stats != nil
|
||||||
|
|
||||||
surveyTimer = time.AfterFunc(time.Duration(OffsetQuestionTimer+v.Timer)*time.Millisecond, func() {
|
surveyTimer = time.AfterFunc(time.Duration(OffsetQuestionTimer+v.Timer)*time.Millisecond, func() {
|
||||||
surveyTimer = nil
|
surveyTimer = nil
|
||||||
|
@ -349,7 +408,12 @@ func SurveyWSAdmin(c *gin.Context) {
|
||||||
survey.Corrected = v.Corrected
|
survey.Corrected = v.Corrected
|
||||||
survey.Update()
|
survey.Update()
|
||||||
|
|
||||||
survey.WSWriteAll(WSMessage{Action: "new_question", QuestionId: v.QuestionId, Corrected: true, Corrections: getCorrectionString(*v.QuestionId)})
|
var stats map[string]interface{}
|
||||||
|
if with_stats {
|
||||||
|
stats = getResponsesStats(*v.QuestionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
survey.WSWriteAll(WSMessage{Action: "new_question", QuestionId: v.QuestionId, Corrected: true, Stats: stats, Corrections: getCorrectionString(*v.QuestionId)})
|
||||||
} else {
|
} else {
|
||||||
var z int64 = 0
|
var z int64 = 0
|
||||||
survey.Direct = &z
|
survey.Direct = &z
|
||||||
|
@ -360,10 +424,18 @@ func SurveyWSAdmin(c *gin.Context) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
v.Corrected = false
|
v.Corrected = false
|
||||||
|
v.Stats = nil
|
||||||
} else {
|
} else {
|
||||||
survey.Corrected = v.Corrected
|
survey.Corrected = v.Corrected
|
||||||
if v.Corrected {
|
if v.Corrected {
|
||||||
v.Corrections = getCorrectionString(*v.QuestionId)
|
v.Corrections = getCorrectionString(*v.QuestionId)
|
||||||
|
if v.Stats != nil {
|
||||||
|
v.Stats = getResponsesStats(*v.QuestionId)
|
||||||
|
} else {
|
||||||
|
v.Stats = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v.Stats = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = survey.Update()
|
_, err = survey.Update()
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
let responses = {};
|
let responses = {};
|
||||||
let corrected = false;
|
let corrected = false;
|
||||||
let next_corrected = false;
|
let next_corrected = false;
|
||||||
|
let with_stats = false;
|
||||||
let timer = 20;
|
let timer = 20;
|
||||||
let timer_end = null;
|
let timer_end = null;
|
||||||
let timer_remain = 0;
|
let timer_remain = 0;
|
||||||
|
@ -308,7 +309,6 @@
|
||||||
Réponses
|
Réponses
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Actions
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-primary"
|
class="btn btn-sm btn-primary"
|
||||||
|
@ -330,11 +330,13 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-info"
|
class="btn btn-sm"
|
||||||
on:click={() => { edit_question = new Question({ id_survey: survey.id }) } }
|
class:btn-outline-success={!with_stats}
|
||||||
title="Ajouter une question"
|
class:btn-success={with_stats}
|
||||||
|
on:click={() => { with_stats = !with_stats } }
|
||||||
|
title="La prochaine correction sera affichée avec les statistiques"
|
||||||
>
|
>
|
||||||
<i class="bi bi-plus"></i>
|
<i class="bi bi-bar-chart-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -344,6 +346,14 @@
|
||||||
>
|
>
|
||||||
<i class="bi bi-bandaid-fill"></i>
|
<i class="bi bi-bandaid-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-info mt-1"
|
||||||
|
on:click={() => { edit_question = new Question({ id_survey: survey.id }) } }
|
||||||
|
title="Ajouter une question"
|
||||||
|
>
|
||||||
|
<i class="bi bi-plus"></i>
|
||||||
|
</button>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -374,7 +384,7 @@
|
||||||
class:btn-primary={!next_corrected}
|
class:btn-primary={!next_corrected}
|
||||||
class:btn-success={next_corrected}
|
class:btn-success={next_corrected}
|
||||||
disabled={(question.id === current_question && next_corrected == corrected) || !ws_up}
|
disabled={(question.id === current_question && next_corrected == corrected) || !ws_up}
|
||||||
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": 0, "question":' + question.id + '}')} }
|
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + (with_stats?', "stats": {}':'') + ', "timer": 0, "question":' + question.id + '}')} }
|
||||||
>
|
>
|
||||||
<i class="bi bi-play-fill"></i>
|
<i class="bi bi-play-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
|
@ -382,7 +392,7 @@
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-danger"
|
class="btn btn-sm btn-danger"
|
||||||
disabled={question.id === current_question || !ws_up}
|
disabled={question.id === current_question || !ws_up}
|
||||||
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + ', "timer": ' + timer * 1000 + ',"question":' + question.id + '}')} }
|
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + next_corrected + (with_stats?', "stats": {}':'') + ', "timer": ' + timer * 1000 + ',"question":' + question.id + '}')} }
|
||||||
>
|
>
|
||||||
<i class="bi bi-stopwatch-fill"></i>
|
<i class="bi bi-stopwatch-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
|
|
||||||
import { user } from '$lib/stores/user';
|
import { user } from '$lib/stores/user';
|
||||||
import { ToastsStore } from '$lib/stores/toasts';
|
import { ToastsStore } from '$lib/stores/toasts';
|
||||||
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
|
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
|
||||||
import QuestionForm from '$lib/components/QuestionForm.svelte';
|
import QuestionForm from '$lib/components/QuestionForm.svelte';
|
||||||
|
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
|
||||||
import { getQuestion } from '$lib/questions';
|
import { getQuestion } from '$lib/questions';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
@ -88,7 +89,12 @@
|
||||||
if (data.action && data.action == "new_question") {
|
if (data.action && data.action == "new_question") {
|
||||||
show_question = data.question;
|
show_question = data.question;
|
||||||
survey.corrected = data.corrected;
|
survey.corrected = data.corrected;
|
||||||
if (data.corrected) {
|
if (data.stats) {
|
||||||
|
stats = data.stats;
|
||||||
|
} else {
|
||||||
|
stats = null;
|
||||||
|
}
|
||||||
|
if(data.corrected) {
|
||||||
corrections = data.corrections;
|
corrections = data.corrections;
|
||||||
} else {
|
} else {
|
||||||
corrections = null;
|
corrections = null;
|
||||||
|
@ -168,6 +174,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let corrections = null;
|
let corrections = null;
|
||||||
|
let stats = null;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -207,6 +214,13 @@
|
||||||
<span>Chargement d'une nouvelle question …</span>
|
<span>Chargement d'une nouvelle question …</span>
|
||||||
</div>
|
</div>
|
||||||
{:then question}
|
{:then question}
|
||||||
|
{#if stats != null}
|
||||||
|
<CorrectionPieChart
|
||||||
|
{question}
|
||||||
|
proposals={true}
|
||||||
|
data={stats}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
<QuestionForm
|
<QuestionForm
|
||||||
{survey}
|
{survey}
|
||||||
{question}
|
{question}
|
||||||
|
@ -221,7 +235,7 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</QuestionForm>
|
</QuestionForm>
|
||||||
{#if question.kind != 'mcq' && question.kind != 'ucq' && question.kind != 'none'}
|
{#if !survey.corrected && question.kind != 'mcq' && question.kind != 'ucq' && question.kind != 'none'}
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
>
|
>
|
||||||
|
|
Reference in New Issue