fix: rename schedule to planning

feat: NotesAPI
This commit is contained in:
dd060606
2024-11-19 09:22:33 +01:00
parent 23449c26d2
commit 243ff072df
9 changed files with 178 additions and 89 deletions

View File

@@ -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"));
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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
View 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;
}

View 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,
};
});
}

View File

@@ -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[];
};

View File

@@ -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);
});
});

View File

@@ -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);
});
});