This repository has been archived on 2024-03-28. You can view files and clone it, but cannot push or open issues or pull requests.
atsebay.t/ui/src/lib/components/WorkGrades.svelte

154 lines
6.5 KiB
Svelte

<script>
import { createEventDispatcher } from 'svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
const dispatch = createEventDispatcher();
export let work;
let gradesP = null;
let gradationStatus = {};
let stats = {"mean": 0, "min": 999, "max": 0};
$: refresh_grades(work);
function refresh_grades(w) {
gradesP = w.getGrades();
gradesP.then((grades) => {
if (grades.length <= 0) return;
let sum = 0;
for (const grade of grades) {
if (!gradationStatus[grade.id])
gradationStatus[grade.id] = grade.gradationStatus();
sum += grade.score;
if (stats.min > grade.score && grade.comment != "- Non rendu -") stats.min = grade.score;
if (stats.max < grade.score) stats.max = grade.score;
}
stats.mean = sum / grades.length;
});
}
async function addMissingStudents(w) {
await w.addMissingGrades();
refresh_grades(w);
}
</script>
<div class="d-flex justify-content-between align-items-center">
<h3 class="mt-3">
Notes
<small class="text-muted">
{#if stats.mean > 0}(moyenne&nbsp;: {Math.round(stats.mean*100)/100}, min&nbsp;: {stats.min}, max&nbsp;: {stats.max}){/if}
</small>
</h3>
<div>
<button
class="btn btn-outline-primary"
title="Afficher le résumé par étapes"
on:click={() => dispatch("switch_steps")}
>
<i class="bi bi-bar-chart-steps"></i>
</button>
<button
class="btn btn-outline-info"
title="Ajouter les étudiants manquant"
on:click={() => addMissingStudents(work)}
>
<i class="bi bi-people"></i>
</button>
<button
class="btn btn-light"
title="Rafraîchir l'affichage des notes"
on:click={() => refresh_grades(work)}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
<div class="card mt-3 mb-5">
{#await gradesP}
<div class="text-center my-5">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement des notes &hellip;</span>
</div>
{:then grades}
<table class="table table-hover table-striped table-sm mb-0">
<thead>
<tr>
<th>Login</th>
<th>Note</th>
<th>Commentaire</th>
<th>Date de la note</th>
</tr>
</thead>
<tbody>
{#if !grades}
<div class="text-center">
Aucune note n'a encore été envoyée pour ce travail.
</div>
{:else}
{#each grades as grade, gid (grade.id)}
<tr>
<td>
<a href="users/{grade.id_user}">{grade.login}</a>
</td>
<td><ScoreBadge score={grade.score} /></td>
<td>{#if grade.comment}{grade.comment}{:else}-{/if}</td>
<td>{grade.date}</td>
<td>
<a
href="/api/users/{grade.id_user}/works/{work.id}/grades/{grade.id}/traces"
target="_blank"
class="btn btn-sm btn-outline-info mr-1"
title="Voir le détail de la notation"
>
<i class="bi bi-list-check"></i>
</a>
<a
href="/api/users/{grade.id_user}/works/{work.id}/grades/{grade.id}/forge"
target="_blank"
class="btn btn-sm btn-outline-primary mr-1"
title="Voir le contenu du dépôt lié"
>
<i class="bi bi-git"></i>
</a>
{#if gradationStatus[grade.id]}
{#await gradationStatus[grade.id]}
<button
class="btn btn-sm btn-outline-success mr-1"
title="Relancer la notation"
on:click={() => { grade.redoGradation().then(() => gradationStatus[grade.id] = grade.gradationStatus()); }}
>
<div class="spinner-border spinner-border-sm" role="status"></div>
</button>
{:then status}
<button
class="btn btn-sm mr-1"
class:btn-success={status.status == "success"}
class:btn-danger={status.status == "failure"}
class:btn-outline-danger={status.status == "killed"}
class:btn-outline-warning={status.status == "pending" || status.status == "running"}
title="Relancer la notation"
on:click={() => { grade.redoGradation(); gradationStatus[grade.id] = null; }}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
{/await}
{/if}
<button
class="btn btn-sm btn-danger mr-1"
title="Supprimer la note"
on:click={() => { grade.delete().then(() => refresh_grades(work)); }}
>
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
{/each}
{/if}
</tbody>
</table>
{/await}
</div>