diff --git a/package.json b/package.json index 5874d9a..779a016 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "prettier": "prettier --check .", "eslint": "eslint .", - "format": "prettier --write .", + "fmt": "prettier --write .", "test:integration": "playwright test --trace on", "test:unit": "vitest run" }, diff --git a/src/lib/api-backend/admin.ts b/src/lib/api-backend/admin.ts index b53cb16..c5e05fc 100644 --- a/src/lib/api-backend/admin.ts +++ b/src/lib/api-backend/admin.ts @@ -1,5 +1,5 @@ import { axiosAPI } from './../axios'; -import type { Page, Table, UnparseableWebhook, UserForAdmin } from './../types'; +import type { GradingTask, Page, Table, UnparseableWebhook, UserForAdmin } from './../types'; import { loadUser } from './../stores'; export const getTables = async (): Promise => { @@ -50,3 +50,12 @@ export const deleteUnparseableWebhooks = async () => { url: '/fapi/admin/unparseable_webhooks' }); }; + +export const getGradingTasks = async ( + page: number, + per_page: number +): Promise> => { + return await axiosAPI + .get>(`/fapi/admin/grading_tasks?page=${page}&per_page=${per_page}`) + .then((res) => res.data); +}; diff --git a/src/lib/api-backend/module.ts b/src/lib/api-backend/module.ts index 51a19da..ec90505 100644 --- a/src/lib/api-backend/module.ts +++ b/src/lib/api-backend/module.ts @@ -33,3 +33,15 @@ export const getAssignment = async ( .get(`/fapi/module/${module_id}/assignment/${assignment_id}`) .then((res) => res.data); }; + +export const triggerGrading = async (module_id: string, assignment_id: string): Promise => { + return await axiosAPI + .post(`/fapi/module/${module_id}/assignment/${assignment_id}/trigger-grading`) + .then((res) => res.data); +}; + +export const syncRepo = async (module_id: string, assignment_id: string): Promise => { + return await axiosAPI + .post(`/fapi/module/${module_id}/assignment/${assignment_id}/sync-repo`) + .then((res) => res.data); +}; diff --git a/src/lib/api-mock/admin.ts b/src/lib/api-mock/admin.ts index 40fab76..07ae605 100644 --- a/src/lib/api-mock/admin.ts +++ b/src/lib/api-mock/admin.ts @@ -1,4 +1,4 @@ -import type { Page, Table, UnparseableWebhook, UserForAdmin } from './../types'; +import type { GradingTask, Page, Table, UnparseableWebhook, UserForAdmin } from './../types'; import * as mock from './../mock'; import { loadUser } from './../stores'; @@ -47,3 +47,19 @@ export const getUnparseableWebhooks = async ( export const deleteUnparseableWebhooks = async () => { mock.unparseable_webhooks.splice(0); }; + +export const getGradingTasks = async ( + page: number, + per_page: number +): Promise> => { + const start = page == 1 ? 0 : (page - 1) * per_page; + const end = start + per_page; + const items = mock.grading_tasks.slice(start, end); + return { + page: page, + per_page: per_page, + total_count: mock.grading_tasks.length, + total_page: Math.ceil(mock.grading_tasks.length / per_page), + data: items + }; +}; diff --git a/src/lib/api-mock/module.ts b/src/lib/api-mock/module.ts index 5c02f46..0728681 100644 --- a/src/lib/api-mock/module.ts +++ b/src/lib/api-mock/module.ts @@ -98,3 +98,27 @@ export const getAssignment = async ( ongoing_run: a.ongoing_run }; }; + +export const triggerGrading = async (module_id: string, assignment_id: string): Promise => { + const assignment = mock.modules + .find((m) => m.id === module_id) + ?.assignments.find((a) => a.id === assignment_id); + mock.grading_tasks.push({ + assignment_id: assignment_id, + created_at: new Date(), + module_id: module_id, + provider_login: mock.users[0].provider_login, + repository_name: assignment?.repository_name ?? 'no matching assignment', + status: 'queued', + updated_at: new Date() + }); +}; + +export const syncRepo = async (module_id: string, assignment_id: string): Promise => { + const assignment = mock.modules + .find((m) => m.id === module_id) + ?.assignments.find((a) => a.id === assignment_id); + if (assignment) { + assignment.linked = true; + } +}; diff --git a/src/lib/mock.ts b/src/lib/mock.ts index 418ccb3..91d93bc 100644 --- a/src/lib/mock.ts +++ b/src/lib/mock.ts @@ -4,7 +4,8 @@ import type { UnparseableWebhook, RunInfo, Details, - CompleteRunInfo + CompleteRunInfo, + GradingTask } from './types'; export const uuidv4 = () => { @@ -176,6 +177,8 @@ for (let index = 0; index < unparseable_webhooks.length; index++) { }; } +export const grading_tasks: GradingTask[] = []; + export const deleteUsers = (ids: string[]): void => { users = users.filter((u) => !ids.includes(u.id)); }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 35eed00..a4ab98d 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -197,3 +197,13 @@ export type UnparseableWebhook = { payload: string; error: string; }; + +export type GradingTask = { + module_id: string; + assignment_id: string; + provider_login: string; + status: string; + created_at: Date; + updated_at: Date; + repository_name: string; +}; diff --git a/src/routes/admin/+layout.svelte b/src/routes/admin/+layout.svelte index 62c96db..98da9e8 100644 --- a/src/routes/admin/+layout.svelte +++ b/src/routes/admin/+layout.svelte @@ -27,6 +27,13 @@ href="/admin/unparseable_webhooks">Unparseable Webhooks + diff --git a/src/routes/admin/grading_tasks/+page.svelte b/src/routes/admin/grading_tasks/+page.svelte new file mode 100644 index 0000000..91cf1e5 --- /dev/null +++ b/src/routes/admin/grading_tasks/+page.svelte @@ -0,0 +1,115 @@ + + +
+

