fix: oprava parsování TechTower po změně jejich webu
CI / Generate TypeScript types (push) Successful in 11s
CI / Server unit tests (push) Successful in 30s
CI / Build server (push) Successful in 29s
CI / Build client (push) Successful in 41s
CI / Playwright E2E tests (push) Successful in 2m0s
CI / Build and push Docker image (push) Successful in 47s
CI / Notify (push) Successful in 2s
CI / Generate TypeScript types (push) Successful in 11s
CI / Server unit tests (push) Successful in 30s
CI / Build server (push) Successful in 29s
CI / Build client (push) Successful in 41s
CI / Playwright E2E tests (push) Successful in 2m0s
CI / Build and push Docker image (push) Successful in 47s
CI / Notify (push) Successful in 2s
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
[
|
||||||
|
"Opraveno načítání jídelního lístku TechTower po změně jejich webu"
|
||||||
|
]
|
||||||
@@ -31,6 +31,11 @@ const SOUP_NAMES = [
|
|||||||
'bramboračka',
|
'bramboračka',
|
||||||
'dýňový krém',
|
'dýňový krém',
|
||||||
'ovarová',
|
'ovarová',
|
||||||
|
'fazolačka',
|
||||||
|
'kyselica',
|
||||||
|
'rajčatová s ',
|
||||||
|
'hrášková s ',
|
||||||
|
'hrachová s ',
|
||||||
];
|
];
|
||||||
const DAYS_IN_WEEK = ['pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota', 'neděle'];
|
const DAYS_IN_WEEK = ['pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota', 'neděle'];
|
||||||
|
|
||||||
@@ -334,6 +339,13 @@ export const getMenuTechTower = async (firstDayOfWeek: Date, mock: boolean = fal
|
|||||||
if (!font) {
|
if (!font) {
|
||||||
throw new Error('Chyba: nenalezen <font> pro obědy v HTML Techtower.');
|
throw new Error('Chyba: nenalezen <font> pro obědy v HTML Techtower.');
|
||||||
}
|
}
|
||||||
|
// Šestý pokus - formát z 10/2026: hlavička je <p><strong><font class="wsw-41">,
|
||||||
|
// jednotlivé dny a položky jsou sourozenecké <p> v témže bloku. Třetí pokus sice najde
|
||||||
|
// hlavičku, ale <font> nemá žádné sourozenecké <p>, proto se použije parsing pátého pokusu.
|
||||||
|
if (thirdTry && $(font).parent().siblings('p').length === 0) {
|
||||||
|
thirdTry = false;
|
||||||
|
fifthTry = true;
|
||||||
|
}
|
||||||
|
|
||||||
const result: Food[][] = [];
|
const result: Food[][] = [];
|
||||||
|
|
||||||
@@ -371,7 +383,11 @@ export const getMenuTechTower = async (firstDayOfWeek: Date, mock: boolean = fal
|
|||||||
if (fifthTry) {
|
if (fifthTry) {
|
||||||
let parsing = false;
|
let parsing = false;
|
||||||
let currentDayIndex = 0;
|
let currentDayIndex = 0;
|
||||||
const container = $(font).parent().parent();
|
let container = $(font).parent().parent();
|
||||||
|
if (container.find('p').length === 0) {
|
||||||
|
// Formát 10/2026 - hlavička je přímo v <p>, kontejnerem je jeho rodič
|
||||||
|
container = $(font).closest('p').parent();
|
||||||
|
}
|
||||||
container.find('p').each((_, p) => {
|
container.find('p').each((_, p) => {
|
||||||
const pHtml = $(p).html() ?? '';
|
const pHtml = $(p).html() ?? '';
|
||||||
const lines = pHtml.split(/<br\s*\/?>/i)
|
const lines = pHtml.split(/<br\s*\/?>/i)
|
||||||
@@ -379,7 +395,15 @@ export const getMenuTechTower = async (firstDayOfWeek: Date, mock: boolean = fal
|
|||||||
.filter(line => line.length > 0);
|
.filter(line => line.length > 0);
|
||||||
for (const text of lines) {
|
for (const text of lines) {
|
||||||
const lower = text.toLocaleLowerCase();
|
const lower = text.toLocaleLowerCase();
|
||||||
|
// Za aktuálním týdnem může následovat menu na další týden - to již nezpracováváme
|
||||||
|
if (parsing && lower.startsWith('následující týden')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (DAYS_IN_WEEK.includes(lower)) {
|
if (DAYS_IN_WEEK.includes(lower)) {
|
||||||
|
if (result[DAYS_IN_WEEK.indexOf(lower)]) {
|
||||||
|
// Opakující se den = začátek dalšího týdne
|
||||||
|
return false;
|
||||||
|
}
|
||||||
currentDayIndex = DAYS_IN_WEEK.indexOf(lower);
|
currentDayIndex = DAYS_IN_WEEK.indexOf(lower);
|
||||||
parsing = true;
|
parsing = true;
|
||||||
result[currentDayIndex] ??= [];
|
result[currentDayIndex] ??= [];
|
||||||
|
|||||||
+305
File diff suppressed because one or more lines are too long
@@ -129,6 +129,43 @@ describe('TechTower parser – nový formát 07/2026', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('TechTower parser – nový formát 10/2026', () => {
|
||||||
|
// Pondělí 5.10.2026, stránka obsahuje i menu na následující týden
|
||||||
|
const MONDAY_2026_10 = new Date('2026-10-05');
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockedAxios.get = jest.fn().mockResolvedValue({ data: loadFixture('techtower-2026-10.html') });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vrátí pole o délce 5 s jídly pro každý den', async () => {
|
||||||
|
const menu = await getMenuTechTower(MONDAY_2026_10);
|
||||||
|
expect(menu).toHaveLength(5);
|
||||||
|
for (const den of menu) {
|
||||||
|
expect(den).toHaveLength(5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('nezahrne menu následujícího týdne', async () => {
|
||||||
|
const menu = await getMenuTechTower(MONDAY_2026_10);
|
||||||
|
expect(menu[0][0].name).toBe('Mexická fazolačka s kukuřicí a masem');
|
||||||
|
expect(menu.flat().some(f => f.name.startsWith('Hrášková'))).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('každý den začíná polévkou s cenou a alergeny', async () => {
|
||||||
|
const menu = await getMenuTechTower(MONDAY_2026_10);
|
||||||
|
for (const den of menu) {
|
||||||
|
expect(den[0].isSoup).toBe(true);
|
||||||
|
expect(den[0].price).toBe('45\xA0Kč');
|
||||||
|
expect(den[0].allergens?.length).toBeGreaterThan(0);
|
||||||
|
expect(den.slice(1).some(f => f.isSoup)).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hodí StaleWeekError pro jiný týden', async () => {
|
||||||
|
await expect(getMenuTechTower(new Date('2026-10-12'))).rejects.toBeInstanceOf(StaleWeekError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('SenkSerikova parser', () => {
|
describe('SenkSerikova parser', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// SenkSerikova parsuje arraybuffer – musíme vrátit Buffer, ne string
|
// SenkSerikova parsuje arraybuffer – musíme vrátit Buffer, ne string
|
||||||
|
|||||||
Reference in New Issue
Block a user