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/WorkGradesSteps.svelte

215 lines
9.3 KiB
Svelte

<script>
import { createEventDispatcher } from 'svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
const dispatch = createEventDispatcher();
export let work;
let gradesP = null;
let grade_idx = {};
let gradationStatus = {};
let stats = [];
$: refresh_grades(work);
function refresh_grades(w) {
gradesP = w.getGrades();
gradesP.then((grades) => {
if (grades.length <= 0) return;
for (const grade of grades) {
grade_idx[grade.id] = grade;
if (!gradationStatus[grade.id]) {
gradationStatus[grade.id] = grade.gradationStatus();
gradationStatus[grade.id].then((status) => {
for (const istage in status.stages) {
const stage = status.stages[istage];
if (stats.length <= istage) {
stats.push({
arch: stage.arch,
name: stage.name,
number: stage.number,
status: [],
steps: [],
});
}
stats[istage].status.push(stage.status);
for (const istep in stage.steps) {
const step = stage.steps[istep];
if (stats[istage].steps.length <= istep) {
stats[istage].steps.push({
name: step.name,
number: step.number,
status: [],
});
}
stats[istage].steps[istep].status.push(step.status);
}
}
stats = stats;
});
}
}
});
}
let view_step = null;
</script>
<div class="d-flex justify-content-between align-items-center">
<h3 class="mt-3">
Réussite des étapes
</h3>
<div>
<button
class="btn btn-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-light"
title="Rafraîchir l'affichage des notes"
on:click={() => refresh_grades(work)}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
{#each stats as stage, istage}
<h5>
{stage.name}
<small>{stage.arch}</small>
</h5>
<div class="row row-cols-5">
{#each stage.steps as step, istep}
<div class="col">
<div class="card mb-3">
<div class="card-body fw-bolder text-truncate" title={step.name}>
{step.number}. {step.name}
</div>
<div
class="card-footer text-center"
class:bg-success={step.status.filter((e) => e == "success").length/step.status.length > 0.5}
on:click={() => view_step = {istage, istep, status: "success"}}
>
<i class="bi bi-check me-2 fw-bolder"></i>
{step.status.filter((e) => e == "success").length}
({Math.trunc(step.status.filter((e) => e == "success").length*100/step.status.length)}&nbsp;%)
</div>
<div
class="card-footer text-center"
class:bg-danger={step.status.filter((e) => e == "failure").length/step.status.length >= 0.5}
on:click={() => view_step = {istage, istep, status: "failure"}}
>
<i class="bi bi-x me-2 fw-bolder"></i>
{step.status.filter((e) => e == "failure").length}
({Math.trunc(step.status.filter((e) => e == "failure").length*100/step.status.length)}&nbsp;%)
</div>
{#if step.status.filter((e) => e == "skipped").length > 0}
<div
class="card-footer text-center"
class:fw-bold={step.status.filter((e) => e == "skipped").length/step.status.length >= 0.5}
on:click={() => view_step = {istage, istep, status: "skipped"}}
>
<i class="bi bi-skip-end me-2 fw-bolder"></i>
{step.status.filter((e) => e == "skipped").length}
({Math.trunc(step.status.filter((e) => e == "skipped").length*100/step.status.length)}&nbsp;%)
</div>
{/if}
</div>
</div>
{/each}
</div>
{/each}
{#if view_step}
<h3>
Étudiants correspondant
<small class="text-muted">
{"{"}
{stats[view_step.istage].name}
<i class="bi bi-arrow-right"></i>
<em>{stats[view_step.istage].steps[view_step.istep].name}</em>
"{view_step.status}"
{"}"}
</small>
</h3>
<div class="row row-cols-6">
{#each Object.keys(gradationStatus) as gsi}
{#await gradationStatus[gsi] then gs}
{#if gs.stages[view_step.istage] && gs.stages[view_step.istage].steps[view_step.istep] && gs.stages[view_step.istage].steps[view_step.istep].status == view_step.status}
<div class="col">
<div class="card mb-3">
<div
class="card-header text-monospace text-truncate"
title={grade_idx[gsi].login}
>
<a href="/users/{grade_idx[gsi].id_user}">
{grade_idx[gsi].login}
</a>
</div>
<ul class="list-group list-group-flush">
{#each gs.stages[view_step.istage].steps as step}
<li
class="list-group-item text-truncate p-2"
class:bg-success={step.status == "success"}
class:bg-light={step.status == "skipped"}
class:bg-danger={step.status == "failure"}
class:bg-warning={step.status == "pending" || step.status == "running"}
class:bg-info={step.status == "killed"}
>
<a
href="/api/users/{grade_idx[gsi].id_user}/works/{work.id}/grades/{grade_idx[gsi].id}/traces/{gs.stages[view_step.istage].number}/{step.number}"
target="_blank"
title="Voir le détail de cette étape"
>
{step.number}.
</a>
{step.name}
</li>
{/each}
</ul>
<div
class="card-footer d-flex justify-content-around align-items-center px-0"
>
<ScoreBadge score={grade_idx[gsi].score} />
<a
href="/api/users/{grade_idx[gsi].id_user}/works/{work.id}/grades/{grade_idx[gsi].id}/traces"
target="_blank"
class="btn btn-sm btn-outline-info"
title="Voir le détail de la notation"
>
<i class="bi bi-list-check"></i>
</a>
<a
href="/api/users/{grade_idx[gsi].id_user}/works/{work.id}/grades/{grade_idx[gsi].id}/forge"
target="_blank"
class="btn btn-sm btn-outline-primary"
title="Voir le contenu du dépôt lié"
>
<i class="bi bi-git"></i>
</a>
<button
class="btn btn-sm btn-outline-success"
title="Relancer la notation"
on:click={() => { grade.redoGradation(); gradationStatus[grade.id] = null; }}
>
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</div>
</div>
{/if}
{/await}
{/each}
</div>
{/if}