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 {
|
import { getViewState } from "../utils/AurionUtils";
|
||||||
getJSFFormParams,
|
import { getNotesFromResponse } from "../utils/NotesUtils";
|
||||||
getViewState,
|
|
||||||
scheduleResponseToEvents,
|
|
||||||
} from "../utils/AurionUtils";
|
|
||||||
import Session from "./Session";
|
import Session from "./Session";
|
||||||
|
|
||||||
class NotesApi {
|
class NotesApi {
|
||||||
@@ -12,11 +9,12 @@ class NotesApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Récupération des notes
|
// Récupération des notes
|
||||||
public fetchNotes(): Promise<void> {
|
public fetchNotes(): Promise<NotesList[]> {
|
||||||
return new Promise<void>(async (resolve, reject) => {
|
return new Promise<NotesList[]>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
// On part depuis la page planning pour pouvoir finalement accéder à la page des notes
|
||||||
const schedulePage = await this.session.sendGET<string>(
|
const schedulePage = await this.session.sendGET<string>(
|
||||||
"/faces/LearnerNotationListPage.xhtml",
|
"/faces/Planning.xhtml",
|
||||||
);
|
);
|
||||||
let viewState = getViewState(schedulePage);
|
let viewState = getViewState(schedulePage);
|
||||||
if (viewState) {
|
if (viewState) {
|
||||||
@@ -33,8 +31,7 @@ class NotesApi {
|
|||||||
const response = await this.session.sendGET<string>(
|
const response = await this.session.sendGET<string>(
|
||||||
"faces/LearnerNotationListPage.xhtml",
|
"faces/LearnerNotationListPage.xhtml",
|
||||||
);
|
);
|
||||||
console.log(response);
|
resolve(getNotesFromResponse(response));
|
||||||
resolve();
|
|
||||||
} else {
|
} else {
|
||||||
reject(new Error("Viewstate not found"));
|
reject(new Error("Viewstate not found"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import {
|
import { getJSFFormParams, getViewState } from "../utils/AurionUtils";
|
||||||
getJSFFormParams,
|
import { planningResponseToEvents } from "../utils/PlanningUtils";
|
||||||
getViewState,
|
|
||||||
scheduleResponseToEvents,
|
|
||||||
} from "../utils/AurionUtils";
|
|
||||||
import Session from "./Session";
|
import Session from "./Session";
|
||||||
|
|
||||||
class ScheduleApi {
|
class PlanningApi {
|
||||||
private session: Session;
|
private session: Session;
|
||||||
constructor(session: Session) {
|
constructor(session: Session) {
|
||||||
this.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)
|
// 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[]> {
|
public fetchPlanning(startDate?: string): Promise<PlanningEvent[]> {
|
||||||
return new Promise<ScheduleEvent[]>(async (resolve, reject) => {
|
return new Promise<PlanningEvent[]>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const schedulePage = await this.session.sendGET<string>(
|
const schedulePage = await this.session.sendGET<string>(
|
||||||
"/faces/Planning.xhtml",
|
"/faces/Planning.xhtml",
|
||||||
@@ -43,19 +40,22 @@ class ScheduleApi {
|
|||||||
const endDate = startDate + 6 * 24 * 60 * 60 * 1000;
|
const endDate = startDate + 6 * 24 * 60 * 60 * 1000;
|
||||||
params.append("form:j_idt118_end", endDate);
|
params.append("form:j_idt118_end", endDate);
|
||||||
} else {
|
} else {
|
||||||
// On récupère le timestamp du lundi de la semaine en cours
|
const now = new Date();
|
||||||
const currentDate = new Date();
|
// Obtenir le jour actuel (0 = dimanche, 1 = lundi, ..., 6 = samedi)
|
||||||
const currentDay = currentDate.getDay();
|
let day = now.getDay();
|
||||||
const daysUntilMonday =
|
// Calculer la différence pour atteindre lundi (0 = dimanche => 1 jour pour atteindre lundi)
|
||||||
(currentDay === 0 ? 1 : 8) - currentDay;
|
const daysToMonday = day === 0 ? 1 : 1 - day;
|
||||||
currentDate.setDate(
|
// Créer la date de début (lundi 6h00)
|
||||||
currentDate.getDate() + daysUntilMonday,
|
const startDate = new Date(now);
|
||||||
);
|
startDate.setDate(now.getDate() + daysToMonday); // Passer au lundi
|
||||||
currentDate.setHours(0, 0, 0, 0);
|
startDate.setHours(6, 0, 0, 0); // Fixer à 6h00
|
||||||
const startTimestamp = currentDate.getTime();
|
// Date de fin (6 jours après la date de début)
|
||||||
// On fixe la date de fin à une semaine après
|
const endDate = new Date(startDate);
|
||||||
const endTimestamp =
|
endDate.setDate(startDate.getDate() + 6); // Ajouter 6 jours
|
||||||
startTimestamp + 6 * 24 * 60 * 60 * 1000;
|
// Convertir les dates en timestamp
|
||||||
|
const startTimestamp = startDate.getTime();
|
||||||
|
const endTimestamp = endDate.getTime();
|
||||||
|
|
||||||
params.append(
|
params.append(
|
||||||
"form:j_idt118_start",
|
"form:j_idt118_start",
|
||||||
startTimestamp.toString(),
|
startTimestamp.toString(),
|
||||||
@@ -70,7 +70,7 @@ class ScheduleApi {
|
|||||||
"faces/Planning.xhtml",
|
"faces/Planning.xhtml",
|
||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
resolve(scheduleResponseToEvents(response));
|
resolve(planningResponseToEvents(response));
|
||||||
} else {
|
} else {
|
||||||
reject(new Error("Viewstate not found"));
|
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 axios, { AxiosInstance } from "axios";
|
||||||
import ScheduleApi from "./ScheduleApi";
|
import PlanningApi from "./PlanningApi";
|
||||||
import { getJSFFormParams, getViewState } from "../utils/AurionUtils";
|
import { getJSFFormParams, getViewState } from "../utils/AurionUtils";
|
||||||
import NotesApi from "./NotesApi";
|
import NotesApi from "./NotesApi";
|
||||||
|
|
||||||
@@ -17,8 +17,8 @@ class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// API pour le calendrier
|
// API pour le calendrier
|
||||||
public getScheduleApi(): ScheduleApi {
|
public getPlanningApi(): PlanningApi {
|
||||||
return new ScheduleApi(this);
|
return new PlanningApi(this);
|
||||||
}
|
}
|
||||||
// API pour les notes
|
// API pour les notes
|
||||||
public getNotesApi(): NotesApi {
|
public getNotesApi(): NotesApi {
|
||||||
|
|||||||
@@ -14,52 +14,6 @@ export function getViewState(html: string): string | undefined {
|
|||||||
return 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)
|
// 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)
|
// 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)
|
// 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;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
subject: string;
|
subject: string;
|
||||||
@@ -9,3 +9,17 @@ type ScheduleEvent = {
|
|||||||
end: string;
|
end: string;
|
||||||
className: 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 { login } from "../src/api/Session";
|
||||||
|
import { noteAverage } from "../src/utils/NotesUtils";
|
||||||
describe("NotesApi", () => {
|
describe("NotesApi", () => {
|
||||||
it("should receive notes", async () => {
|
it("should receive notes", async () => {
|
||||||
const username = process.env.TEST_USERNAME;
|
const username = process.env.TEST_USERNAME;
|
||||||
@@ -10,7 +11,12 @@ describe("NotesApi", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const session = await login(username, password);
|
const session = await login(username, password);
|
||||||
const schedule = await session.getNotesApi().fetchNotes();
|
const notes = await session.getNotesApi().fetchNotes();
|
||||||
expect(schedule).not.toBeDefined();
|
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";
|
import { login } from "../src/api/Session";
|
||||||
describe("ScheduleApi", () => {
|
describe("PlanningApi", () => {
|
||||||
it("should receive a schedule", async () => {
|
it("should receive a planning", async () => {
|
||||||
const username = process.env.TEST_USERNAME;
|
const username = process.env.TEST_USERNAME;
|
||||||
const password = process.env.TEST_PASSWORD;
|
const password = process.env.TEST_PASSWORD;
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
@@ -10,7 +10,8 @@ describe("ScheduleApi", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const session = await login(username, password);
|
const session = await login(username, password);
|
||||||
const schedule = await session.getScheduleApi().fetchSchedule();
|
const planning = await session.getPlanningApi().fetchPlanning();
|
||||||
expect(schedule).toBeInstanceOf(Array);
|
console.log(planning);
|
||||||
|
expect(planning).toBeInstanceOf(Array);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user