mirror of
https://github.com/dd060606/WebAurion-API.git
synced 2026-01-18 16:47:26 +01:00
fix: rename schedule to planning
feat: NotesAPI
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
import {
|
||||
getJSFFormParams,
|
||||
getViewState,
|
||||
scheduleResponseToEvents,
|
||||
} from "../utils/AurionUtils";
|
||||
import { getViewState } from "../utils/AurionUtils";
|
||||
import { getNotesFromResponse } from "../utils/NotesUtils";
|
||||
import Session from "./Session";
|
||||
|
||||
class NotesApi {
|
||||
@@ -12,11 +9,12 @@ class NotesApi {
|
||||
}
|
||||
|
||||
// Récupération des notes
|
||||
public fetchNotes(): Promise<void> {
|
||||
return new Promise<void>(async (resolve, reject) => {
|
||||
public fetchNotes(): Promise<NotesList[]> {
|
||||
return new Promise<NotesList[]>(async (resolve, reject) => {
|
||||
try {
|
||||
// On part depuis la page planning pour pouvoir finalement accéder à la page des notes
|
||||
const schedulePage = await this.session.sendGET<string>(
|
||||
"/faces/LearnerNotationListPage.xhtml",
|
||||
"/faces/Planning.xhtml",
|
||||
);
|
||||
let viewState = getViewState(schedulePage);
|
||||
if (viewState) {
|
||||
@@ -33,8 +31,7 @@ class NotesApi {
|
||||
const response = await this.session.sendGET<string>(
|
||||
"faces/LearnerNotationListPage.xhtml",
|
||||
);
|
||||
console.log(response);
|
||||
resolve();
|
||||
resolve(getNotesFromResponse(response));
|
||||
} else {
|
||||
reject(new Error("Viewstate not found"));
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import {
|
||||
getJSFFormParams,
|
||||
getViewState,
|
||||
scheduleResponseToEvents,
|
||||
} from "../utils/AurionUtils";
|
||||
import { getJSFFormParams, getViewState } from "../utils/AurionUtils";
|
||||
import { planningResponseToEvents } from "../utils/PlanningUtils";
|
||||
import Session from "./Session";
|
||||
|
||||
class ScheduleApi {
|
||||
class PlanningApi {
|
||||
private session: Session;
|
||||
constructor(session: Session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
// Récupération de l'emploi du temps en fonction de la date de début et de fin (timestamps en millisecondes)
|
||||
public fetchSchedule(startDate?: string): Promise<ScheduleEvent[]> {
|
||||
return new Promise<ScheduleEvent[]>(async (resolve, reject) => {
|
||||
public fetchPlanning(startDate?: string): Promise<PlanningEvent[]> {
|
||||
return new Promise<PlanningEvent[]>(async (resolve, reject) => {
|
||||
try {
|
||||
const schedulePage = await this.session.sendGET<string>(
|
||||
"/faces/Planning.xhtml",
|
||||
@@ -43,19 +40,22 @@ class ScheduleApi {
|
||||
const endDate = startDate + 6 * 24 * 60 * 60 * 1000;
|
||||
params.append("form:j_idt118_end", endDate);
|
||||
} else {
|
||||
// On récupère le timestamp du lundi de la semaine en cours
|
||||
const currentDate = new Date();
|
||||
const currentDay = currentDate.getDay();
|
||||
const daysUntilMonday =
|
||||
(currentDay === 0 ? 1 : 8) - currentDay;
|
||||
currentDate.setDate(
|
||||
currentDate.getDate() + daysUntilMonday,
|
||||
);
|
||||
currentDate.setHours(0, 0, 0, 0);
|
||||
const startTimestamp = currentDate.getTime();
|
||||
// On fixe la date de fin à une semaine après
|
||||
const endTimestamp =
|
||||
startTimestamp + 6 * 24 * 60 * 60 * 1000;
|
||||
const now = new Date();
|
||||
// Obtenir le jour actuel (0 = dimanche, 1 = lundi, ..., 6 = samedi)
|
||||
let day = now.getDay();
|
||||
// Calculer la différence pour atteindre lundi (0 = dimanche => 1 jour pour atteindre lundi)
|
||||
const daysToMonday = day === 0 ? 1 : 1 - day;
|
||||
// Créer la date de début (lundi 6h00)
|
||||
const startDate = new Date(now);
|
||||
startDate.setDate(now.getDate() + daysToMonday); // Passer au lundi
|
||||
startDate.setHours(6, 0, 0, 0); // Fixer à 6h00
|
||||
// Date de fin (6 jours après la date de début)
|
||||
const endDate = new Date(startDate);
|
||||
endDate.setDate(startDate.getDate() + 6); // Ajouter 6 jours
|
||||
// Convertir les dates en timestamp
|
||||
const startTimestamp = startDate.getTime();
|
||||
const endTimestamp = endDate.getTime();
|
||||
|
||||
params.append(
|
||||
"form:j_idt118_start",
|
||||
startTimestamp.toString(),
|
||||
@@ -70,7 +70,7 @@ class ScheduleApi {
|
||||
"faces/Planning.xhtml",
|
||||
params,
|
||||
);
|
||||
resolve(scheduleResponseToEvents(response));
|
||||
resolve(planningResponseToEvents(response));
|
||||
} else {
|
||||
reject(new Error("Viewstate not found"));
|
||||
}
|
||||
@@ -81,4 +81,4 @@ class ScheduleApi {
|
||||
}
|
||||
}
|
||||
|
||||
export default ScheduleApi;
|
||||
export default PlanningApi;
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios, { AxiosInstance } from "axios";
|
||||
import ScheduleApi from "./ScheduleApi";
|
||||
import PlanningApi from "./PlanningApi";
|
||||
import { getJSFFormParams, getViewState } from "../utils/AurionUtils";
|
||||
import NotesApi from "./NotesApi";
|
||||
|
||||
@@ -17,8 +17,8 @@ class Session {
|
||||
}
|
||||
|
||||
// API pour le calendrier
|
||||
public getScheduleApi(): ScheduleApi {
|
||||
return new ScheduleApi(this);
|
||||
public getPlanningApi(): PlanningApi {
|
||||
return new PlanningApi(this);
|
||||
}
|
||||
// API pour les notes
|
||||
public getNotesApi(): NotesApi {
|
||||
|
||||
@@ -14,52 +14,6 @@ export function getViewState(html: string): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Conversion du calendrier au format JSON
|
||||
export function getJSONSchedule(xml: string): object {
|
||||
const parser = load(xml, {
|
||||
xmlMode: true,
|
||||
});
|
||||
const json = parser('update[id="form:j_idt118"]').text();
|
||||
return JSON.parse(json)["events"];
|
||||
}
|
||||
|
||||
// On convertit la réponse du serveur XML en cours du planning
|
||||
export function scheduleResponseToEvents(response: string): ScheduleEvent[] {
|
||||
const json: any = getJSONSchedule(response);
|
||||
|
||||
return json.map((event: any) => {
|
||||
// On récupère les informations des cours
|
||||
const eventInfo = event.title.split(" - ");
|
||||
|
||||
let room = eventInfo[1].trim();
|
||||
// Pour les matières qui ne sont pas bien formatées par défaut...
|
||||
let subject = "";
|
||||
let title = "";
|
||||
if (eventInfo.length >= 9) {
|
||||
subject = eventInfo[eventInfo.length - 6].trim();
|
||||
title = eventInfo[eventInfo.length - 4].trim();
|
||||
} else {
|
||||
subject = eventInfo[eventInfo.length - 4].trim();
|
||||
title = eventInfo[eventInfo.length - 3].trim();
|
||||
}
|
||||
|
||||
let instructors = eventInfo[eventInfo.length - 2].trim();
|
||||
let learners = eventInfo[eventInfo.length - 1].trim();
|
||||
|
||||
return {
|
||||
id: event.id,
|
||||
title,
|
||||
subject,
|
||||
room,
|
||||
instructors,
|
||||
learners,
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
className: event.className,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Paramètres nécessaires pour effectuer une requête avec le backend Java Server Faces (JSF)
|
||||
// Form ID : ID du formulaire (Récupérable avec BurpSuite / Inspecteur de requêtes)
|
||||
// Render ID : ID de l'élément à mettre à jour (Récupérable avec BurpSuite / Inspecteur de requêtes)
|
||||
|
||||
70
src/utils/NotesUtils.ts
Normal file
70
src/utils/NotesUtils.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { load } from "cheerio";
|
||||
|
||||
export function getNotesFromResponse(htmlReponse: string): NotesList[] {
|
||||
// On parcourt le tableau des notes
|
||||
const parser = load(htmlReponse);
|
||||
const table = parser("table tbody");
|
||||
const notes: Note[] = [];
|
||||
//On récupère chaque notes avec les informations associées
|
||||
table.find("tr").each((index, element) => {
|
||||
const note: Note = {
|
||||
date: "",
|
||||
code: "",
|
||||
subject: "",
|
||||
note: "",
|
||||
absence: "",
|
||||
description: "",
|
||||
instructor: "",
|
||||
};
|
||||
const fields = [
|
||||
"date",
|
||||
"code",
|
||||
"subject",
|
||||
"note",
|
||||
"absence",
|
||||
"description",
|
||||
"instructor",
|
||||
];
|
||||
// On construit l'objet note avec les informations de la ligne
|
||||
load(element)("td").each((index, element) => {
|
||||
let value = load(element).text().trim();
|
||||
// S'il s'agit du code de la note, on le formate
|
||||
if (index === 1) {
|
||||
// On supprime le dernier _DS+Numéro afin de créer un code commun avec les notes d'une même matière
|
||||
value = value.replace(/_DS\d$/, "");
|
||||
}
|
||||
note[fields[index]] = value;
|
||||
});
|
||||
notes.push(note);
|
||||
});
|
||||
// On regroupe les notes par matière
|
||||
const notesByCode: NotesList[] = [];
|
||||
notes.forEach((note) => {
|
||||
// On regroupe par code de matière
|
||||
const code = note.code;
|
||||
const existingNote = notesByCode.find((n) => n.code === code);
|
||||
if (existingNote) {
|
||||
existingNote.notes.push(note);
|
||||
} else {
|
||||
notesByCode.push({
|
||||
code,
|
||||
notes: [note],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return notesByCode;
|
||||
}
|
||||
|
||||
// On calcule la moyenne d'une liste de notes
|
||||
export function noteAverage(note: Note[]): number {
|
||||
let sum = 0;
|
||||
let count = 0;
|
||||
note.forEach((n) => {
|
||||
if (n.note !== "") {
|
||||
sum += parseFloat(n.note);
|
||||
count++;
|
||||
}
|
||||
});
|
||||
return sum / count;
|
||||
}
|
||||
47
src/utils/PlanningUtils.ts
Normal file
47
src/utils/PlanningUtils.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { load } from "cheerio";
|
||||
|
||||
// Conversion du calendrier au format JSON
|
||||
export function getJSONSchedule(xml: string): object {
|
||||
const parser = load(xml, {
|
||||
xmlMode: true,
|
||||
});
|
||||
const json = parser('update[id="form:j_idt118"]').text();
|
||||
return JSON.parse(json)["events"];
|
||||
}
|
||||
|
||||
// On convertit la réponse du serveur XML en cours du planning
|
||||
export function planningResponseToEvents(response: string): PlanningEvent[] {
|
||||
const json: any = getJSONSchedule(response);
|
||||
|
||||
return json.map((event: any) => {
|
||||
// On récupère les informations des cours
|
||||
const eventInfo = event.title.split(" - ");
|
||||
|
||||
let room = eventInfo[1].trim();
|
||||
// Pour les matières qui ne sont pas bien formatées par défaut...
|
||||
let subject = "";
|
||||
let title = "";
|
||||
if (eventInfo.length >= 9) {
|
||||
subject = eventInfo[eventInfo.length - 6].trim();
|
||||
title = eventInfo[eventInfo.length - 4].trim();
|
||||
} else {
|
||||
subject = eventInfo[eventInfo.length - 4].trim();
|
||||
title = eventInfo[eventInfo.length - 3].trim();
|
||||
}
|
||||
|
||||
let instructors = eventInfo[eventInfo.length - 2].trim();
|
||||
let learners = eventInfo[eventInfo.length - 1].trim();
|
||||
|
||||
return {
|
||||
id: event.id,
|
||||
title,
|
||||
subject,
|
||||
room,
|
||||
instructors,
|
||||
learners,
|
||||
start: event.start,
|
||||
end: event.end,
|
||||
className: event.className,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
type ScheduleEvent = {
|
||||
type PlanningEvent = {
|
||||
id: string;
|
||||
title: string;
|
||||
subject: string;
|
||||
@@ -9,3 +9,17 @@ type ScheduleEvent = {
|
||||
end: string;
|
||||
className: string;
|
||||
};
|
||||
type Note = {
|
||||
date: string;
|
||||
code: string;
|
||||
subject: string;
|
||||
note: string;
|
||||
absence: string;
|
||||
description: string;
|
||||
instructor: string;
|
||||
[key: string]: string;
|
||||
};
|
||||
type NotesList = {
|
||||
code: string;
|
||||
notes: Note[];
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { login } from "../src/api/Session";
|
||||
import { noteAverage } from "../src/utils/NotesUtils";
|
||||
describe("NotesApi", () => {
|
||||
it("should receive notes", async () => {
|
||||
const username = process.env.TEST_USERNAME;
|
||||
@@ -10,7 +11,12 @@ describe("NotesApi", () => {
|
||||
}
|
||||
|
||||
const session = await login(username, password);
|
||||
const schedule = await session.getNotesApi().fetchNotes();
|
||||
expect(schedule).not.toBeDefined();
|
||||
const notes = await session.getNotesApi().fetchNotes();
|
||||
console.log(JSON.stringify(notes, null, 2));
|
||||
console.log("Les moyennes: ");
|
||||
notes.forEach((note) => {
|
||||
console.log(note.code + ": " + noteAverage(note.notes));
|
||||
});
|
||||
expect(notes).toBeInstanceOf(Array);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { login } from "../src/api/Session";
|
||||
describe("ScheduleApi", () => {
|
||||
it("should receive a schedule", async () => {
|
||||
describe("PlanningApi", () => {
|
||||
it("should receive a planning", async () => {
|
||||
const username = process.env.TEST_USERNAME;
|
||||
const password = process.env.TEST_PASSWORD;
|
||||
if (!username || !password) {
|
||||
@@ -10,7 +10,8 @@ describe("ScheduleApi", () => {
|
||||
}
|
||||
|
||||
const session = await login(username, password);
|
||||
const schedule = await session.getScheduleApi().fetchSchedule();
|
||||
expect(schedule).toBeInstanceOf(Array);
|
||||
const planning = await session.getPlanningApi().fetchPlanning();
|
||||
console.log(planning);
|
||||
expect(planning).toBeInstanceOf(Array);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user