Refactor work page
This commit is contained in:
parent
6c471b5ec1
commit
07ceb20d63
153
ui/src/lib/components/WorkGrades.svelte
Normal file
153
ui/src/lib/components/WorkGrades.svelte
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<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 : {Math.round(stats.mean*100)/100}, min : {stats.min}, max : {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 …</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>
|
39
ui/src/lib/components/WorkHeader.svelte
Normal file
39
ui/src/lib/components/WorkHeader.svelte
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<script>
|
||||||
|
import { user } from '$lib/stores/user';
|
||||||
|
import DateFormat from '$lib/components/DateFormat.svelte';
|
||||||
|
import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
|
||||||
|
|
||||||
|
export let work;
|
||||||
|
export let my_submission;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<dl style="columns: 3">
|
||||||
|
<dt>Date de début</dt>
|
||||||
|
<dd><DateFormat date={new Date(work.start_availability)} dateStyle="medium" timeStyle="medium" /></dd>
|
||||||
|
<dt>Date de fin</dt>
|
||||||
|
<dd><DateFormat date={new Date(work.end_availability)} dateStyle="medium" timeStyle="medium" /></dd>
|
||||||
|
{#if work.submission_url != "-"}
|
||||||
|
<dt>Rendu ?</dt>
|
||||||
|
<dd>
|
||||||
|
{#if work.submission_url}
|
||||||
|
<SubmissionStatus work={w} user={$user} />
|
||||||
|
{:else}
|
||||||
|
{#await my_submission}
|
||||||
|
<div class="spinner-grow spinner-grow-sm mx-1" role="status"></div>
|
||||||
|
{:then submission}
|
||||||
|
<i
|
||||||
|
class="bi bi-check-circle text-success"
|
||||||
|
title="Oui !"
|
||||||
|
></i>
|
||||||
|
<DateFormat date={new Date(submission.date)} dateStyle="medium" timeStyle="medium" />
|
||||||
|
{:catch}
|
||||||
|
<i
|
||||||
|
class="bi bi-x-circle text-danger"
|
||||||
|
title="Pas de rendu trouvé"
|
||||||
|
></i>
|
||||||
|
Non
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
</dd>
|
||||||
|
{/if}
|
||||||
|
</dl>
|
@ -3,11 +3,12 @@
|
|||||||
|
|
||||||
import { user } from '$lib/stores/user';
|
import { user } from '$lib/stores/user';
|
||||||
import DateFormat from '$lib/components/DateFormat.svelte';
|
import DateFormat from '$lib/components/DateFormat.svelte';
|
||||||
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
|
|
||||||
import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
|
import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
|
||||||
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
|
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
|
||||||
import TraceStatus from '$lib/components/TraceStatus.svelte';
|
import TraceStatus from '$lib/components/TraceStatus.svelte';
|
||||||
import WorkAdmin from '$lib/components/WorkAdmin.svelte';
|
import WorkAdmin from '$lib/components/WorkAdmin.svelte';
|
||||||
|
import WorkGrades from '$lib/components/WorkGrades.svelte';
|
||||||
|
import WorkHeader from '$lib/components/WorkHeader.svelte';
|
||||||
import WorkRepository from '$lib/components/WorkRepository.svelte';
|
import WorkRepository from '$lib/components/WorkRepository.svelte';
|
||||||
import { getScore } from '$lib/users';
|
import { getScore } from '$lib/users';
|
||||||
|
|
||||||
@ -15,41 +16,15 @@
|
|||||||
let edit = false;
|
let edit = false;
|
||||||
let my_submission = null;
|
let my_submission = null;
|
||||||
let warn_already_used = false;
|
let warn_already_used = false;
|
||||||
|
let showSteps = false;
|
||||||
let w = null;
|
let w = null;
|
||||||
let gradesP = null;
|
|
||||||
let gradationStatus = {};
|
|
||||||
let stats = {"mean": 0, "min": 999, "max": 0};
|
|
||||||
|
|
||||||
$: w = data.work;
|
$: w = data.work;
|
||||||
$: refresh_submission(data.work);
|
$: refresh_submission(data.work);
|
||||||
$: refresh_grades(data.work);
|
|
||||||
|
|
||||||
function refresh_submission(w) {
|
function refresh_submission(w) {
|
||||||
my_submission = w.getSubmission();
|
my_submission = w.getSubmission();
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
{#if $user && $user.is_admin}
|
{#if $user && $user.is_admin}
|
||||||
@ -75,151 +50,21 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
{#if showSteps}
|
||||||
<h3 class="mt-3">
|
|
||||||
Notes
|
|
||||||
<small class="text-muted">
|
|
||||||
{#if stats.mean > 0}(moyenne : {Math.round(stats.mean*100)/100}, min : {stats.min}, max : {stats.max}){/if}
|
|
||||||
</small>
|
|
||||||
</h3>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-info"
|
|
||||||
title="Ajouter les étudiants manquant"
|
|
||||||
on:click={() => addMissingStudents(w)}
|
|
||||||
>
|
|
||||||
<i class="bi bi-people"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-light"
|
|
||||||
title="Rafraîchir l'affichage des notes"
|
|
||||||
on:click={() => refresh_grades(w)}
|
|
||||||
>
|
|
||||||
<i class="bi bi-arrow-clockwise"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card mt-3 mb-5">
|
|
||||||
{#await gradesP}
|
|
||||||
<div class="text-center mb-5">
|
|
||||||
<div class="spinner-border text-primary mx-3" role="status"></div>
|
|
||||||
<span>Chargement des notes …</span>
|
|
||||||
</div>
|
|
||||||
{:then grades}
|
|
||||||
<p>
|
|
||||||
|
|
||||||
<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}
|
{:else}
|
||||||
{#each grades as grade, gid (grade.id)}
|
<WorkGrades
|
||||||
<tr>
|
work={w}
|
||||||
<td>
|
on:switch_steps={() => showSteps = true}
|
||||||
<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/{w.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/{w.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>
|
|
||||||
{#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}
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-danger mr-1"
|
|
||||||
title="Supprimer la note"
|
|
||||||
on:click={() => { grade.delete().then(() => refresh_grades(w)); }}
|
|
||||||
>
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
{/if}
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
{:else if (!$user || !$user.is_admin) && new Date(w.start_availability) > new Date()}
|
{:else if (!$user || !$user.is_admin) && new Date(w.start_availability) > new Date()}
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
<i class="bi bi-stopwatch-fill"></i>
|
<i class="bi bi-stopwatch-fill"></i>
|
||||||
<strong>Ce travail n'est pas encore ouvert.</strong> Revenez plus tard !
|
<strong>Ce travail n'est pas encore ouvert.</strong> Revenez plus tard !
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<dl style="columns: 3">
|
<WorkHeader work={w} {my_submission} />
|
||||||
<dt>Date de début</dt>
|
|
||||||
<dd><DateFormat date={new Date(w.start_availability)} dateStyle="medium" timeStyle="medium" /></dd>
|
|
||||||
<dt>Date de fin</dt>
|
|
||||||
<dd><DateFormat date={new Date(w.end_availability)} dateStyle="medium" timeStyle="medium" /></dd>
|
|
||||||
{#if w.submission_url != "-"}
|
|
||||||
<dt>Rendu ?</dt>
|
|
||||||
<dd>
|
|
||||||
{#if w.submission_url}
|
|
||||||
<SubmissionStatus work={w} user={$user} />
|
|
||||||
{:else}
|
|
||||||
{#await my_submission}
|
|
||||||
<div class="spinner-grow spinner-grow-sm mx-1" role="status"></div>
|
|
||||||
{:then submission}
|
|
||||||
<i
|
|
||||||
class="bi bi-check-circle text-success"
|
|
||||||
title="Oui !"
|
|
||||||
></i>
|
|
||||||
<DateFormat date={new Date(submission.date)} dateStyle="medium" timeStyle="medium" />
|
|
||||||
{:catch}
|
|
||||||
<i
|
|
||||||
class="bi bi-x-circle text-danger"
|
|
||||||
title="Pas de rendu trouvé"
|
|
||||||
></i>
|
|
||||||
Non
|
|
||||||
{/await}
|
|
||||||
{/if}
|
|
||||||
</dd>
|
|
||||||
{/if}
|
|
||||||
</dl>
|
|
||||||
{#if w.description}
|
{#if w.description}
|
||||||
<hr>
|
<hr>
|
||||||
{@html w.description}
|
{@html w.description}
|
||||||
|
Reference in New Issue
Block a user