Pierre-Olivier Mercier
61f4795511
All checks were successful
continuous-integration/drone/push Build is passing
242 lines
12 KiB
Svelte
242 lines
12 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
|
|
import { user } from '$lib/stores/user';
|
|
import BuildState from '$lib/components/BuildState.svelte';
|
|
import DateFormat from '$lib/components/DateFormat.svelte';
|
|
import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
|
|
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
|
|
import WorkRepository from '$lib/components/WorkRepository.svelte';
|
|
import { getRepositories } from '$lib/repositories';
|
|
import { ToastsStore } from '$lib/stores/toasts';
|
|
import { getUsers } from '$lib/users';
|
|
|
|
export let data;
|
|
let usersP = null;
|
|
let show_dl_btn = { };
|
|
let repositoriesP = { };
|
|
let w = null;
|
|
$: {
|
|
w = data.work;
|
|
usersP = getUsers(w.promo, w.group);
|
|
usersP.then((users) => {
|
|
nb_users = users.length;
|
|
show_dl_btn = { };
|
|
for (const user of users) {
|
|
updateRepoUser(w.id, user.id);
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateRepoUser(wid, userid) {
|
|
repositoriesP[userid] = getRepositories(wid, userid);
|
|
}
|
|
|
|
async function runGradations() {
|
|
for (const user of await usersP) {
|
|
if (repositoriesP[user.id]) {
|
|
try {
|
|
for (const repo of await repositoriesP[user.id]) {
|
|
repo.runGradation();
|
|
}
|
|
} catch {}
|
|
}
|
|
}
|
|
}
|
|
|
|
let nb_rendus = 0;
|
|
let nb_users = 0;
|
|
|
|
let show_logs = null;
|
|
let run_pull_for = {repo: null, user: null, struct: {tag: null, sig_optional: false}};
|
|
let search_repo_for = {repo: null, user: null};
|
|
</script>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<div class="d-flex align-items-center">
|
|
<h2>
|
|
<a href="works/{w.id}" class="text-muted" style="text-decoration: none"><</a>
|
|
{w.title}
|
|
<small class="text-muted">Rendus {Math.trunc(nb_rendus/nb_users*100)} % ({nb_rendus}/{nb_users})</small>
|
|
</h2>
|
|
<SurveyBadge class="ms-2" survey={w} />
|
|
</div>
|
|
<div>
|
|
<button
|
|
class="btn btn-sm btn-success mr-1"
|
|
title="Relancer les tests"
|
|
on:click={runGradations}
|
|
>
|
|
<i class="bi bi-play"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{#await usersP then users}
|
|
<table class="w-100 mb-5">
|
|
<thead>
|
|
<tr>
|
|
<th>Login</th>
|
|
<th>Rendu</th>
|
|
<th>Dépôts</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#each users as user (user.id)}
|
|
<tr>
|
|
<td><a href="users/{user.login}" class="text-truncate">{user.login}</a></td>
|
|
<td class="text-center">
|
|
<SubmissionStatus work={w} user={user} on:done={() => { nb_rendus += 1; show_dl_btn[user.id] = true; }} />
|
|
</td>
|
|
<td>
|
|
{#if repositoriesP[user.id]}
|
|
{#await repositoriesP[user.id] then repos}
|
|
<div class="d-flex flex-column">
|
|
{#each repos as repo (repo.id)}
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<code class="text-truncate mx-1">
|
|
{repo.uri}
|
|
</code>
|
|
<div class="mx-1" style="white-space: nowrap">
|
|
{#if repo.last_check}
|
|
<DateFormat date={new Date(repo.last_check)} dateStyle="medium" timeStyle="medium" />
|
|
<BuildState
|
|
repo_pull_state={repo.getBuildState()}
|
|
on:show_logs={() => { show_logs = repo.getBuildLogs(user.id); (new bootstrap.Modal(document.getElementById('logsModal'))).show(); }}
|
|
/>
|
|
{:else}
|
|
-
|
|
{/if}
|
|
</div>
|
|
<div style="white-space: nowrap;">
|
|
<button
|
|
class="btn btn-sm btn-primary mr-1"
|
|
title="Rafraîchir"
|
|
on:click={() => { run_pull_for = { repo, user, struct: {tag:''}, modal: new bootstrap.Modal(document.getElementById('pullModal'))}; run_pull_for.modal.show(); }}
|
|
>
|
|
<i class="bi bi-arrow-clockwise"></i>
|
|
</button>
|
|
<button
|
|
class="btn btn-sm btn-danger mr-1"
|
|
title="Désassocier le dépôt"
|
|
on:click={() => { repo.delete(user.id); updateRepoUser(w.id, user.id); }}
|
|
>
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
<button
|
|
class="btn btn-sm btn-success mr-1"
|
|
class:disabled={!show_dl_btn[user.id]}
|
|
title="Relancer les tests"
|
|
on:click={() => { repo.runGradation(); }}
|
|
>
|
|
<i class="bi bi-play"></i>
|
|
</button>
|
|
<a
|
|
href="/api/users/{user.id}/works/{w.id}/repositories/{repo.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>
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{:catch err}
|
|
<div class="d-flex justify-content-between">
|
|
-
|
|
<button
|
|
class="btn btn-sm btn-info mx-1"
|
|
title="Choisir un dépôt"
|
|
on:click={() => { search_repo_for = { repo: null, user, modal: new bootstrap.Modal(document.getElementById('repoModal'))}; search_repo_for.modal.show(); }}
|
|
>
|
|
<i class="bi bi-search"></i>
|
|
</button>
|
|
</div>
|
|
{/await}
|
|
{/if}
|
|
</td>
|
|
<td class="d-flex gap-1">
|
|
<a
|
|
href="/api/users/{user.id}/works/{w.id}/download"
|
|
class="btn btn-sm btn-dark"
|
|
class:disabled={!show_dl_btn[user.id]}
|
|
title="Télécharger la tarball du rendu"
|
|
>
|
|
<i class="bi bi-download"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{/each}
|
|
</tbody>
|
|
</table>
|
|
{/await}
|
|
|
|
<div class="modal fade" tabindex="-1" id="pullModal">
|
|
<div class="modal-dialog">
|
|
<form class="modal-content" on:submit|preventDefault={() => {run_pull_for.modal.hide(); try { run_pull_for.repo.retrieveWork(run_pull_for.struct); updateRepoUser(w.id, run_pull_for.user.id); } catch(err) { ToastsStore.addToast({color: "danger", title: "Connexion impossible", msg: err}) };}}>
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Repository Pull {#if run_pull_for.user}{run_pull_for.user.login}{/if}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group row mb-2">
|
|
<label class="col-2 col-form-label" for="pull-tag">Tag</label>
|
|
<!-- svelte-ignore a11y-autofocus -->
|
|
<input class="form-control col" id="pull-tag" autofocus placeholder={w.tag} bind:value={run_pull_for.struct.tag}>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" value="1" id="optional-sig" bind:checked={run_pull_for.struct.sig_optional}>
|
|
<label class="form-check-label" for="optional-sig">
|
|
Signature du tag optionnelle
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="submit" class="btn btn-primary">
|
|
Récupérer le travail
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" tabindex="-1" id="repoModal">
|
|
<div class="modal-dialog">
|
|
<form class="modal-content" on:submit|preventDefault={() => {search_repo_for.modal.hide(); try { search_repo_for.repo.retrieveWork(run_pull_for.struct); updateRepoUser(w.id, run_pull_for.user.id); } catch(err) { ToastsStore.addToast({color: "danger", title: "Connexion impossible", msg: err}) }; search_repo_for.user = null; }}>
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Repository selector {#if search_repo_for.user}{search_repo_for.user.login}{/if}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
{#if search_repo_for.user}
|
|
<WorkRepository work={w} user={search_repo_for.user} />
|
|
{/if}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" tabindex="-1" id="logsModal">
|
|
<div class="modal-dialog modal-xl">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Repository Logs</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
{#if show_logs}
|
|
{#await show_logs}
|
|
<div class="modal-body d-flex justify-content-between">
|
|
<div class="spinner-border text-primary" role="status"></div>
|
|
</div>
|
|
{:then logs}
|
|
<div class="modal-body d-flex justify-content-between bg-dark text-light">
|
|
<pre class="pb-2">{#each logs as l (l.pos)}{l.out}{/each}</pre>
|
|
</div>
|
|
{/await}
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|