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/routes/works/[wid]/rendus/+page.svelte
Pierre-Olivier Mercier 61f4795511
All checks were successful
continuous-integration/drone/push Build is passing
New route to view user's trace through repository
2023-03-05 14:41:09 +01:00

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">&lt;</a>
{w.title}
<small class="text-muted">Rendus {Math.trunc(nb_rendus/nb_users*100)}&nbsp;% ({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>