From 138fba5adedb2d60345b20e25d051010ff547d07 Mon Sep 17 00:00:00 2001
From: Brieuc Dubois <git@bhasher.com>
Date: Sun, 13 Apr 2025 14:46:19 +0200
Subject: [PATCH] Fix #82: backend part

---
 backend/app/crud/tasks.py                     |  9 ++++++++
 backend/app/routes/tasks.py                   | 15 ++++++++++++
 frontend/src/lib/api/tasks.ts                 |  9 ++++++++
 .../src/routes/sessions/[id]/+page.svelte     | 11 +++++++--
 frontend/src/routes/sessions/[id]/+page.ts    | 23 +++++++++++++++++--
 5 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/backend/app/crud/tasks.py b/backend/app/crud/tasks.py
index e3dbd2a..f487f1b 100644
--- a/backend/app/crud/tasks.py
+++ b/backend/app/crud/tasks.py
@@ -49,3 +49,12 @@ def get_task_status_session(db: Session, session_id: int) -> models.TaskStatus |
         .order_by(models.TaskStatus.created_at.desc())
         .first()
     )
+
+
+def get_task_status_completed(db: Session, student_id: int) -> list[models.TaskStatus]:
+    return (
+        db.query(models.TaskStatus)
+        .filter(models.TaskStatus.student_id == student_id)
+        .filter(models.TaskStatus.status == "finish")
+        .all()
+    )
diff --git a/backend/app/routes/tasks.py b/backend/app/routes/tasks.py
index 68eede8..043143e 100644
--- a/backend/app/routes/tasks.py
+++ b/backend/app/routes/tasks.py
@@ -67,6 +67,21 @@ def get_task_status_session(
     return task_status
 
 
+@taskRouter.get(
+    "/status/completed/students/{student_id}", response_model=list[schemas.TaskStatus]
+)
+def get_task_status_completed(
+    student_id: int,
+    db: Session = Depends(get_db),
+):
+    task_status = crud.get_task_status_completed(db, student_id)
+    if task_status is None:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND, detail="Task status not found"
+        )
+    return task_status
+
+
 @taskRouter.get("/{task_id}", response_model=schemas.Task)
 def get_task(
     task_id: int,
diff --git a/frontend/src/lib/api/tasks.ts b/frontend/src/lib/api/tasks.ts
index a2ed536..0f99fe8 100644
--- a/frontend/src/lib/api/tasks.ts
+++ b/frontend/src/lib/api/tasks.ts
@@ -97,3 +97,12 @@ export async function getTaskStatusFromSessionAPI(
 	if (!response.ok) return null;
 	return await response.json();
 }
+
+export async function getTaskStatusCompletedFromStudentAPI(
+	fetch: fetchType,
+	student_id: number
+): Promise<any> {
+	const response = await fetch(`/api/tasks/status/completed/students/${student_id}`);
+	if (!response.ok) return null;
+	return await response.json();
+}
diff --git a/frontend/src/routes/sessions/[id]/+page.svelte b/frontend/src/routes/sessions/[id]/+page.svelte
index 36ed70b..3b9f6b5 100644
--- a/frontend/src/routes/sessions/[id]/+page.svelte
+++ b/frontend/src/routes/sessions/[id]/+page.svelte
@@ -9,7 +9,7 @@
 
 	let { data }: { data: PageData } = $props();
 	let user = data.user!;
-	let { session, jwt, tasks } = data;
+	let { session, jwt, tasks, completedTasks } = data;
 	let { onlineUsers } = session;
 
 	let level = $state('all');
@@ -70,6 +70,8 @@
 			toastAlert($t('tasks.statusFail'));
 			return;
 		}
+		completedTasks.push(currentTask);
+
 		taskInProgress = false;
 		currentTask = null;
 		toastSuccess($t('tasks.taskFinished'));
@@ -134,7 +136,12 @@
 						{#if level === 'all' || l === level}
 							<optgroup label={l}>
 								{#each tasks.filter((task: Task) => task.level === l) as task (task.id)}
-									<option value={task}>{task.shortTitle}</option>
+									<option value={task}>
+										{#if completedTasks.includes(task)}
+											✓
+										{/if}
+										{task.shortTitle}
+									</option>
 								{/each}
 							</optgroup>
 						{/if}
diff --git a/frontend/src/routes/sessions/[id]/+page.ts b/frontend/src/routes/sessions/[id]/+page.ts
index 08494fa..fd99a73 100644
--- a/frontend/src/routes/sessions/[id]/+page.ts
+++ b/frontend/src/routes/sessions/[id]/+page.ts
@@ -1,5 +1,9 @@
 import { getSessionAPI } from '$lib/api/sessions';
-import { getTasksAPI, getTaskStatusFromSessionAPI } from '$lib/api/tasks';
+import {
+	getTasksAPI,
+	getTaskStatusCompletedFromStudentAPI,
+	getTaskStatusFromSessionAPI
+} from '$lib/api/tasks';
 import Session from '$lib/types/session';
 import Task, { TaskStatus } from '$lib/types/tasks';
 import { error, type Load } from '@sveltejs/kit';
@@ -38,5 +42,20 @@ export const load: Load = async ({ params, fetch, data }) => {
 		}
 	}
 
-	return { session, jwt, tasks, currentTask };
+	let completedTasks: Task[] = [];
+
+	if (session.student) {
+		const completedTasksStatusRaw = await getTaskStatusCompletedFromStudentAPI(
+			fetch,
+			session.student.id
+		);
+		if (completedTasksStatusRaw) {
+			const completedTasksStatus = TaskStatus.parseAll(completedTasksStatusRaw);
+			completedTasks = completedTasksStatus
+				.map((taskStatus) => tasks.find((task) => task.id === taskStatus.task_id))
+				.filter((task) => task !== undefined) as Task[];
+		}
+	}
+
+	return { session, jwt, tasks, currentTask, completedTasks };
 };
-- 
GitLab