Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f71128a7f3 | |||
| 720a380be8 | |||
| 6c602cb10a | |||
| bb09c1cce5 | |||
| f162fcaadc |
@@ -7,4 +7,5 @@ INSERT INTO "permissions" ("id", "nom") VALUES
|
|||||||
('module_read', 'Consulter les modules et enseignements'),
|
('module_read', 'Consulter les modules et enseignements'),
|
||||||
('module_write', 'Gérer les modules et enseignements'),
|
('module_write', 'Gérer les modules et enseignements'),
|
||||||
('user_read', 'Consulter les utilisateurs et leurs rôles'),
|
('user_read', 'Consulter les utilisateurs et leurs rôles'),
|
||||||
('user_write', 'Gérer les utilisateurs et leurs rôles');
|
('user_write', 'Gérer les utilisateurs et leurs rôles'),
|
||||||
|
('role_write', 'Gérer les rôles et leurs permissions');
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ INSERT INTO "permissions" ("id", "nom") VALUES
|
|||||||
('module_read', 'Consulter les modules et enseignements'),
|
('module_read', 'Consulter les modules et enseignements'),
|
||||||
('module_write', 'Gérer les modules et enseignements'),
|
('module_write', 'Gérer les modules et enseignements'),
|
||||||
('user_read', 'Consulter les utilisateurs et leurs rôles'),
|
('user_read', 'Consulter les utilisateurs et leurs rôles'),
|
||||||
('user_write', 'Gérer les utilisateurs et leurs rôles')
|
('user_write', 'Gérer les utilisateurs et leurs rôles'),
|
||||||
|
('role_write', 'Gérer les rôles et leurs permissions')
|
||||||
ON CONFLICT ("id") DO UPDATE SET "nom" = EXCLUDED."nom";
|
ON CONFLICT ("id") DO UPDATE SET "nom" = EXCLUDED."nom";
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
|
||||||
type Student = { numEtud: number; nom: string; prenom: string; idPromo: string };
|
type Student = {
|
||||||
|
numEtud: number;
|
||||||
|
nom: string;
|
||||||
|
prenom: string;
|
||||||
|
idPromo: string;
|
||||||
|
};
|
||||||
type UE = { id: number; nom: string };
|
type UE = { id: number; nom: string };
|
||||||
type UEModule = { idModule: string; idUE: number; idPromo: string; coeff: number };
|
type UEModule = {
|
||||||
|
idModule: string;
|
||||||
|
idUE: number;
|
||||||
|
idPromo: string;
|
||||||
|
coeff: number;
|
||||||
|
};
|
||||||
type Module = { id: string; nom: string };
|
type Module = { id: string; nom: string };
|
||||||
type Note = { numEtud: number; idModule: string; note: number };
|
type Note = { numEtud: number; idModule: string; note: number };
|
||||||
type Ajustement = { numEtud: number; idUE: number; valeur: number };
|
type Ajustement = { numEtud: number; idUE: number; valeur: number };
|
||||||
@@ -202,9 +212,7 @@ export default function NoteRecap({ numEtud }: Props) {
|
|||||||
return (
|
return (
|
||||||
<div key={ue.id} class="edit-section">
|
<div key={ue.id} class="edit-section">
|
||||||
{/* UE header */}
|
{/* UE header */}
|
||||||
<div
|
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; flex-wrap: wrap">
|
||||||
style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; flex-wrap: wrap"
|
|
||||||
>
|
|
||||||
<p class="edit-section-title" style="margin: 0">{ue.nom}</p>
|
<p class="edit-section-title" style="margin: 0">{ue.nom}</p>
|
||||||
{avg !== null && (
|
{avg !== null && (
|
||||||
<span class={noteClass(avg)} style="font-size: 0.78rem">
|
<span class={noteClass(avg)} style="font-size: 0.78rem">
|
||||||
@@ -254,9 +262,7 @@ export default function NoteRecap({ numEtud }: Props) {
|
|||||||
</span>
|
</span>
|
||||||
{isEditing
|
{isEditing
|
||||||
? (
|
? (
|
||||||
<div
|
<div style="display: flex; align-items: center; gap: 0.25rem">
|
||||||
style="display: flex; align-items: center; gap: 0.25rem"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
class="form-input"
|
class="form-input"
|
||||||
style="width: 5rem; text-align: center; font-size: 0.85rem"
|
style="width: 5rem; text-align: center; font-size: 0.85rem"
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// @deno-types="https://cdn.sheetjs.com/xlsx-0.20.3/package/types/index.d.ts"
|
||||||
|
import * as XLSX from "https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs";
|
||||||
|
import { Handlers } from "$fresh/server.ts";
|
||||||
|
import { db } from "../../../../../databases/db.ts";
|
||||||
|
import { notes } from "../../../../../databases/schema.ts";
|
||||||
|
|
||||||
|
export const handler: Handlers = {
|
||||||
|
//# 44 POST /notes/import-xlsx
|
||||||
|
async POST(request) {
|
||||||
|
try {
|
||||||
|
const formData = await request.formData();
|
||||||
|
const file = formData.get("file");
|
||||||
|
const idModule = formData.get("idModule");
|
||||||
|
|
||||||
|
if (!file || !(file instanceof File)) {
|
||||||
|
return new Response("Champ 'file' manquant", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!idModule || typeof idModule !== "string") {
|
||||||
|
return new Response("Champ 'idModule' manquant", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = await file.arrayBuffer();
|
||||||
|
const workbook = XLSX.read(buffer);
|
||||||
|
const sheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||||
|
const rows = XLSX.utils.sheet_to_json(sheet) as {
|
||||||
|
numEtud: number;
|
||||||
|
note: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const { numEtud, note } = row;
|
||||||
|
|
||||||
|
if (!numEtud || note === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.insert(notes)
|
||||||
|
.values({ numEtud, idModule, note })
|
||||||
|
.onConflictDoUpdate({
|
||||||
|
target: [notes.numEtud, notes.idModule],
|
||||||
|
set: { note },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(null, { status: 204 });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error importing notes:", error);
|
||||||
|
return new Response("Failed to import notes", { status: 500 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,24 +1,40 @@
|
|||||||
// #115 - E2E tests for GET /permissions
|
// #115 - E2E tests for GET /permissions
|
||||||
// Handler statique (pas de DB), test direct du handler
|
|
||||||
|
|
||||||
import { assertEquals, assertExists } from "@std/assert";
|
import { assertEquals, assertExists } from "@std/assert";
|
||||||
import { makeEmployeeContext, makeGetRequest } from "../helpers/handler.ts";
|
import { makeEmployeeContext, makeGetRequest } from "../helpers/handler.ts";
|
||||||
|
import { seedPermissions, truncateAll } from "../helpers/db_integration.ts";
|
||||||
import { handler as permissionsHandler } from "$apps/admin/api/permissions.ts";
|
import { handler as permissionsHandler } from "$apps/admin/api/permissions.ts";
|
||||||
|
|
||||||
|
const PERMISSIONS = [
|
||||||
|
{ id: "note_read", nom: "Consulter les notes des étudiants" },
|
||||||
|
{ id: "note_write", nom: "Saisir et modifier les notes" },
|
||||||
|
{ id: "student_read", nom: "Consulter la liste des étudiants" },
|
||||||
|
{
|
||||||
|
id: "student_write",
|
||||||
|
nom: "Gérer les étudiants (ajout, modification, suppression)",
|
||||||
|
},
|
||||||
|
{ id: "module_read", nom: "Consulter les modules et enseignements" },
|
||||||
|
{ id: "module_write", nom: "Gérer les modules et enseignements" },
|
||||||
|
{ id: "user_read", nom: "Consulter les utilisateurs et leurs rôles" },
|
||||||
|
{ id: "user_write", nom: "Gérer les utilisateurs et leurs rôles" },
|
||||||
|
{ id: "role_write", nom: "Gérer les rôles et leurs permissions" },
|
||||||
|
];
|
||||||
|
|
||||||
Deno.test({
|
Deno.test({
|
||||||
name: "e2e permissions: GET /permissions returns all 9 permissions",
|
name: "e2e permissions: GET /permissions returns all 9 permissions",
|
||||||
fn() {
|
async fn() {
|
||||||
const res = permissionsHandler.GET!(
|
await truncateAll();
|
||||||
|
await seedPermissions(PERMISSIONS);
|
||||||
|
const res = await permissionsHandler.GET!(
|
||||||
makeGetRequest("/permissions"),
|
makeGetRequest("/permissions"),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
assertEquals(res.status, 200);
|
assertEquals(res.status, 200);
|
||||||
return res.text().then((text) => {
|
const text = await res.text();
|
||||||
const data = JSON.parse(text);
|
const data = JSON.parse(text);
|
||||||
assertEquals(data.length, 9);
|
assertEquals(data.length, 9);
|
||||||
assertExists(data.find((p: { id: string }) => p.id === "student_read"));
|
assertExists(data.find((p: { id: string }) => p.id === "student_read"));
|
||||||
assertExists(data.find((p: { id: string }) => p.id === "role_write"));
|
assertExists(data.find((p: { id: string }) => p.id === "role_write"));
|
||||||
});
|
|
||||||
},
|
},
|
||||||
sanitizeResources: false,
|
sanitizeResources: false,
|
||||||
sanitizeOps: false,
|
sanitizeOps: false,
|
||||||
@@ -27,7 +43,9 @@ Deno.test({
|
|||||||
Deno.test({
|
Deno.test({
|
||||||
name: "e2e permissions: GET /permissions - all entries have id and nom",
|
name: "e2e permissions: GET /permissions - all entries have id and nom",
|
||||||
async fn() {
|
async fn() {
|
||||||
const res = permissionsHandler.GET!(
|
await truncateAll();
|
||||||
|
await seedPermissions(PERMISSIONS);
|
||||||
|
const res = await permissionsHandler.GET!(
|
||||||
makeGetRequest("/permissions"),
|
makeGetRequest("/permissions"),
|
||||||
makeEmployeeContext(),
|
makeEmployeeContext(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -111,3 +111,9 @@ export async function seedAjustements(
|
|||||||
): Promise<typeof schema.ajustements.$inferSelect[]> {
|
): Promise<typeof schema.ajustements.$inferSelect[]> {
|
||||||
return await testDb.insert(schema.ajustements).values(rows).returning();
|
return await testDb.insert(schema.ajustements).values(rows).returning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function seedPermissions(
|
||||||
|
rows: { id: string; nom: string }[],
|
||||||
|
): Promise<typeof schema.permissions.$inferSelect[]> {
|
||||||
|
return await testDb.insert(schema.permissions).values(rows).returning();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user