Users

+ + {#await itemPromise} +

...loading items

+ + {:then page} + + + + + + + + + + + + + + {#each page.data as item} + + + + + + + + + + {/each} + +
Created atUpdated atModule IDAssignment IDProvider loginRepositoryStatus
{item.created_at.toISOString()}{item.updated_at.toISOString()}{item.module_id}{item.assignment_id}{item.provider_login}{item.repository_name}{item.status}
+ {#if page.total_page > 1} +
+
+ {#if pageNumber > 1} + + {/if} +
+
+ Page {pageNumber} +
+
+ {#if pageNumber < page.total_page} + + {/if} +
+
+ {/if} + {:catch error} +

{error.message}

+ {/await} +
+ + diff --git a/src/routes/admin/grading_tasks/+page.ts b/src/routes/admin/grading_tasks/+page.ts new file mode 100644 index 0000000..1c272c0 --- /dev/null +++ b/src/routes/admin/grading_tasks/+page.ts @@ -0,0 +1,5 @@ +export const csr = true; + +// since there's no dynamic data here, we can prerender +// it so that it gets served as a static asset in production +export const prerender = true; diff --git a/src/routes/module/[moduleId]/assignment/[assignmentId]/+page.svelte b/src/routes/module/[moduleId]/assignment/[assignmentId]/+page.svelte index 292ebcc..2a7a2df 100644 --- a/src/routes/module/[moduleId]/assignment/[assignmentId]/+page.svelte +++ b/src/routes/module/[moduleId]/assignment/[assignmentId]/+page.svelte @@ -7,7 +7,7 @@ export let data; const computeGrade = (a: Assignment): number => { - if (a.latest_run !== undefined) { + if (a.latest_run) { let grade = 0; let max_grade = 0; for (const detail of a.latest_run.details) { @@ -79,6 +79,19 @@
Coefficient: {assignment.factor_percentage} %
+ {#if assignment.repo_linked} +
+
+ +
+ +
+ {/if} {#if assignment.locked}
@@ -105,6 +118,12 @@ > {#if !assignment.repo_linked}
(missing)
+ {/if}
@@ -112,7 +131,7 @@
Grading state:
- {#if assignment.ongoing_run !== undefined} + {#if assignment.ongoing_run}
Ongoing
@@ -122,7 +141,7 @@
{/if} - {#if assignment.latest_run !== undefined} + {#if assignment.latest_run}
Latest run:
- {#if assignment.latest_run !== undefined} + {#if assignment.latest_run}

Details of latest run:

    {#each assignment.latest_run.details as detail} @@ -282,4 +301,23 @@ transform: rotate(360deg); } } + + .btn { + /*border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); */ + border-radius: 0.375rem; + } + + .btn-success { + background-color: #198754; + } + + .btn-link { + background: none !important; + border: none; + padding: 0 !important; + font-family: arial, sans-serif; + color: #069; + text-decoration: underline; + cursor: pointer; + }