Pierre-Olivier Mercier
140935e15d
All checks were successful
continuous-integration/drone/push Build is passing
138 lines
5.1 KiB
Svelte
138 lines
5.1 KiB
Svelte
<script>
|
|
import { createEventDispatcher } from 'svelte';
|
|
|
|
import QuestionProposals from './QuestionProposals.svelte';
|
|
import ResponseCorrected from './ResponseCorrected.svelte';
|
|
import CorrectionResponseFooter from './CorrectionResponseFooter.svelte';
|
|
import { autoCorrection } from '../lib/correctionTemplates';
|
|
import { getUser } from '../lib/users';
|
|
|
|
export let cts = null;
|
|
export let filter = "";
|
|
export let question = null;
|
|
export let proposals = null;
|
|
export let notCorrected = false;
|
|
export let showStudent = false;
|
|
export let templates = false;
|
|
|
|
function refreshResponses() {
|
|
let req = question.getResponses();
|
|
|
|
req.then((res) => {
|
|
responses = res;
|
|
dispatch('nb_responses', res.length);
|
|
});
|
|
|
|
return req;
|
|
}
|
|
|
|
const dispatch = createEventDispatcher();
|
|
let req_responses = refreshResponses();
|
|
let responses = [];
|
|
|
|
let filteredResponses = [];
|
|
$:{
|
|
filteredResponses = responses.filter((r) => (notCorrected || r.time_scored <= r.time_reported || !r.time_scored) && (!filter || ((filter[0] == '!' && !r.value.match(filter.substring(1))) || r.value.match(filter))));
|
|
}
|
|
|
|
export async function applyCorrections() {
|
|
for (const r of filteredResponses) {
|
|
const my_correction = { };
|
|
let has_no_lost_answer = false;
|
|
let completed_correction = false;
|
|
|
|
for (const tpl of templates) {
|
|
if (tpl.score >= 0) has_no_lost_answer = true;
|
|
if (!tpl.regexp && tpl.label) continue;
|
|
|
|
if (tpl.regexp && (tpl.regexp[0] == '!' && !r.value.match(tpl.regexp.substring(1))) || r.value.match(tpl.regexp)) {
|
|
my_correction[tpl.id] = true;
|
|
completed_correction = true;
|
|
} else {
|
|
my_correction[tpl.id] = false;
|
|
}
|
|
}
|
|
|
|
// If no valid correction template AND valid answer is defined,
|
|
// don't consider the absence of match as valid answer.
|
|
if (!completed_correction && has_no_lost_answer) continue;
|
|
|
|
const auto = await autoCorrection(r.id_user, my_correction);
|
|
r.score = auto.score;
|
|
r.score_explaination = auto.score_explaination;
|
|
await r.save();
|
|
}
|
|
req_responses = refreshResponses();
|
|
}
|
|
</script>
|
|
|
|
{#await req_responses}
|
|
<div class="text-center mt-4">
|
|
<div class="spinner-border text-primary mx-3" role="status"></div>
|
|
<span>Récupération des réponses…</span>
|
|
</div>
|
|
{:then}
|
|
{#each filteredResponses as response, rid (response.id)}
|
|
<div class="row">
|
|
<div class="col">
|
|
<div class="card mt-3">
|
|
<div class="card-body">
|
|
{#if question.kind == 'mcq' || question.kind == 'ucq'}
|
|
{#if !proposals}
|
|
<div class="alert bg-danger">
|
|
Une erreur s'est produite, aucune proposition n'a été chargée
|
|
</div>
|
|
{:else}
|
|
<QuestionProposals
|
|
kind={question.kind}
|
|
prefixid={'r' + response.id}
|
|
{proposals}
|
|
readonly
|
|
value={response.value}
|
|
/>
|
|
{/if}
|
|
{:else}
|
|
<p
|
|
class="card-text"
|
|
style="white-space: pre-line"
|
|
>
|
|
{response.value}
|
|
</p>
|
|
{/if}
|
|
<ResponseCorrected
|
|
{response}
|
|
/>
|
|
</div>
|
|
<div class="card-footer">
|
|
<CorrectionResponseFooter
|
|
{cts}
|
|
{rid}
|
|
bind:response={response}
|
|
{templates}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{#if showStudent}
|
|
<div class="col-auto">
|
|
<div class="text-center mt-2" style="max-width: 110px">
|
|
{#await getUser(response.id_user)}
|
|
<div class="spinner-border text-primary mx-3" role="status"></div>
|
|
{:then user}
|
|
<a href="/users/{user.login}">
|
|
<img class="img-thumbnail" src="https://photos.cri.epita.fr/thumb/{user.login}" alt="avatar {user.login}">
|
|
<div
|
|
class="text-truncate"
|
|
title={user.login}
|
|
>
|
|
{user.login}
|
|
</div>
|
|
</a>
|
|
{/await}
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/each}
|
|
{/await}
|