Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implémentation de Alise #466

Merged
merged 4 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/images/service_alise.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
489 changes: 409 additions & 80 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@react-navigation/native": "^6.1.17",
"@react-navigation/native-stack": "^6.9.26",
"@react-navigation/stack": "^6.4.0",
"alise-api": "^0.1.2",
"axios": "^1.7.7",
"buffer": "^6.0.3",
"cal-parser": "^1.0.2",
Expand Down
4 changes: 3 additions & 1 deletion src/providers/AlertProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ const AlertProvider = ({ children }: AlertProviderProps) => {
<Pressable
key={title}
onPress={() => {
onPress();
if (onPress) {
onPress();
}
hideAlert();
}}
style={({ pressed }) => [
Expand Down
1 change: 1 addition & 0 deletions src/router/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export type RouteParameters = {
ExternalTurboselfLogin: undefined;
ExternalArdLogin: undefined;
ExternalIzlyLogin: undefined;
ExternalAliseLogin: undefined;
IzlyActivation: { username: string, password: string };
PriceError: { account: Client, accountId: string };
QrcodeScanner: { accountID: string };
Expand Down
4 changes: 4 additions & 0 deletions src/router/screens/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import PriceBeforeScan from "@/views/settings/ExternalAccount/PriceBeforeScan";
import SettingsFlagsInfos from "@/views/settings/SettingsFlagsInfos";
import ExternalIzlyLogin from "@/views/settings/ExternalAccount/Izly";
import IzlyActivation from "@/views/settings/ExternalAccount/IzlyActivation";
import ExternalAliseLogin from "@/views/settings/ExternalAccount/Alise";

const settingsScreens = [
createScreen("Settings", Settings, {
Expand Down Expand Up @@ -101,6 +102,9 @@ const settingsScreens = [
createScreen("ExternalIzlyLogin", ExternalIzlyLogin, {
headerTitle: "Connexion à Izly",
}),
createScreen("ExternalAliseLogin", ExternalAliseLogin, {
headerTitle: "Connexion à Alise",
}),

createScreen("IzlyActivation", IzlyActivation, {
headerTitle: "Configuration de Izly",
Expand Down
14 changes: 14 additions & 0 deletions src/services/alise/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { AliseAccount } from "@/stores/account/types";
import type { Balance } from "../shared/Balance";

export const getBalance = async (account: AliseAccount, force = false): Promise<Array<Balance>> => {
const balance = force ? (await account.authentication.session.getInformations()).balance : account.authentication.session.account?.balance ?? 0;
const mealPrice = account.authentication.mealPrice;

return [{
amount: balance,
currency: "€",
remaining: Math.floor(balance / (mealPrice ?? 0)),
label: "Self"
}];
};
26 changes: 26 additions & 0 deletions src/services/alise/bookings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { AliseAccount } from "@/stores/account/types";
import type { BookingDay, BookingTerminal } from "../shared/Booking";

export const getBookings = async (account: AliseAccount, force = false): Promise<BookingTerminal[]> => {
const bookings = force ? await account.authentication.session.getBookings() : await account.authentication.bookings;
return [{
id: "",
terminalLabel: "Self",
days: bookings.map((day) => ({
id: (day.identifier ?? ""),
canBook: day.canBook,
date: day.date,
booked: day.booked
})),
account: account
}];
};

export const bookDay = async (account: AliseAccount, id: string, date: Date, booked: boolean): Promise<BookingDay> => {
const bookedDay = await account.authentication.session.bookDay(id, 1, !booked);
return {
id: bookedDay.identifier ?? "",
canBook: bookedDay.canBook,
booked: bookedDay.booked,
};
};
12 changes: 12 additions & 0 deletions src/services/alise/history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { AliseAccount } from "@/stores/account/types";
import type { ReservationHistory } from "../shared/ReservationHistory";

export const getHistory = async (account: AliseAccount): Promise<ReservationHistory[]> => {
const history = await account.authentication.session.getFinancialHistory();
return (history ?? []).map((item) => ({
timestamp: item.date.getTime(),
amount: item.amount,
currency: "€",
label: item.label,
}));
};
16 changes: 16 additions & 0 deletions src/services/alise/reload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { AliseAccount } from "@/stores/account/types";
import { authenticateWithCredentials } from "alise-api";

export const reload = async (account: AliseAccount): Promise<AliseAccount["authentication"]> => {
const auth = { ...account.authentication };
const session = await authenticateWithCredentials(auth.username, auth.password, auth.schoolID);
const bookings = await session.getBookings();
return {
session,
schoolID: auth.schoolID,
username: auth.username,
password: auth.password,
mealPrice: auth.mealPrice,
bookings: bookings
};
};
6 changes: 5 additions & 1 deletion src/services/balance.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {AccountService, type ExternalAccount} from "@/stores/account/types";
import type {Balance} from "./shared/Balance";

export const balanceFromExternal = async (account: ExternalAccount): Promise<Balance[]> => {
export const balanceFromExternal = async (account: ExternalAccount, force = false): Promise<Balance[]> => {
switch (account.service) {
case AccountService.Turboself: {
const { getBalance } = await import("./turboself/balance");
Expand All @@ -15,6 +15,10 @@ export const balanceFromExternal = async (account: ExternalAccount): Promise<Bal
const { balance } = await import("./izly/balance");
return balance(account);
}
case AccountService.Alise: {
const { getBalance } = await import("./alise/balance");
return getBalance(account, force);
}
default: {
return [];
}
Expand Down
12 changes: 11 additions & 1 deletion src/services/booking.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AccountService, type ExternalAccount } from "@/stores/account/types";
import type { BookingDay, BookingTerminal } from "./shared/Booking";

export const getBookingsAvailableFromExternal = async (account: ExternalAccount, weekNumber?: number): Promise<BookingTerminal[]> => {
export const getBookingsAvailableFromExternal = async (account: ExternalAccount, weekNumber?: number, force = false): Promise<BookingTerminal[]> => {
switch (account.service) {
case AccountService.Turboself: {
const { getBookingWeek } = await import("./turboself/booking");
Expand All @@ -14,6 +14,11 @@ export const getBookingsAvailableFromExternal = async (account: ExternalAccount,
// TODO: Implement ARD
return [];
}
case AccountService.Alise: {
const { getBookings } = await import("./alise/bookings");
const bookings = await getBookings(account, force);
return bookings;
}
default: {
return [];
}
Expand All @@ -27,6 +32,11 @@ export const bookDayFromExternal = async (account: ExternalAccount, id: string,
const bookedDay = await bookDay(account, id, date, booked);
return bookedDay;
}
case AccountService.Alise: {
const { bookDay } = await import("./alise/bookings");
const bookedDay = await bookDay(account, id, date, booked);
return bookedDay;
}
case AccountService.ARD: {
return undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion src/services/ecoledirecte/attendance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Absence} from "@/services/shared/Absence";
import {Delay} from "@/services/shared/Delay";

const decodeDelay = (item: AttendanceItem): Delay => {
const timeInterval = dateStringAsTimeInterval(item.displayDate);
const timeInterval = dateStringAsTimeInterval(item.displayDate);
const duration = (timeInterval?.end && timeInterval.start) ? getDuration(timeInterval).getTime() / (60 * 1000): 0;
return {
id: item.id.toString(),
Expand Down
4 changes: 2 additions & 2 deletions src/services/ecoledirecte/chats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export const getChats = async (account: EcoleDirecteAccount): Promise<Chat[]> =>
};

const cleanMessage = (message: string) => {
return message.replace(/>\s+/g, '>').replace(/&nbsp;/g, ' ');
}
return message.replace(/>\s+/g, ">").replace(/&nbsp;/g, " ");
};

export const getChatMessages = async (account: EcoleDirecteAccount, chat: Chat): Promise<ChatMessage> => {
if (!account.instance)
Expand Down
4 changes: 2 additions & 2 deletions src/services/ecoledirecte/homework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { AttachmentType } from "../shared/Attachment";
import { formatDate } from "@/services/ecoledirecte/format-date";

const cleanContent = (message: string) => {
return message.replace(/>\s+/g, '>').replace(/&nbsp;/g, ' ');
}
return message.replace(/>\s+/g, ">").replace(/&nbsp;/g, " ");
};

export const getHomeworkForWeek = async (
account: EcoleDirecteAccount,
Expand Down
88 changes: 44 additions & 44 deletions src/services/ecoledirecte/time-interval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,74 +8,74 @@ const months = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet"
/**
* @param str "A string containing a date formatted as "mercredi 21 février 2024 de 08:10", will return the date for the specified day and hour"
*/
export function dateAsISO860(str: string): string {
export function dateAsISO860 (str: string): string {
const parts = str.split(" ");
const timeIndex = parts.findIndex(part => part.includes(":"));
const hour = parts[timeIndex].split(":");
const monthIndex = parts.findIndex(part => months.includes(part));

return (new Date(
Number(parts[monthIndex + 1]),
months.indexOf(parts[monthIndex]),
Number(parts[monthIndex - 1]),
Number(hour[0]),
Number(hour[1])
Number(parts[monthIndex + 1]),
months.indexOf(parts[monthIndex]),
Number(parts[monthIndex - 1]),
Number(hour[0]),
Number(hour[1])
)).toISOString();
}

export function dateStringAsTimeInterval(
export function dateStringAsTimeInterval (
str: string,
): Timeinterval | undefined {
if (str.includes("du")) {
/**
/**
* @example
* str is equal to "du mercredi 21 février 2024 au jeudi 22 février 2024"
* or "du mercredi 27 novembre 2024 à 08:10 au vendredi 06 décembre 2024 à 08:10"
*/
const [startPart, endPart] = str.split("au").map(part => part.trim());
let start = startPart.replace("du", "").trim();
let end = endPart.trim();
const [startPart, endPart] = str.split("au").map(part => part.trim());
let start = startPart.replace("du", "").trim();
let end = endPart.trim();

if (!start.includes(":")) {
start += " de 00:00";
} else {
start = start.replace(" à ", " de ");
}
if (!start.includes(":")) {
start += " de 00:00";
} else {
start = start.replace(" à ", " de ");
}

if (!end.includes(":")) {
end += " de 23:59";
} else {
end = end.replace(" à ", " de ");
}
if (!end.includes(":")) {
end += " de 23:59";
} else {
end = end.replace(" à ", " de ");
}

return {
start: dateAsISO860(start),
end: dateAsISO860(end)
} as Timeinterval;
return {
start: dateAsISO860(start),
end: dateAsISO860(end)
} as Timeinterval;
}

if (str.includes("le")) {
/**
/**
* @example
* str is equal to "le mercredi 21 février 2024 de 08:10 à 16:10"
* or "le jeudi 22 février 2024"
*/
const parts = str.split("à");
let startDate: string;
let endDate: string;
const parts = str.split("à");
let startDate: string;
let endDate: string;

// It's a full day ("le mercredi 21 février 2024")
if (!str.includes(":")) {
startDate = `${parts[0].replace("le", "").trim()} de 00:00`;
endDate = `${parts[0].split("de")[0].replace("le", "").trim()} de 23:59`;
} else {
startDate = parts[0].replace("le", "").trim();
endDate = `${parts[0].split("de")[0].replace("le", "").trim()} de ${parts[1].trim()}`;
}
// It's a full day ("le mercredi 21 février 2024")
if (!str.includes(":")) {
startDate = `${parts[0].replace("le", "").trim()} de 00:00`;
endDate = `${parts[0].split("de")[0].replace("le", "").trim()} de 23:59`;
} else {
startDate = parts[0].replace("le", "").trim();
endDate = `${parts[0].split("de")[0].replace("le", "").trim()} de ${parts[1].trim()}`;
}

const start = dateAsISO860(startDate);
const end = dateAsISO860(endDate);
return { start: start, end: end } as Timeinterval;
const start = dateAsISO860(startDate);
const end = dateAsISO860(endDate);
return { start: start, end: end } as Timeinterval;
}
return undefined;
}
Expand All @@ -100,6 +100,6 @@ export const getDurationInHours = (interval: Timeinterval): string => {
const diff = new Date(interval.end).getTime() - new Date(interval.start).getTime();
const hours = Math.floor(diff / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
return `${hours}h${minutes.toString().padStart(2, '0')}`;

return `${hours}h${minutes.toString().padStart(2, "0")}`;
};
5 changes: 5 additions & 0 deletions src/services/reload-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export async function reload <T extends Account> (account: T): Promise<Reconnect
// keep instance the same
return { instance: undefined, authentication: auth };
}
case AccountService.Alise: {
const { reload } = await import("./alise/reload");
const auth = await reload(account);
return { instance: undefined, authentication: auth };
}
case AccountService.ARD: {
const { reload } = await import("./ard/reload");
const instance = await reload(account);
Expand Down
4 changes: 4 additions & 0 deletions src/services/reservation-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const reservationHistoryFromExternal = async (account: ExternalAccount):
const { history: getHistory } = await import("./izly/history");
return getHistory(account);
}
case AccountService.Alise: {
const { getHistory } = await import("./alise/history");
return getHistory(account);
}
default: {
return [];
}
Expand Down
10 changes: 3 additions & 7 deletions src/services/shared/Booking.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { ARDAccount, TurboselfAccount } from "@/stores/account/types";
import { AliseAccount, ARDAccount, TurboselfAccount } from "@/stores/account/types";

export interface BookingTerminal {
id: string;
week: number;
from: Date;
to: Date;
terminalLabel: string;
days: BookingDay[];
account: TurboselfAccount | ARDAccount;
account: TurboselfAccount | ARDAccount | AliseAccount;
}

export interface BookingDay {
id: string;
canBook: boolean;
date: Date;
message: string;
date?: Date;
booked: boolean;
}
Loading
Loading