Compare commits
No commits in common. "e0cd502c352a2fe5e7f5f016cc2cc5e343793af9" and "fdceb2547ca9df521f35db2cb6fbe92163c64dad" have entirely different histories.
e0cd502c35
...
fdceb2547c
68
direct.go
68
direct.go
@ -98,16 +98,9 @@ func msgCurrentState(survey *Survey) (msg WSMessage) {
|
|||||||
Action: "pause",
|
Action: "pause",
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var correction map[string]int
|
|
||||||
if survey.Corrected {
|
|
||||||
correction = getCorrectionString(*survey.Direct)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = WSMessage{
|
msg = WSMessage{
|
||||||
Action: "new_question",
|
Action: "new_question",
|
||||||
QuestionId: survey.Direct,
|
QuestionId: survey.Direct,
|
||||||
Corrected: survey.Corrected,
|
|
||||||
Corrections: correction,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -153,15 +146,13 @@ func WSWriteAll(message WSMessage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WSMessage struct {
|
type WSMessage struct {
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
SurveyId *int64 `json:"survey,omitempty"`
|
SurveyId *int64 `json:"survey,omitempty"`
|
||||||
QuestionId *int64 `json:"question,omitempty"`
|
QuestionId *int64 `json:"question,omitempty"`
|
||||||
Stats map[string]interface{} `json:"stats,omitempty"`
|
Stats map[string]interface{} `json:"stats,omitempty"`
|
||||||
UserId *int64 `json:"user,omitempty"`
|
UserId *int64 `json:"user,omitempty"`
|
||||||
Response string `json:"value,omitempty"`
|
Response string `json:"value,omitempty"`
|
||||||
Corrected bool `json:"corrected,omitempty"`
|
Timer uint `json:"timer,omitempty"`
|
||||||
Corrections map[string]int `json:"corrections,omitempty"`
|
|
||||||
Timer uint `json:"timer,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Survey) WSWriteAll(message WSMessage) {
|
func (s *Survey) WSWriteAll(message WSMessage) {
|
||||||
@ -238,25 +229,6 @@ loopadmin:
|
|||||||
log.Println(u.Login, "admin disconnected")
|
log.Println(u.Login, "admin disconnected")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCorrectionString(qid int64) (ret map[string]int) {
|
|
||||||
q, err := getQuestion(int(qid))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cts, err := q.GetCorrectionTemplates()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = map[string]int{}
|
|
||||||
for _, ct := range cts {
|
|
||||||
ret[ct.RegExp] = ct.Score
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
@ -302,29 +274,15 @@ func SurveyWSAdmin(c *gin.Context) {
|
|||||||
if *survey.Direct != 0 {
|
if *survey.Direct != 0 {
|
||||||
var z int64 = 0
|
var z int64 = 0
|
||||||
survey.Direct = &z
|
survey.Direct = &z
|
||||||
survey.Corrected = false
|
|
||||||
survey.Update()
|
survey.Update()
|
||||||
}
|
}
|
||||||
go func(corrected bool) {
|
go func() {
|
||||||
time.Sleep(time.Duration(OffsetQuestionTimer+v.Timer) * time.Millisecond)
|
time.Sleep(time.Duration(OffsetQuestionTimer+v.Timer) * time.Millisecond)
|
||||||
|
survey.WSWriteAll(WSMessage{Action: "pause"})
|
||||||
if corrected {
|
WSAdminWriteAll(WSMessage{Action: "pause", SurveyId: &survey.Id})
|
||||||
survey.Corrected = v.Corrected
|
}()
|
||||||
survey.Update()
|
|
||||||
|
|
||||||
survey.WSWriteAll(WSMessage{Action: "new_question", QuestionId: v.QuestionId, Corrected: true, Corrections: getCorrectionString(*v.QuestionId)})
|
|
||||||
} else {
|
|
||||||
survey.WSWriteAll(WSMessage{Action: "pause"})
|
|
||||||
WSAdminWriteAll(WSMessage{Action: "pause", SurveyId: &survey.Id})
|
|
||||||
}
|
|
||||||
}(v.Corrected)
|
|
||||||
v.Corrected = false
|
|
||||||
} else {
|
} else {
|
||||||
survey.Direct = v.QuestionId
|
survey.Direct = v.QuestionId
|
||||||
survey.Corrected = v.Corrected
|
|
||||||
if v.Corrected {
|
|
||||||
v.Corrections = getCorrectionString(*v.QuestionId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_, err = survey.Update()
|
_, err = survey.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
50
help.go
50
help.go
@ -1,10 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -12,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func declareAPIAdminHelpRoutes(router *gin.RouterGroup) {
|
func declareAPIAdminHelpRoutes(router *gin.RouterGroup) {
|
||||||
router.GET("/help", func(c *gin.Context) {
|
router.GET("/help", func(c *gin.Context) {
|
||||||
nhs, err := getNeedHelps("WHERE date_treated IS NULL")
|
nhs, err := getNeedHelps()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Unable to getNeedHelps:", err)
|
log.Println("Unable to getNeedHelps:", err)
|
||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during need helps retrieval. Please retry."})
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during need helps retrieval. Please retry."})
|
||||||
@ -21,29 +19,6 @@ func declareAPIAdminHelpRoutes(router *gin.RouterGroup) {
|
|||||||
|
|
||||||
c.JSON(http.StatusOK, nhs)
|
c.JSON(http.StatusOK, nhs)
|
||||||
})
|
})
|
||||||
|
|
||||||
needhelpsRoutes := router.Group("/help/:hid")
|
|
||||||
needhelpsRoutes.Use(needHelpHandler)
|
|
||||||
|
|
||||||
needhelpsRoutes.PUT("", func(c *gin.Context) {
|
|
||||||
current := c.MustGet("needhelp").(*NeedHelp)
|
|
||||||
|
|
||||||
var new NeedHelp
|
|
||||||
if err := c.ShouldBindJSON(&new); err != nil {
|
|
||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
new.Id = current.Id
|
|
||||||
|
|
||||||
if err := new.Update(); err != nil {
|
|
||||||
log.Println("Unable to Update needhelp:", err)
|
|
||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Sprintf("An error occurs during needhelp entry updation: %s", err.Error())})
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusOK, new)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func declareAPIAuthHelpRoutes(router *gin.RouterGroup) {
|
func declareAPIAuthHelpRoutes(router *gin.RouterGroup) {
|
||||||
@ -61,19 +36,6 @@ func declareAPIAuthHelpRoutes(router *gin.RouterGroup) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func needHelpHandler(c *gin.Context) {
|
|
||||||
if hid, err := strconv.Atoi(string(c.Param("hid"))); err != nil {
|
|
||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Bad need help identifier."})
|
|
||||||
return
|
|
||||||
} else if nh, err := getNeedHelp(hid); err != nil {
|
|
||||||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Need help entry not found."})
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
c.Set("needhelp", nh)
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type NeedHelp struct {
|
type NeedHelp struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
IdUser int64 `json:"id_user"`
|
IdUser int64 `json:"id_user"`
|
||||||
@ -82,8 +44,8 @@ type NeedHelp struct {
|
|||||||
DateTreated *time.Time `json:"treated,omitempty"`
|
DateTreated *time.Time `json:"treated,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNeedHelps(cond string) (nh []NeedHelp, err error) {
|
func getNeedHelps() (nh []NeedHelp, err error) {
|
||||||
if rows, errr := DBQuery("SELECT id_need_help, id_user, date, comment, date_treated FROM user_need_help " + cond); errr != nil {
|
if rows, errr := DBQuery("SELECT id_need_help, id_user, date, comment, date_treated FROM user_need_help"); errr != nil {
|
||||||
return nil, errr
|
return nil, errr
|
||||||
} else {
|
} else {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
@ -103,12 +65,6 @@ func getNeedHelps(cond string) (nh []NeedHelp, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNeedHelp(id int) (n *NeedHelp, err error) {
|
|
||||||
n = new(NeedHelp)
|
|
||||||
err = DBQueryRow("SELECT id_need_help, id_user, date, comment, date_treated FROM user_need_help WHERE id_need_help=?", id).Scan(&n.Id, &n.IdUser, &n.Date, &n.Comment, &n.DateTreated)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) NewNeedHelp() (NeedHelp, error) {
|
func (u *User) NewNeedHelp() (NeedHelp, error) {
|
||||||
if res, err := DBExec("INSERT INTO user_need_help (id_user, comment) VALUES (?, ?)", u.Id, ""); err != nil {
|
if res, err := DBExec("INSERT INTO user_need_help (id_user, comment) VALUES (?, ?)", u.Id, ""); err != nil {
|
||||||
return NeedHelp{}, err
|
return NeedHelp{}, err
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -29,7 +27,7 @@ func declareAPIAuthResponsesRoutes(router *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var responses []Response
|
var responses []Response
|
||||||
if err := c.ShouldBindJSON(&responses); err != nil {
|
if err := c.ShouldBindJSON(responses); err != nil {
|
||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -47,7 +45,7 @@ func declareAPIAuthResponsesRoutes(router *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, response := range responses {
|
for _, response := range responses {
|
||||||
if !uauth.IsAdmin && !s.Shown && (s.Corrected || s.Direct == nil || *s.Direct != response.IdQuestion) {
|
if !uauth.IsAdmin && !s.Shown && (s.Direct == nil || *s.Direct != response.IdQuestion) {
|
||||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Cette question n'est pas disponible"})
|
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Cette question n'est pas disponible"})
|
||||||
return
|
return
|
||||||
} else if len(response.Answer) > 0 {
|
} else if len(response.Answer) > 0 {
|
||||||
@ -144,7 +142,7 @@ func declareAPIAuthQuestionResponsesRoutes(router *gin.RouterGroup) {
|
|||||||
q := c.MustGet("question").(*Question)
|
q := c.MustGet("question").(*Question)
|
||||||
|
|
||||||
res, err := q.GetMyResponse(u, false)
|
res, err := q.GetMyResponse(u, false)
|
||||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
if err != nil {
|
||||||
log.Printf("Unable to GetMyResponse(uid=%d;qid=%d;false): %s", u.Id, q.Id, err.Error())
|
log.Printf("Unable to GetMyResponse(uid=%d;qid=%d;false): %s", u.Id, q.Id, err.Error())
|
||||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during response retrieval."})
|
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during response retrieval."})
|
||||||
return
|
return
|
||||||
|
@ -31,41 +31,34 @@
|
|||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
let req_proposals = null;
|
let req_proposals = null;
|
||||||
export let proposals = null;
|
|
||||||
let req_responses = null;
|
let req_responses = null;
|
||||||
let mean = null;
|
|
||||||
|
|
||||||
export let data = {
|
if (question.kind == "int") {
|
||||||
|
req_responses = question.getResponses();
|
||||||
|
req_responses.then((responses) => {
|
||||||
|
const proposal_idx = { };
|
||||||
|
for (const res of responses) {
|
||||||
|
if (proposal_idx[res.value]) {
|
||||||
|
data.datasets[0].values[proposal_idx[res.value]] += 1;
|
||||||
|
} else {
|
||||||
|
data.labels.push(res.value);
|
||||||
|
data.datasets[0].values.push(1);
|
||||||
|
proposal_idx[res.value] = new String(data.labels.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
req_proposals = refreshProposals();
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
values: []
|
values: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!proposals) {
|
|
||||||
if (question.kind == "int") {
|
|
||||||
req_responses = question.getResponses();
|
|
||||||
req_responses.then((responses) => {
|
|
||||||
const values = [];
|
|
||||||
const proposal_idx = { };
|
|
||||||
for (const res of responses) {
|
|
||||||
if (proposal_idx[res.value]) {
|
|
||||||
data.datasets[0].values[proposal_idx[res.value]] += 1;
|
|
||||||
values.push(Number(res.value));
|
|
||||||
} else {
|
|
||||||
data.labels.push(res.value);
|
|
||||||
data.datasets[0].values.push(1);
|
|
||||||
proposal_idx[res.value] = new String(data.labels.length - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mean = Math.trunc(values.reduce((p, e) => p + e) / values.length*10)/10;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
req_proposals = refreshProposals();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="{className}">
|
<div class="{className}">
|
||||||
@ -81,16 +74,7 @@
|
|||||||
<span>Récupération des réponses…</span>
|
<span>Récupération des réponses…</span>
|
||||||
</div>
|
</div>
|
||||||
{:then}
|
{:then}
|
||||||
{#if mean !== null}
|
<Chart data={data} type="pie" maxSlices="9" />
|
||||||
<div class="text-center">
|
|
||||||
Moyenne représentative : <strong>{mean}</strong>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if question.kind === "mcq"}
|
|
||||||
<Chart data={data} type="bar" />
|
|
||||||
{:else}
|
|
||||||
<Chart data={data} type="pie" maxSlices="9" />
|
|
||||||
{/if}
|
|
||||||
{/await}
|
{/await}
|
||||||
{/await}
|
{/await}
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,35 +19,6 @@
|
|||||||
templates = templates;
|
templates = templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
function genTemplates() {
|
|
||||||
question.getProposals().then((proposals) => {
|
|
||||||
let i = 0;
|
|
||||||
for (const p of proposals) {
|
|
||||||
// Search proposal in templates
|
|
||||||
let found = false;
|
|
||||||
for (const tpl of templates) {
|
|
||||||
if (tpl.regexp.indexOf(p.id.toString()) !== -1) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
const ct = new CorrectionTemplate()
|
|
||||||
ct.id_question = question.id;
|
|
||||||
ct.regexp = p.id.toString();
|
|
||||||
ct.label = String.fromCharCode(97 + i);
|
|
||||||
ct.save().then((ct) => {
|
|
||||||
templates.push(ct);
|
|
||||||
templates = templates;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function delTemplate(tpl) {
|
function delTemplate(tpl) {
|
||||||
tpl.delete().then(() => {
|
tpl.delete().then(() => {
|
||||||
const idx = templates.findIndex((e) => e.id === tpl.id);
|
const idx = templates.findIndex((e) => e.id === tpl.id);
|
||||||
@ -138,13 +109,4 @@
|
|||||||
>
|
>
|
||||||
<i class="bi bi-plus"></i> Ajouter un template
|
<i class="bi bi-plus"></i> Ajouter un template
|
||||||
</button>
|
</button>
|
||||||
{#if question.kind == "mcq" || question.kind == "ucq"}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-outline-info me-1"
|
|
||||||
on:click={genTemplates}
|
|
||||||
>
|
|
||||||
<i class="bi bi-magic"></i> Générer les templates
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
export let qid;
|
export let qid;
|
||||||
export let response_history = null;
|
export let response_history = null;
|
||||||
export let readonly = false;
|
export let readonly = false;
|
||||||
export let corrections = {};
|
|
||||||
export let survey = null;
|
export let survey = null;
|
||||||
export let value = "";
|
export let value = "";
|
||||||
|
|
||||||
@ -92,8 +91,6 @@
|
|||||||
kind={question.kind}
|
kind={question.kind}
|
||||||
{proposals}
|
{proposals}
|
||||||
readonly
|
readonly
|
||||||
live={survey.direct !== null}
|
|
||||||
{corrections}
|
|
||||||
bind:value={value}
|
bind:value={value}
|
||||||
on:change={() => { dispatch("change"); }}
|
on:change={() => { dispatch("change"); }}
|
||||||
/>
|
/>
|
||||||
@ -111,8 +108,6 @@
|
|||||||
kind={question.kind}
|
kind={question.kind}
|
||||||
{proposals}
|
{proposals}
|
||||||
{readonly}
|
{readonly}
|
||||||
live={survey.direct !== null}
|
|
||||||
{corrections}
|
|
||||||
bind:value={value}
|
bind:value={value}
|
||||||
on:change={() => { dispatch("change"); }}
|
on:change={() => { dispatch("change"); }}
|
||||||
/>
|
/>
|
||||||
@ -137,7 +132,7 @@
|
|||||||
></textarea>
|
></textarea>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if survey && survey.corrected && response_history}
|
{#if survey && survey.corrected}
|
||||||
<ResponseCorrected
|
<ResponseCorrected
|
||||||
response={response_history}
|
response={response_history}
|
||||||
{survey}
|
{survey}
|
||||||
|
@ -5,11 +5,9 @@
|
|||||||
|
|
||||||
export let edit = false;
|
export let edit = false;
|
||||||
export let proposals = [];
|
export let proposals = [];
|
||||||
export let live = false;
|
|
||||||
export let kind = 'mcq';
|
export let kind = 'mcq';
|
||||||
export let prefixid = '';
|
export let prefixid = '';
|
||||||
export let readonly = false;
|
export let readonly = false;
|
||||||
export let corrections = {};
|
|
||||||
export let id_question = 0;
|
export let id_question = 0;
|
||||||
export let value;
|
export let value;
|
||||||
|
|
||||||
@ -30,14 +28,12 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class:d-flex={live} class:justify-content-around={live}>
|
|
||||||
{#each proposals as proposal, pid (proposal.id)}
|
{#each proposals as proposal, pid (proposal.id)}
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
{#if kind == 'mcq'}
|
{#if kind == 'mcq'}
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class:btn-check={live}
|
class="form-check-input"
|
||||||
class:form-check-input={!live}
|
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
name={prefixid + 'proposal' + proposal.id_question}
|
name={prefixid + 'proposal' + proposal.id_question}
|
||||||
id={prefixid + 'p' + proposal.id}
|
id={prefixid + 'p' + proposal.id}
|
||||||
@ -48,8 +44,7 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
class:btn-check={live}
|
class="form-check-input"
|
||||||
class:form-check-input={!live}
|
|
||||||
disabled={readonly}
|
disabled={readonly}
|
||||||
name={prefixid + 'proposal' + proposal.id_question}
|
name={prefixid + 'proposal' + proposal.id_question}
|
||||||
id={prefixid + 'p' + proposal.id}
|
id={prefixid + 'p' + proposal.id}
|
||||||
@ -89,14 +84,7 @@
|
|||||||
</form>
|
</form>
|
||||||
{:else}
|
{:else}
|
||||||
<label
|
<label
|
||||||
class:form-check-label={!live}
|
class="form-check-label"
|
||||||
class:btn={live}
|
|
||||||
class:btn-lg={live}
|
|
||||||
class:btn-primary={live && !corrections && value.indexOf(proposal.id.toString()) != -1}
|
|
||||||
class:btn-outline-primary={live && !corrections && value.indexOf(proposal.id.toString()) == -1}
|
|
||||||
class:btn-success={live && corrections && corrections[proposal.id] == 0}
|
|
||||||
class:btn-outline-warning={live && corrections && corrections[proposal.id] != 0 && corrections[proposal.id] != -100}
|
|
||||||
class:btn-outline-danger={live && corrections && corrections[proposal.id] == -100}
|
|
||||||
for={prefixid + 'p' + proposal.id}
|
for={prefixid + 'p' + proposal.id}
|
||||||
>
|
>
|
||||||
{proposal.label}
|
{proposal.label}
|
||||||
@ -104,7 +92,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
|
||||||
{#if edit}
|
{#if edit}
|
||||||
{#if kind == 'mcq'}
|
{#if kind == 'mcq'}
|
||||||
<input
|
<input
|
||||||
|
@ -51,51 +51,10 @@ export async function getUserScore(uid, survey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserNeedingHelp {
|
|
||||||
constructor(res) {
|
|
||||||
if (res) {
|
|
||||||
this.update(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update({ id, id_user, date, comment, treated }) {
|
|
||||||
this.id = id;
|
|
||||||
this.id_user = id_user;
|
|
||||||
this.date = new Date(date);
|
|
||||||
this.comment = comment;
|
|
||||||
if (treated) {
|
|
||||||
this.treated = new Date(treated);
|
|
||||||
} else {
|
|
||||||
this.treated = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mark_treated() {
|
|
||||||
this.treated = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
async save() {
|
|
||||||
const res = await fetch(this.id?`api/help/${this.id}`:'api/help', {
|
|
||||||
method: this.id?'PUT':'POST',
|
|
||||||
headers: {'Accept': 'application/json'},
|
|
||||||
body: JSON.stringify(this),
|
|
||||||
});
|
|
||||||
if (res.status == 200) {
|
|
||||||
const data = await res.json()
|
|
||||||
this.update(data);
|
|
||||||
return data;
|
|
||||||
} else {
|
|
||||||
throw new Error((await res.json()).errmsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getUserNeedingHelp() {
|
export async function getUserNeedingHelp() {
|
||||||
const res = await fetch(`api/help`, {headers: {'Accept': 'application/json'}})
|
const res = await fetch(`api/help`, {headers: {'Accept': 'application/json'}})
|
||||||
if (res.status == 200) {
|
if (res.status == 200) {
|
||||||
return (await res.json()).map((nh) => {
|
return await res.json();
|
||||||
return new UserNeedingHelp(nh)
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error((await res.json()).errmsg);
|
throw new Error((await res.json()).errmsg);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { user } from '../stores/user';
|
import { user } from '../stores/user';
|
||||||
import { getUser, getUserNeedingHelp } from '../lib/users';
|
import { getUser, getUserNeedingHelp } from '../lib/users';
|
||||||
import DateFormat from '../components/DateFormat.svelte';
|
|
||||||
import SurveyList from '../components/SurveyList.svelte';
|
import SurveyList from '../components/SurveyList.svelte';
|
||||||
import ValidateSubmissions from '../components/ValidateSubmissions.svelte';
|
import ValidateSubmissions from '../components/ValidateSubmissions.svelte';
|
||||||
|
|
||||||
let direct = null;
|
let direct = null;
|
||||||
|
|
||||||
let users_needing_help = [];
|
|
||||||
$: if ($user && $user.is_admin) {
|
|
||||||
users_needing_help = getUserNeedingHelp();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mark_needhelp_treated(unh) {
|
|
||||||
unh.mark_treated();
|
|
||||||
|
|
||||||
unh.save().then(() => {
|
|
||||||
users_needing_help = getUserNeedingHelp();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="card bg-light">
|
<div class="card bg-light">
|
||||||
@ -43,7 +29,7 @@
|
|||||||
|
|
||||||
{#if $user.is_admin}
|
{#if $user.is_admin}
|
||||||
<p class="lead">Demande d'aide :</p>
|
<p class="lead">Demande d'aide :</p>
|
||||||
{#await users_needing_help}
|
{#await getUserNeedingHelp()}
|
||||||
<span class="spinner-border spinner-border" role="status" aria-hidden="true"></span>
|
<span class="spinner-border spinner-border" role="status" aria-hidden="true"></span>
|
||||||
{:then nhs}
|
{:then nhs}
|
||||||
<ul style="columns: 2">
|
<ul style="columns: 2">
|
||||||
@ -54,14 +40,7 @@
|
|||||||
{:then u}
|
{:then u}
|
||||||
<a href="users/{u.id}">{u.login}</a>
|
<a href="users/{u.id}">{u.login}</a>
|
||||||
{/await}
|
{/await}
|
||||||
(<DateFormat date={user.date} dateStyle="medium" timeStyle="medium" />)
|
({user.date})
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-info"
|
|
||||||
on:click={e => {mark_needhelp_treated(user)}}
|
|
||||||
>
|
|
||||||
<i class="bi bi-check" title="Marquer la demande d'aide comme traîtée"></i>
|
|
||||||
</button>
|
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { user } from '../../../stores/user';
|
import { user } from '../../../stores/user';
|
||||||
import CorrectionPieChart from '../../../components/CorrectionPieChart.svelte';
|
|
||||||
import SurveyAdmin from '../../../components/SurveyAdmin.svelte';
|
import SurveyAdmin from '../../../components/SurveyAdmin.svelte';
|
||||||
import SurveyBadge from '../../../components/SurveyBadge.svelte';
|
import SurveyBadge from '../../../components/SurveyBadge.svelte';
|
||||||
import { getSurvey } from '../../../lib/surveys';
|
import { getSurvey } from '../../../lib/surveys';
|
||||||
@ -44,8 +43,7 @@
|
|||||||
let wsstats = null;
|
let wsstats = null;
|
||||||
let current_question = null;
|
let current_question = null;
|
||||||
let responses = {};
|
let responses = {};
|
||||||
let corrected = false;
|
let timer = 20000;
|
||||||
let timer = 20;
|
|
||||||
let timer_end = null;
|
let timer_end = null;
|
||||||
let timer_remain = 0;
|
let timer_remain = 0;
|
||||||
let timer_cancel = null;
|
let timer_cancel = null;
|
||||||
@ -85,52 +83,6 @@
|
|||||||
responsesbyid = tmp;
|
responsesbyid = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph_data = {labels:[]};
|
|
||||||
async function reset_graph_data(questionid) {
|
|
||||||
if (questionid) {
|
|
||||||
const labels = [];
|
|
||||||
const flabels = [];
|
|
||||||
|
|
||||||
let question = null;
|
|
||||||
for (const q of await req_questions) {
|
|
||||||
if (q.id == current_question) {
|
|
||||||
question = q;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (question) {
|
|
||||||
for (const p of await question.getProposals()) {
|
|
||||||
flabels.push(p.id.toString());
|
|
||||||
labels.push(p.label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
graph_data = {
|
|
||||||
labels,
|
|
||||||
flabels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
values: labels.map(() => 0)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current_question && responses[current_question] && graph_data.labels.length != 0) {
|
|
||||||
const values = graph_data.datasets[0].values.map(() => 0);
|
|
||||||
|
|
||||||
for (const u in responses[current_question]) {
|
|
||||||
const res = responses[current_question][u];
|
|
||||||
for (const r of res.split(',')) {
|
|
||||||
let idx = graph_data.flabels.indexOf(r);
|
|
||||||
values[idx] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
graph_data.datasets[0].values = values;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let asks = [];
|
let asks = [];
|
||||||
function wsconnect() {
|
function wsconnect() {
|
||||||
if (ws !== null) return;
|
if (ws !== null) return;
|
||||||
@ -146,7 +98,7 @@
|
|||||||
|
|
||||||
ws.addEventListener("close", (e) => {
|
ws.addEventListener("close", (e) => {
|
||||||
ws_up = false;
|
ws_up = false;
|
||||||
console.log('Socket is closed. Reconnect will be attempted in 1 second.');
|
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e);
|
||||||
ws = null;
|
ws = null;
|
||||||
updateSurvey();
|
updateSurvey();
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
@ -154,6 +106,10 @@
|
|||||||
}, 1500);
|
}, 1500);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ws.onerror((evt) => {
|
||||||
|
console.log('onerror', evt)
|
||||||
|
})
|
||||||
|
|
||||||
ws.addEventListener("error", (err) => {
|
ws.addEventListener("error", (err) => {
|
||||||
ws_up = false;
|
ws_up = false;
|
||||||
console.log('Socket closed due to error.', err);
|
console.log('Socket closed due to error.', err);
|
||||||
@ -162,6 +118,7 @@
|
|||||||
|
|
||||||
ws.addEventListener("message", (message) => {
|
ws.addEventListener("message", (message) => {
|
||||||
const data = JSON.parse(message.data);
|
const data = JSON.parse(message.data);
|
||||||
|
console.log(data);
|
||||||
if (data.action && data.action == "new_question") {
|
if (data.action && data.action == "new_question") {
|
||||||
current_question = data.question;
|
current_question = data.question;
|
||||||
if (timer_cancel) {
|
if (timer_cancel) {
|
||||||
@ -174,14 +131,11 @@
|
|||||||
} else {
|
} else {
|
||||||
timer_end = null;
|
timer_end = null;
|
||||||
}
|
}
|
||||||
reset_graph_data(data.question);
|
|
||||||
} else if (data.action && data.action == "stats") {
|
} else if (data.action && data.action == "stats") {
|
||||||
wsstats = data.stats;
|
wsstats = data.stats;
|
||||||
} else if (data.action && data.action == "new_response") {
|
} else if (data.action && data.action == "new_response") {
|
||||||
if (!responses[data.question]) responses[data.question] = { };
|
if (!responses[data.question]) responses[data.question] = {};
|
||||||
responses[data.question][data.user] = data.value;
|
responses[data.question][data.user] = data.value;
|
||||||
|
|
||||||
reset_graph_data();
|
|
||||||
} else if (data.action && data.action == "new_ask") {
|
} else if (data.action && data.action == "new_ask") {
|
||||||
asks.push({"id": data.question, "content": data.value, "userid": data.user});
|
asks.push({"id": data.question, "content": data.value, "userid": data.user});
|
||||||
asks = asks;
|
asks = asks;
|
||||||
@ -267,7 +221,7 @@
|
|||||||
disabled
|
disabled
|
||||||
value={timer_remain}
|
value={timer_remain}
|
||||||
>
|
>
|
||||||
<span class="input-group-text">s</span>
|
<span class="input-group-text">ms</span>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
|
<div class="input-group input-group-sm float-end" style="max-width: 150px;">
|
||||||
@ -277,7 +231,7 @@
|
|||||||
bind:value={timer}
|
bind:value={timer}
|
||||||
placeholder="Valeur du timer"
|
placeholder="Valeur du timer"
|
||||||
>
|
>
|
||||||
<span class="input-group-text">s</span>
|
<span class="input-group-text">ms</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
<button
|
||||||
@ -299,20 +253,9 @@
|
|||||||
class="btn btn-sm btn-primary"
|
class="btn btn-sm btn-primary"
|
||||||
disabled={!current_question || !ws_up}
|
disabled={!current_question || !ws_up}
|
||||||
on:click={() => { ws.send('{"action":"pause"}')} }
|
on:click={() => { ws.send('{"action":"pause"}')} }
|
||||||
title="Passer sur une scène sans question"
|
|
||||||
>
|
>
|
||||||
<i class="bi bi-pause-fill"></i>
|
<i class="bi bi-pause-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm"
|
|
||||||
class:btn-outline-success={!corrected}
|
|
||||||
class:btn-success={corrected}
|
|
||||||
on:click={() => { corrected = !corrected } }
|
|
||||||
title="La prochaine question est affichée corrigée"
|
|
||||||
>
|
|
||||||
<i class="bi bi-eye"></i>
|
|
||||||
</button>
|
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -339,11 +282,9 @@
|
|||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm"
|
class="btn btn-sm btn-primary"
|
||||||
class:btn-primary={!corrected}
|
|
||||||
class:btn-success={corrected}
|
|
||||||
disabled={question.id === current_question || !ws_up}
|
disabled={question.id === current_question || !ws_up}
|
||||||
on:click={() => { ws.send('{"action":"new_question", "corrected": ' + corrected + ', "timer": 0, "question":' + question.id + '}')} }
|
on:click={() => { ws.send('{"action":"new_question", "timer": 0, "question":' + question.id + '}')} }
|
||||||
>
|
>
|
||||||
<i class="bi bi-play-fill"></i>
|
<i class="bi bi-play-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -351,18 +292,10 @@
|
|||||||
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": ' + corrected + ', "timer": ' + timer * 1000 + ',"question":' + question.id + '}')} }
|
on:click={() => { ws.send('{"action":"new_question", "timer": ' + timer + ',"question":' + question.id + '}')} }
|
||||||
>
|
>
|
||||||
<i class="bi bi-stopwatch-fill"></i>
|
<i class="bi bi-stopwatch-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
<a
|
|
||||||
href="/surveys/{survey.id}/responses/{question.id}"
|
|
||||||
target="_blank"
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-success"
|
|
||||||
>
|
|
||||||
<i class="bi bi-files"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
@ -468,17 +401,6 @@
|
|||||||
<span>Chargement des propositions …</span>
|
<span>Chargement des propositions …</span>
|
||||||
</div>
|
</div>
|
||||||
{:then proposals}
|
{:then proposals}
|
||||||
{#if current_question == question.id}
|
|
||||||
<CorrectionPieChart
|
|
||||||
{question}
|
|
||||||
{proposals}
|
|
||||||
data={graph_data}
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<CorrectionPieChart
|
|
||||||
{question}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<table class="table table-sm table-striped table-hover mb-0">
|
<table class="table table-sm table-striped table-hover mb-0">
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -506,17 +428,6 @@
|
|||||||
<span>Chargement des propositions …</span>
|
<span>Chargement des propositions …</span>
|
||||||
</div>
|
</div>
|
||||||
{:then proposals}
|
{:then proposals}
|
||||||
{#if current_question == question.id}
|
|
||||||
<CorrectionPieChart
|
|
||||||
{question}
|
|
||||||
{proposals}
|
|
||||||
data={graph_data}
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<CorrectionPieChart
|
|
||||||
{question}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<table class="table table-sm table-striped table-hover mb-0">
|
<table class="table table-sm table-striped table-hover mb-0">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -89,8 +89,6 @@
|
|||||||
console.log(data);
|
console.log(data);
|
||||||
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;
|
|
||||||
corrections = data.corrections;
|
|
||||||
if (timer_cancel) {
|
if (timer_cancel) {
|
||||||
clearInterval(timer_cancel);
|
clearInterval(timer_cancel);
|
||||||
timer_cancel = null;
|
timer_cancel = null;
|
||||||
@ -157,11 +155,9 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let corrections = {};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#await surveyP then unused}
|
{#await surveyP then survey}
|
||||||
{#if $user && $user.is_admin}
|
{#if $user && $user.is_admin}
|
||||||
<a href="surveys/{survey.id}/admin" class="btn btn-primary ms-1 float-end" title="Aller à l'interface d'administration"><i class="bi bi-pencil"></i></a>
|
<a href="surveys/{survey.id}/admin" class="btn btn-primary ms-1 float-end" title="Aller à l'interface d'administration"><i class="bi bi-pencil"></i></a>
|
||||||
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
|
||||||
@ -189,10 +185,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{:then question}
|
{:then question}
|
||||||
<QuestionForm
|
<QuestionForm
|
||||||
{survey}
|
|
||||||
{question}
|
{question}
|
||||||
readonly={timer >= 100 || survey.corrected}
|
readonly={timer >= 100}
|
||||||
{corrections}
|
|
||||||
bind:value={value}
|
bind:value={value}
|
||||||
on:change={sendValue}
|
on:change={sendValue}
|
||||||
>
|
>
|
||||||
@ -206,7 +200,7 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
>
|
>
|
||||||
Soumettre cette réponse
|
Soumettre la réponse
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
{/await}
|
||||||
@ -232,7 +226,7 @@
|
|||||||
class="form-control"
|
class="form-control"
|
||||||
bind:value={myQuestion}
|
bind:value={myQuestion}
|
||||||
autofocus
|
autofocus
|
||||||
placeholder="Remarques, soucis, choses pas claires ? Levez la main ou écrivez ici !"
|
placeholder="Remarques, soucis, choses pas claires ? Demandez !"
|
||||||
></textarea>
|
></textarea>
|
||||||
<button
|
<button
|
||||||
class="d-sm-none btn btn-primary"
|
class="d-sm-none btn btn-primary"
|
||||||
|
Reference in New Issue
Block a user