Migration de la base de données : SQLite vers PostgreSQL #56

Merged
djalim merged 6 commits from feature/postgresMigration into develop 2026-04-21 10:07:45 +00:00
2 changed files with 133 additions and 214 deletions
Showing only changes of commit 9636242b42 - Show all commits
+61 -113
View File
@@ -1,55 +1,36 @@
import { Handlers } from "$fresh/server.ts"; import { Handlers } from "$fresh/server.ts";
import { Database } from "@db/sqlite"; import { db } from "$root/databases/db.ts";
import { mobility, promotions, students } from "$root/databases/schema.ts";
import { eq } from "npm:drizzle-orm";
export const handler: Handlers = { export const handler: Handlers = {
// deno-lint-ignore require-await
async GET() { async GET() {
try { try {
console.log("Connecting to mobility database..."); const studentRows = await db
const connection = new Database("databases/data/mobility.db", { .select({
create: false, id: students.userId,
}); firstName: students.firstName,
connection.run( lastName: students.lastName,
"ATTACH DATABASE 'databases/data/students.db' AS students", promotionId: students.promotionId,
); endyear: promotions.endyear,
console.log("Connected to databases."); current: promotions.current,
})
.from(students)
.leftJoin(promotions, eq(students.promotionId, promotions.id));
const students = connection.prepare( const mobilityRows = await db.select().from(mobility);
`SELECT
students.userId AS id,
students.firstName,
students.lastName,
students.promotionId AS promotionId,
promotions.name AS promotionName
FROM students.students
LEFT JOIN students.promotions ON students.promotionId = promotions.id`,
).all();
const mobilities = connection.prepare( const promotionRows = await db
`SELECT .select({ id: promotions.id, endyear: promotions.endyear, current: promotions.current })
mobility.id, .from(promotions);
mobility.studentId,
mobility.startDate,
mobility.endDate,
mobility.weeksCount,
mobility.destinationCountry,
mobility.destinationName,
mobility.mobilityStatus
FROM mobility`,
).all();
const promotions = connection.prepare(
`SELECT id, name FROM students.promotions`,
).all();
connection.close();
return new Response( return new Response(
JSON.stringify({ mobilities, students, promotions }), JSON.stringify({
{ mobilities: mobilityRows,
status: 200, students: studentRows,
headers: { "Content-Type": "application/json" }, promotions: promotionRows,
}, }),
{ status: 200, headers: { "Content-Type": "application/json" } },
); );
} catch (error) { } catch (error) {
console.error("Error fetching mobility data:", error); console.error("Error fetching mobility data:", error);
@@ -58,8 +39,6 @@ export const handler: Handlers = {
}, },
async POST(request) { async POST(request) {
console.log("API /mobility/api/insert_mobility POST called");
try { try {
const body = await request.json(); const body = await request.json();
const { data } = body; const { data } = body;
@@ -67,32 +46,8 @@ export const handler: Handlers = {
if (!Array.isArray(data)) { if (!Array.isArray(data)) {
throw new Error("Invalid request body"); throw new Error("Invalid request body");
} }
console.log("Connecting to mobility database...");
const connection = new Database("databases/data/mobility.db", {
create: false,
});
console.log("Attaching students database..."); for (const entry of data) {
connection.run(
"ATTACH DATABASE 'databases/data/students.db' AS students",
);
console.log("Students database attached successfully.");
const insertQuery = connection.prepare(
`INSERT INTO mobility (
id, studentId, startDate, endDate, weeksCount, destinationCountry, destinationName, mobilityStatus
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
startDate = excluded.startDate,
endDate = excluded.endDate,
weeksCount = excluded.weeksCount,
destinationCountry = excluded.destinationCountry,
destinationName = excluded.destinationName,
mobilityStatus = excluded.mobilityStatus`,
);
for (const mobility of data) {
const { const {
id, id,
studentId, studentId,
@@ -102,19 +57,16 @@ export const handler: Handlers = {
destinationCountry, destinationCountry,
destinationName, destinationName,
mobilityStatus = "N/A", mobilityStatus = "N/A",
} = mobility; } = entry;
console.log("Processing mobility data:", mobility); const studentExists = await db
.select({ userId: students.userId })
.from(students)
.where(eq(students.userId, studentId))
.limit(1)
.then((rows) => rows.length > 0);
const studentExists = connection if (!studentExists) {
.prepare(
`SELECT COUNT(*) AS count FROM students.students WHERE userId = ?`,
)
.get(studentId);
console.log(`Student ${studentId} exists:`, studentExists.count > 0);
if (studentExists.count === 0) {
console.warn(`Skipping mobility for unknown studentId: ${studentId}`); console.warn(`Skipping mobility for unknown studentId: ${studentId}`);
continue; continue;
} }
@@ -123,43 +75,39 @@ export const handler: Handlers = {
if (startDate && endDate) { if (startDate && endDate) {
const start = new Date(startDate); const start = new Date(startDate);
const end = new Date(endDate); const end = new Date(endDate);
if (start <= end) { calculatedWeeksCount = start <= end
calculatedWeeksCount = Math.ceil( ? Math.ceil(
(end.getTime() - start.getTime()) / (7 * 24 * 60 * 60 * 1000), (end.getTime() - start.getTime()) / (7 * 24 * 60 * 60 * 1000),
); )
} else { : null;
calculatedWeeksCount = null;
}
} }
console.log("Executing SQL insert/update query for:", { await db
id, .insert(mobility)
studentId, .values({
startDate, id,
endDate, studentId,
calculatedWeeksCount, startDate,
destinationCountry, endDate,
destinationName, weeksCount: calculatedWeeksCount,
mobilityStatus, destinationCountry,
}); destinationName,
mobilityStatus,
insertQuery.run( })
id, .onConflictDoUpdate({
studentId, target: mobility.id,
startDate, set: {
endDate, startDate,
calculatedWeeksCount, endDate,
destinationCountry, weeksCount: calculatedWeeksCount,
destinationName, destinationCountry,
mobilityStatus, destinationName,
); mobilityStatus,
},
});
} }
connection.close(); return new Response("Data inserted/updated successfully", { status: 200 });
console.log("Mobility data inserted/updated successfully.");
return new Response("Data inserted/updated successfully", {
status: 200,
});
} catch (error) { } catch (error) {
console.error("Error inserting mobility data:", error); console.error("Error inserting mobility data:", error);
return new Response("Failed to insert/update data", { status: 500 }); return new Response("Failed to insert/update data", { status: 500 });
+72 -101
View File
@@ -1,150 +1,121 @@
import { FreshContext, Handlers } from "$fresh/server.ts"; import { FreshContext, Handlers } from "$fresh/server.ts";
import connect from "$root/databases/connect.ts"; import { db } from "$root/databases/db.ts";
import { promotions, students } from "$root/databases/schema.ts";
import { AuthenticatedState } from "$root/defaults/interfaces.ts"; import { AuthenticatedState } from "$root/defaults/interfaces.ts";
import { Database } from "@db/sqlite"; import { eq, lt } from "npm:drizzle-orm";
/** async function getItself(
* 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, userId: string,
): { student: Student | null; promo: Promotion | null } { ): Promise<{ student: Student | null; promo: Promotion | null }> {
const studentQuery = "select * from students where userId = ?"; const student = await db
const student: Student | undefined = database.prepare(studentQuery).get( .select()
userId, .from(students)
); .where(eq(students.userId, userId))
.limit(1)
.then((rows) => rows[0] ?? null);
if (!student) { if (!student) {
return { student: null, promo: null }; return { student: null, promo: null };
} }
const promoQuery = "select * from promotions where id = ?"; const promo = await db
const promo: Promotion | undefined = database.prepare(promoQuery).get( .select()
student.promotionId, .from(promotions)
); .where(eq(promotions.id, student.promotionId!))
.limit(1)
.then((rows) => rows[0] ?? null);
return { student, promo: promo ?? null }; return { student, promo };
} }
/** async function getAll(): Promise<
* Gets itself from the database. { students: Student[]; promos: Promotion[] }
* @param database The database connexion > {
* @param userId The user ID. const rows = await db
* @returns Itself from the database. .select({
*/ userId: students.userId,
function getAll( firstName: students.firstName,
database: Database, lastName: students.lastName,
): { students: Student[]; promos: Promotion[] } { mail: students.mail,
const studentsQuery = ` promotionId: students.promotionId,
select userId, firstName, lastName, mail, promotionId })
from students inner join promotions .from(students)
on students.promotionId = promotions.id .innerJoin(promotions, eq(students.promotionId, promotions.id))
where promotions.current < 6`; .where(lt(promotions.current, 6));
const students: Student[] = database.prepare(studentsQuery).all();
const promosQuery = "select * from promotions where promotions.current < 6"; const promos = await db
const promos: Promotion[] | undefined = database.prepare(promosQuery).all(); .select()
.from(promotions)
.where(lt(promotions.current, 6));
return { students, promos }; return { students: rows as Student[], promos };
} }
/** async function addStudents(
* Add users to the database. studentList: Student[],
* @param database The database connexion promoId: number,
* @param students The students to add ): Promise<void> {
* @param promoId The promotion id. for (const student of studentList) {
*/ await db
function addStudents(database: Database, students: Student[], promoId: string) { .insert(students)
const query = ` .values({
INSERT INTO students userId: student.userId,
(userId, firstName, lastName, mail, promotionId) firstName: student.firstName,
VALUES (?, ?, ?, ?, ?)`; lastName: student.lastName,
mail: student.mail,
const statement = database.prepare(query); promotionId: promoId,
})
for (const student of students) { .onConflictDoNothing();
statement.run(
student.userId,
student.firstName,
student.lastName,
student.mail,
promoId,
);
} }
} }
export const handler: Handlers<null, AuthenticatedState> = { export const handler: Handlers<null, AuthenticatedState> = {
/**
* 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( async GET(
_request: Request, _request: Request,
context: FreshContext<AuthenticatedState>, context: FreshContext<AuthenticatedState>,
): Promise<Response> { ): Promise<Response> {
using connection = connect("students");
const database = connection.database;
if (context.state.session.eduPersonPrimaryAffiliation == "student") { if (context.state.session.eduPersonPrimaryAffiliation == "student") {
return new Response( return new Response(
JSON.stringify(getItself(database, context.state.session.uid)), JSON.stringify(await getItself(context.state.session.uid)),
{ { headers: { "content-type": "application/json" } },
headers: {
"content-type": "application/json",
},
},
); );
} }
return new Response( return new Response(
JSON.stringify(getAll(database)), JSON.stringify(await getAll()),
{ { headers: { "content-type": "application/json" } },
headers: {
"content-type": "application/json",
},
},
); );
}, },
/**
* Add students in the database.
* @param request The HTTP request.
* @param _context The Fresh context.
* @returns HTTP 201 on successful insert.
*/
async POST( async POST(
request: Request, request: Request,
_context: FreshContext<AuthenticatedState>, _context: FreshContext<AuthenticatedState>,
): Promise<Response> { ): Promise<Response> {
const { students, promo }: { students: Student[]; promo: string } = const { students: studentList, promo }: {
await request.json(); students: Student[];
promo: string;
} = await request.json();
if (!promo || !promo.match(/^\d{4}-\dA$/) || !Array.isArray(students)) { if (!promo || !promo.match(/^\d{4}-\dA$/) || !Array.isArray(studentList)) {
return new Response(null, { status: 400 }); return new Response(null, { status: 400 });
} }
using connection = connect("students");
const database = connection.database;
const { endyear, current } = promo.match( const { endyear, current } = promo.match(
/^(?<endyear>\d{4})-(?<current>\d)A$/, /^(?<endyear>\d{4})-(?<current>\d)A$/,
)?.groups!; )?.groups!;
database.prepare( await db
"insert or ignore into promotions (endyear, current) values (?, ?)", .insert(promotions)
).run(endyear, current); .values({ endyear: Number(endyear), current: Number(current) })
.onConflictDoNothing();
const { id: promoId }: { id: string } = database const promo_row = await db
.prepare("select id from promotions where endyear = ? and current = ?") .select()
.get(endyear, current)!; .from(promotions)
.where(eq(promotions.endyear, Number(endyear)))
.then((rows) => rows.find((r) => r.current === Number(current))!);
addStudents(database, students, promoId); await addStudents(studentList, promo_row.id);
return new Response(null, { status: 201 }); return new Response(null, { status: 201 });
}, },