From e88045c952001ed78f5acf818633c0db56475623 Mon Sep 17 00:00:00 2001 From: Kevin FEDYNA Date: Mon, 27 Jan 2025 13:11:13 +0100 Subject: [PATCH] Refactored students --- databases/init/students.sql | 17 +- fresh.gen.ts | 10 +- routes/(apps)/_middleware.ts | 4 +- .../students/(_islands)/ConsultStudents.tsx | 2 +- .../students/(_islands)/UploadStudents.tsx | 2 +- routes/(apps)/students/(_props)/props.ts | 1 - routes/(apps)/students/api/insert_students.ts | 84 ---------- routes/(apps)/students/api/students.ts | 155 ++++++++++++++++++ routes/(apps)/students/api/types.d.ts | 13 ++ .../students/partials/(admin)/consult.tsx | 3 +- .../students/partials/(admin)/upload.tsx | 3 +- routes/(apps)/students/partials/index.tsx | 2 +- routes/(apps)/students/partials/overview.tsx | 17 -- 13 files changed, 187 insertions(+), 126 deletions(-) delete mode 100644 routes/(apps)/students/api/insert_students.ts create mode 100644 routes/(apps)/students/api/students.ts create mode 100644 routes/(apps)/students/api/types.d.ts delete mode 100644 routes/(apps)/students/partials/overview.tsx diff --git a/databases/init/students.sql b/databases/init/students.sql index 4dbc7e8..b5a7878 100644 --- a/databases/init/students.sql +++ b/databases/init/students.sql @@ -1,15 +1,14 @@ create table promotions ( - id integer primary key autoincrement, - name text, - endyear integer, - current integer + id integer primary key autoincrement, + endyear integer, + current integer ); create table students ( - userId text primary key, - firstName text, - lastName text, - mail text, - promotionId integer, + userId text primary key, + firstName text, + lastName text, + mail text, + promotionId integer, foreign key(promotionId) references promotions(id) ); \ No newline at end of file diff --git a/fresh.gen.ts b/fresh.gen.ts index 2beb32c..3061dcd 100644 --- a/fresh.gen.ts +++ b/fresh.gen.ts @@ -13,12 +13,12 @@ import * as $_apps_notes_index from "./routes/(apps)/notes/index.tsx"; import * as $_apps_notes_partials_admin_courses from "./routes/(apps)/notes/partials/(admin)/courses.tsx"; import * as $_apps_notes_partials_index from "./routes/(apps)/notes/partials/index.tsx"; import * as $_apps_notes_partials_notes from "./routes/(apps)/notes/partials/notes.tsx"; -import * as $_apps_students_api_insert_students from "./routes/(apps)/students/api/insert_students.ts"; +import * as $_apps_students_api_students from "./routes/(apps)/students/api/students.ts"; +import * as $_apps_students_api_types_d from "./routes/(apps)/students/api/types.d.ts"; import * as $_apps_students_index from "./routes/(apps)/students/index.tsx"; import * as $_apps_students_partials_admin_consult from "./routes/(apps)/students/partials/(admin)/consult.tsx"; import * as $_apps_students_partials_admin_upload from "./routes/(apps)/students/partials/(admin)/upload.tsx"; import * as $_apps_students_partials_index from "./routes/(apps)/students/partials/index.tsx"; -import * as $_apps_students_partials_overview from "./routes/(apps)/students/partials/overview.tsx"; import * as $_404 from "./routes/_404.tsx"; import * as $_app from "./routes/_app.tsx"; import * as $_middleware from "./routes/_middleware.ts"; @@ -55,8 +55,8 @@ const manifest = { $_apps_notes_partials_admin_courses, "./routes/(apps)/notes/partials/index.tsx": $_apps_notes_partials_index, "./routes/(apps)/notes/partials/notes.tsx": $_apps_notes_partials_notes, - "./routes/(apps)/students/api/insert_students.ts": - $_apps_students_api_insert_students, + "./routes/(apps)/students/api/students.ts": $_apps_students_api_students, + "./routes/(apps)/students/api/types.d.ts": $_apps_students_api_types_d, "./routes/(apps)/students/index.tsx": $_apps_students_index, "./routes/(apps)/students/partials/(admin)/consult.tsx": $_apps_students_partials_admin_consult, @@ -64,8 +64,6 @@ const manifest = { $_apps_students_partials_admin_upload, "./routes/(apps)/students/partials/index.tsx": $_apps_students_partials_index, - "./routes/(apps)/students/partials/overview.tsx": - $_apps_students_partials_overview, "./routes/_404.tsx": $_404, "./routes/_app.tsx": $_app, "./routes/_middleware.ts": $_middleware, diff --git a/routes/(apps)/_middleware.ts b/routes/(apps)/_middleware.ts index abb11cc..f30f19f 100644 --- a/routes/(apps)/_middleware.ts +++ b/routes/(apps)/_middleware.ts @@ -6,12 +6,12 @@ import { export const handler: MiddlewareHandler[] = [ /** - * Check if user is authenticated and add session to context accordingly. + * Get all available pages for current user. * @param request The HTTP incomming request. * @param context The Fresh context object with custom `AuthenticatedState`. * @returns The response from the next middleware. */ - async function checkAuthentication( + async function getAllAvailablePages( request: Request, context: FreshContext, ): Promise { diff --git a/routes/(apps)/students/(_islands)/ConsultStudents.tsx b/routes/(apps)/students/(_islands)/ConsultStudents.tsx index 1f82580..835754f 100644 --- a/routes/(apps)/students/(_islands)/ConsultStudents.tsx +++ b/routes/(apps)/students/(_islands)/ConsultStudents.tsx @@ -22,7 +22,7 @@ export default function ConsultStudents() { useEffect(() => { const fetchData = async () => { try { - const response = await fetch("/students/api/insert_students"); + const response = await fetch("/students/api/students"); if (!response.ok) { throw new Error(`Error fetching data: ${response.statusText}`); } diff --git a/routes/(apps)/students/(_islands)/UploadStudents.tsx b/routes/(apps)/students/(_islands)/UploadStudents.tsx index 244e4d2..6ff1d5a 100644 --- a/routes/(apps)/students/(_islands)/UploadStudents.tsx +++ b/routes/(apps)/students/(_islands)/UploadStudents.tsx @@ -39,7 +39,7 @@ export default function UploadStudents() { console.log(`Data from sheet ${sheetName}:`, data); - const response = await fetch("/students/api/insert_students", { + const response = await fetch("/students/api/students", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ promoName: sheetName, data }), diff --git a/routes/(apps)/students/(_props)/props.ts b/routes/(apps)/students/(_props)/props.ts index 93a50a5..13bafe9 100644 --- a/routes/(apps)/students/(_props)/props.ts +++ b/routes/(apps)/students/(_props)/props.ts @@ -5,7 +5,6 @@ const properties: AppProperties = { icon: "badge", pages: { index: "Homepage", - overview: "Students overview", upload: "Upload students", consult: "Consult students", }, diff --git a/routes/(apps)/students/api/insert_students.ts b/routes/(apps)/students/api/insert_students.ts deleted file mode 100644 index f1c5020..0000000 --- a/routes/(apps)/students/api/insert_students.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Handlers } from "$fresh/server.ts"; -// import { Database } from "@db/sqlite"; -import connect from "$root/databases/connect.ts"; - -export const handler: Handlers = { - // deno-lint-ignore require-await - async GET() { - try { - using connection = connect("students"); - - const promotions = connection.database.prepare( - "select id, name from promotions", - ).all(); - - const students = connection.database - .prepare( - `select userId, firstName, lastName, mail, promotionId from students`, - ) - .all(); - - return new Response( - JSON.stringify({ promotions, students }), - { - status: 200, - headers: { "Content-Type": "application/json" }, - }, - ); - } catch (error) { - console.error("Error fetching data:", error); - return new Response("Failed to fetch data", { status: 500 }); - } - }, - - async POST(request) { - console.log("API /students/api/insert_students called"); - - try { - const body = await request.json(); - const { data, promoName } = body; - - console.log("Received data:", { promoName, data }); - - if (!promoName || !Array.isArray(data)) { - throw new Error("Invalid request body"); - } - - using connection = connect("students"); - - connection.database.prepare( - "INSERT OR IGNORE INTO promotions (name) VALUES (?)", - ).run(promoName); - - const promoIdRow: { id: number } = connection.database - .prepare("SELECT id FROM promotions WHERE name = ?") - .get(promoName)!; - const promoId = promoIdRow.id; - - console.log(`Promotion ID for "${promoName}":`, promoId); - - const insertQuery = connection.database.prepare( - `INSERT INTO students - (userId, firstName, lastName, mail, promotionId) - VALUES (?, ?, ?, ?, ?)`, - ); - - for (const student of data) { - console.log("Inserting student:", student); - insertQuery.run( - student.Identifiant, - student.Nom, - student["Prénom"], - student.Mail, - promoId, - ); - } - - console.log("All data inserted successfully"); - return new Response("Data inserted successfully", { status: 201 }); - } catch (error) { - console.error("Error inserting data:", error); - return new Response("Failed to insert data", { status: 500 }); - } - }, -}; diff --git a/routes/(apps)/students/api/students.ts b/routes/(apps)/students/api/students.ts new file mode 100644 index 0000000..eb1dd2b --- /dev/null +++ b/routes/(apps)/students/api/students.ts @@ -0,0 +1,155 @@ +import { FreshContext, Handlers } from "$fresh/server.ts"; +import connect from "$root/databases/connect.ts"; +import { AuthenticatedState } from "$root/defaults/interfaces.ts"; +import { Database } from "@db/sqlite"; + +/** + * Gets itself from the database. + * @param database The database connection + * @param userId The user ID. + * @returns Itself from the database. + */ +function getItself( + database: Database, + userId: string, +): { student: Student | null; promo: Promotion | null } { + const studentQuery = "select * from students where userId = ?"; + const student: Student | undefined = database.prepare(studentQuery).get( + userId, + ); + + if (!student) { + return { student: null, promo: null }; + } + + const promoQuery = "select * from promotions where id = ?"; + const promo: Promotion | undefined = database.prepare(promoQuery).get( + student.promotionId, + ); + + return { student, promo: promo ?? null }; +} + +/** + * Gets itself from the database. + * @param database The database connection + * @param userId The user ID. + * @returns Itself from the database. + */ +function getAll( + database: Database, + userId: string, +): { student: Student | null; promo: Promotion | null } { + const studentQuery = "select * from students where userId = ?"; + const student: Student | undefined = database.prepare(studentQuery).get( + userId, + ); + + if (!student) { + return { student: null, promo: null }; + } + + const promoQuery = "select * from promotions where id = ?"; + const promo: Promotion | undefined = database.prepare(promoQuery).get( + student.promotionId, + ); + + return { student, promo: promo ?? null }; +} + + +export const handler: Handlers = { + /** + * The students the user can see. + * @param _request The HTTP request. + * @param _context The context with authenticated state. + * @returns All students our user can see. + */ + // deno-lint-ignore require-await + async GET( + _request: Request, + context: FreshContext, + ): Promise { + using connection = connect("students"); + const database = connection.database; + + if (context.state.session.eduPersonPrimaryAffiliation == "student") { + return new Response( + JSON.stringify({ + student: getItself(database, context.state.session.uid), + }), + { + headers: { + "content-type": "application/json", + }, + }, + ); + } + + const promotions = database.prepare("select id, name from promotions") + .all(); + + const students = database.prepare( + "select userId, firstName, lastName, mail, promotionId from students", + ).all(); + + return new Response( + JSON.stringify({ promotions, students }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + }, + ); + }, + + async POST(request) { + console.log("API /students/api/insert_students called"); + + try { + const body = await request.json(); + const { data, promoName } = body; + + console.log("Received data:", { promoName, data }); + + if (!promoName || !Array.isArray(data)) { + throw new Error("Invalid request body"); + } + + using connection = connect("students"); + + connection.database.prepare( + "INSERT OR IGNORE INTO promotions (name) VALUES (?)", + ).run(promoName); + + const promoIdRow: { id: number } = connection.database + .prepare("SELECT id FROM promotions WHERE name = ?") + .get(promoName)!; + const promoId = promoIdRow.id; + + console.log(`Promotion ID for "${promoName}":`, promoId); + + const insertQuery = connection.database.prepare( + `INSERT INTO students + (userId, firstName, lastName, mail, promotionId) + VALUES (?, ?, ?, ?, ?)`, + ); + + for (const student of data) { + console.log("Inserting student:", student); + insertQuery.run( + student.Identifiant, + student.Nom, + student["Prénom"], + student.Mail, + promoId, + ); + } + + console.log("All data inserted successfully"); + return new Response("Data inserted successfully", { status: 201 }); + } catch (error) { + console.error("Error inserting data:", error); + return new Response("Failed to insert data", { status: 500 }); + } + }, +}; diff --git a/routes/(apps)/students/api/types.d.ts b/routes/(apps)/students/api/types.d.ts new file mode 100644 index 0000000..95beeb7 --- /dev/null +++ b/routes/(apps)/students/api/types.d.ts @@ -0,0 +1,13 @@ +interface Student { + userId: string; + firstName: string; + lastName: string; + mail: string; + promotionId: number; +} + +interface Promotion { + id: number; + endyear: number; + current: number; +} diff --git a/routes/(apps)/students/partials/(admin)/consult.tsx b/routes/(apps)/students/partials/(admin)/consult.tsx index e506a9b..c46c2a5 100644 --- a/routes/(apps)/students/partials/(admin)/consult.tsx +++ b/routes/(apps)/students/partials/(admin)/consult.tsx @@ -4,8 +4,7 @@ import { makePartials, } from "$root/defaults/makePartials.tsx"; import { FreshContext } from "$fresh/server.ts"; -import { State } from "$root/routes/_middleware.ts"; -//import EditStudents from "../(_islands)/EditStudents.tsx"; +import { State } from "$root/defaults/interfaces.ts"; // deno-lint-ignore require-await async function Students(_request: Request, _context: FreshContext) { diff --git a/routes/(apps)/students/partials/(admin)/upload.tsx b/routes/(apps)/students/partials/(admin)/upload.tsx index f1b9ef8..d64608a 100644 --- a/routes/(apps)/students/partials/(admin)/upload.tsx +++ b/routes/(apps)/students/partials/(admin)/upload.tsx @@ -4,8 +4,7 @@ import { makePartials, } from "$root/defaults/makePartials.tsx"; import { FreshContext } from "$fresh/server.ts"; -import { State } from "$root/routes/_middleware.ts"; -//import EditStudents from "../(_islands)/EditStudents.tsx"; +import { State } from "$root/defaults/interfaces.ts"; // deno-lint-ignore require-await async function Students(_request: Request, _context: FreshContext) { diff --git a/routes/(apps)/students/partials/index.tsx b/routes/(apps)/students/partials/index.tsx index 2971e0e..a54dff7 100644 --- a/routes/(apps)/students/partials/index.tsx +++ b/routes/(apps)/students/partials/index.tsx @@ -3,7 +3,7 @@ import { makePartials, } from "$root/defaults/makePartials.tsx"; import { FreshContext } from "$fresh/server.ts"; -import { State } from "$root/routes/_middleware.ts"; +import { State } from "$root/defaults/interfaces.ts"; // deno-lint-ignore require-await export async function Index(_request: Request, context: FreshContext) { diff --git a/routes/(apps)/students/partials/overview.tsx b/routes/(apps)/students/partials/overview.tsx deleted file mode 100644 index ceac77c..0000000 --- a/routes/(apps)/students/partials/overview.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Partial } from "$fresh/runtime.ts"; -import { RouteConfig } from "$fresh/server.ts"; - -type ModulesProps = Record; - -export const config: RouteConfig = { - skipAppWrapper: true, - skipInheritedLayouts: true, -}; - -export default function Modules(_props: ModulesProps) { - return ( - - students - - ); -}