detekce stareho menu u toweru #52

Merged
mates merged 3 commits from upravy into master 2026-03-06 09:34:25 +01:00
3 changed files with 36 additions and 9 deletions
Showing only changes of commit f8a65d7177 - Show all commits

View File

@@ -4,6 +4,10 @@ import { getMenuSladovnickaMock, getMenuTechTowerMock, getMenuUMotlikuMock, getM
import { formatDate } from "./utils"; import { formatDate } from "./utils";
import { Food } from "../../types/gen/types.gen"; import { Food } from "../../types/gen/types.gen";
export class StaleWeekError extends Error {
constructor(public food: Food[][]) { super('Data jsou z jiného týdne'); }
}
// Fráze v názvech jídel, které naznačují že se jedná o polévku // Fráze v názvech jídel, které naznačují že se jedná o polévku
const SOUP_NAMES = [ const SOUP_NAMES = [
'polévka', 'polévka',
@@ -299,7 +303,6 @@ export const getMenuTechTower = async (firstDayOfWeek: Date, mock: boolean = fal
} }
const result: Food[][] = []; const result: Food[][] = [];
// TODO validovat, že v textu nalezeného <font> je rozsah, do kterého spadá vstupní datum
const siblings = secondTry ? $(font).parent().parent().parent().siblings('p') : $(font).parent().parent().siblings(); const siblings = secondTry ? $(font).parent().parent().parent().siblings('p') : $(font).parent().parent().siblings();
let parsing = false; let parsing = false;
let currentDayIndex = 0; let currentDayIndex = 0;
@@ -345,6 +348,18 @@ export const getMenuTechTower = async (firstDayOfWeek: Date, mock: boolean = fal
}) })
} }
} }
// Validace, zda text hlavičky obsahuje datum odpovídající požadovanému týdnu
const headerText = $(font).text().trim();
const dateMatch = headerText.match(/(\d{1,2})\.(\d{1,2})\./);
if (dateMatch) {
const foundDay = parseInt(dateMatch[1]);
const foundMonth = parseInt(dateMatch[2]) - 1; // JS months are 0-based
if (foundDay !== firstDayOfWeek.getDate() || foundMonth !== firstDayOfWeek.getMonth()) {
throw new StaleWeekError(result);
}
}
return result; return result;
} }

View File

@@ -1,6 +1,6 @@
import { InsufficientPermissions, PizzaDayConflictError, formatDate, getDayOfWeekIndex, getFirstWorkDayOfWeek, getHumanDate, getIsWeekend, getWeekNumber } from "./utils"; import { InsufficientPermissions, PizzaDayConflictError, formatDate, getDayOfWeekIndex, getFirstWorkDayOfWeek, getHumanDate, getIsWeekend, getWeekNumber } from "./utils";
import getStorage from "./storage"; import getStorage from "./storage";
import { getMenuSladovnicka, getMenuTechTower, getMenuZastavkaUmichala, getMenuSenkSerikova } from "./restaurants"; import { getMenuSladovnicka, getMenuTechTower, getMenuZastavkaUmichala, getMenuSenkSerikova, StaleWeekError } from "./restaurants";
import { getTodayMock } from "./mock"; import { getTodayMock } from "./mock";
import { removeAllUserPizzas } from "./pizza"; import { removeAllUserPizzas } from "./pizza";
import { ClientData, DepartureTime, LunchChoice, PizzaDayState, Restaurant, RestaurantDayMenu, WeekMenu } from "../../types/gen/types.gen"; import { ClientData, DepartureTime, LunchChoice, PizzaDayState, Restaurant, RestaurantDayMenu, WeekMenu } from "../../types/gen/types.gen";
@@ -216,6 +216,7 @@ export async function getRestaurantMenu(restaurant: Restaurant, date?: Date, for
for (let i = 0; i < restaurantWeekFood.length && i < weekMenu.length; i++) { for (let i = 0; i < restaurantWeekFood.length && i < weekMenu.length; i++) {
weekMenu[i][restaurant]!.food = restaurantWeekFood[i]; weekMenu[i][restaurant]!.food = restaurantWeekFood[i];
weekMenu[i][restaurant]!.lastUpdate = now; weekMenu[i][restaurant]!.lastUpdate = now;
weekMenu[i][restaurant]!.isStale = false;
// Detekce uzavření pro každou restauraci // Detekce uzavření pro každou restauraci
switch (restaurant) { switch (restaurant) {
@@ -245,22 +246,34 @@ export async function getRestaurantMenu(restaurant: Restaurant, date?: Date, for
// Uložení do storage // Uložení do storage
await storage.setData(getMenuKey(usedDate), weekMenu); await storage.setData(getMenuKey(usedDate), weekMenu);
} catch (e: any) { } catch (e: any) {
if (e instanceof StaleWeekError) {
for (let i = 0; i < e.food.length && i < weekMenu.length; i++) {
weekMenu[i][restaurant]!.food = e.food[i];
weekMenu[i][restaurant]!.lastUpdate = now;
weekMenu[i][restaurant]!.isStale = true;
}
await storage.setData(getMenuKey(usedDate), weekMenu);
} else {
console.error(`Selhalo načtení jídel pro podnik ${restaurant}`, e); console.error(`Selhalo načtení jídel pro podnik ${restaurant}`, e);
} }
} }
}
const result = weekMenu[dayOfWeekIndex][restaurant]!; const result = weekMenu[dayOfWeekIndex][restaurant]!;
result.warnings = generateMenuWarnings(result, now); result.warnings = generateMenuWarnings(result);
return result; return result;
} }
/** /**
* Generuje varování o kvalitě/úplnosti dat menu restaurace. * Generuje varování o kvalitě/úplnosti dat menu restaurace.
*/ */
function generateMenuWarnings(menu: RestaurantDayMenu, now: number): string[] { function generateMenuWarnings(menu: RestaurantDayMenu): string[] {
const warnings: string[] = []; const warnings: string[] = [];
if (!menu.food?.length || menu.closed) { if (!menu.food?.length || menu.closed) {
return warnings; return warnings;
} }
if (menu.isStale) {
warnings.push('Data jsou z minulého týdne');
}
const hasSoup = menu.food.some(f => f.isSoup); const hasSoup = menu.food.some(f => f.isSoup);
if (!hasSoup) { if (!hasSoup) {
warnings.push('Chybí polévka'); warnings.push('Chybí polévka');
@@ -269,10 +282,6 @@ function generateMenuWarnings(menu: RestaurantDayMenu, now: number): string[] {
if (missingPrice) { if (missingPrice) {
warnings.push('U některých jídel chybí cena'); warnings.push('U některých jídel chybí cena');
} }
const STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
if (menu.lastUpdate && (now - menu.lastUpdate) > STALE_THRESHOLD_MS) {
warnings.push('Data jsou starší než 24 hodin');
}
return warnings; return warnings;
} }

View File

@@ -186,6 +186,9 @@ RestaurantDayMenu:
type: array type: array
items: items:
type: string type: string
isStale:
description: Příznak, zda data mohou pocházet z jiného týdne
type: boolean
RestaurantDayMenuMap: RestaurantDayMenuMap:
description: Objekt, kde klíčem je podnik ((#Restaurant)) a hodnotou denní menu daného podniku ((#RestaurantDayMenu)) description: Objekt, kde klíčem je podnik ((#Restaurant)) a hodnotou denní menu daného podniku ((#RestaurantDayMenu))
type: object type: object