New page to see grades sumary
This commit is contained in:
parent
7642a23947
commit
1f4ce865aa
206
ui/src/lib/components/WorkGradesSteps.svelte
Normal file
206
ui/src/lib/components/WorkGradesSteps.svelte
Normal file
@ -0,0 +1,206 @@
|
||||
<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,
|
||||
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)} %)
|
||||
</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)} %)
|
||||
</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)} %)
|
||||
</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"}
|
||||
>
|
||||
{step.number}. {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}
|
@ -8,6 +8,7 @@
|
||||
import TraceStatus from '$lib/components/TraceStatus.svelte';
|
||||
import WorkAdmin from '$lib/components/WorkAdmin.svelte';
|
||||
import WorkGrades from '$lib/components/WorkGrades.svelte';
|
||||
import WorkGradesSteps from '$lib/components/WorkGradesSteps.svelte';
|
||||
import WorkHeader from '$lib/components/WorkHeader.svelte';
|
||||
import WorkRepository from '$lib/components/WorkRepository.svelte';
|
||||
import { getScore } from '$lib/users';
|
||||
@ -51,7 +52,10 @@
|
||||
|
||||
<hr>
|
||||
{#if showSteps}
|
||||
|
||||
<WorkGradesSteps
|
||||
work={w}
|
||||
on:switch_steps={() => showSteps = false}
|
||||
/>
|
||||
{:else}
|
||||
<WorkGrades
|
||||
work={w}
|
||||
|
Reference in New Issue
Block a user