Úprava pro novou podobu stránek Sladovnická
All checks were successful
ci/woodpecker/push/workflow Pipeline was successful

This commit is contained in:
Martin Berka 2025-08-04 17:30:04 +02:00
parent 593ffcf02b
commit a9709a944f
Signed by: mates
SSH Key Fingerprint: SHA256:HILXS+ahJ33PQ6YDd3ToEV92OujgFG6CUiFQmvgBx0Q
2 changed files with 50 additions and 65 deletions

View File

@ -415,6 +415,7 @@ function App() {
Poslední změny: Poslední změny:
<ul> <ul>
<li>Podpora ručního refresh týdne</li> <li>Podpora ručního refresh týdne</li>
<li>Úprava pro přepracovanou podobu stránek Sladovnická</li>
</ul> </ul>
</Alert> </Alert>
{dayIndex != null && {dayIndex != null &&

View File

@ -23,7 +23,7 @@ const SOUP_NAMES = [
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'];
// URL na týdenní menu jednotlivých restaurací // URL na týdenní menu jednotlivých restaurací
const SLADOVNICKA_URL = 'https://sladovnicka.unasplzenchutna.cz/cz/denni-nabidka'; const SLADOVNICKA_URL = 'https://sladovnicka.unasplzenchutna.cz/cz/#denni-nabidka';
const U_MOTLIKU_URL = 'https://www.umotliku.cz/menu'; const U_MOTLIKU_URL = 'https://www.umotliku.cz/menu';
const TECHTOWER_URL = 'https://www.equifarm.cz/restaurace-techtower'; const TECHTOWER_URL = 'https://www.equifarm.cz/restaurace-techtower';
const ZASTAVKAUMICHALA_URL = 'https://www.zastavkaumichala.cz'; const ZASTAVKAUMICHALA_URL = 'https://www.zastavkaumichala.cz';
@ -78,81 +78,65 @@ export const getMenuSladovnicka = async (firstDayOfWeek: Date, mock: boolean = f
const html = await getHtml(SLADOVNICKA_URL); const html = await getHtml(SLADOVNICKA_URL);
const $ = load(html); const $ = load(html);
const list = $('ul.tab-links').children(); const menuContentElements = $('#daily-menu-content-list').children('[id^="daily-menu-content-"]');
// Prozatím předpokládáme, že budou mít vždy elementy pro všech 5 dní v týdnu, i pokud bude zavřeno
if (menuContentElements.length < 5) {
throw Error("Neočekávaný počet dní v menu Sladovnické: " + menuContentElements.length + ", očekáváno 5 (možná je některý den zavřeno?)");
}
const result: Food[][] = []; const result: Food[][] = [];
for (let dayIndex = 0; dayIndex < 5; dayIndex++) { for (let dayIndex = 0; dayIndex < 5; dayIndex++) {
const currentDate = new Date(firstDayOfWeek); const dayChildren = $(menuContentElements[dayIndex]).children();
currentDate.setDate(firstDayOfWeek.getDate() + dayIndex); // Prozatím předpokládáme, že budou mít vždy polévku a hlavní jídla
const searchedDayText = `${currentDate.getDate()}.${currentDate.getMonth() + 1}.${capitalize(DAYS_IN_WEEK[dayIndex])}`; if (dayChildren.length < 2) {
// Najdeme index pro vstupní datum (např. při svátcích bude posunutý) throw Error("Neočekávaný počet children v menu Sladovnické pro den " + dayIndex + ": " + dayChildren.length + ", očekávány alespoň 2 (polévka a hlavní jídlo)");
// TODO validovat, že vstupní datum je v aktuálním týdnu
// TODO tenhle způsob je zbytečně komplikovaný - stačilo by hledat rovnou v div.tab-content, protože každý den tam má datum taky (akorát je print-only)
let index = undefined;
list.each((i, dayRow) => {
const rowText = $(dayRow).first().text().trim();
if (rowText === searchedDayText) {
index = i;
}
})
if (index === undefined) {
// Pravděpodobně svátek, nebo je zavřeno
result[dayIndex] = [{
amount: undefined,
name: "Pro daný den nebyla nalezena denní nabídka",
price: "",
isSoup: false,
}];
continue;
} }
// Dle dohledaného indexu najdeme správný tabpanel // Parsování polévky
const rows = $('div.tab-content').children(); const soupElement = dayChildren.get(0);
if (index >= rows.length) { const soupTable = $(soupElement).find('table tbody tr');
throw Error("V HTML nebyl nalezen řádek menu pro index " + index); const soupCells = soupTable.children('td');
}
const tabPanel = $(rows.get(index));
// Opětovná validace, že daný tabpanel je pro vstupní datum
const headers = tabPanel.find('h2');
if (headers.length !== 3) {
throw Error("Neočekávaný počet elementů h2 v menu pro datum " + searchedDayText + ", očekávány 3, ale nalezeno bylo " + headers.length);
}
const dayText = $(headers.get(0)).text().trim();
if (dayText !== searchedDayText) {
throw Error("Neočekávaný datum na řádce nalezeného dne: '" + dayText + "', ale očekáváno bylo '" + searchedDayText + "'");
}
// V tabpanelu očekáváme dvě tabulky - pro polévku a pro hlavní jídlo
const tables = tabPanel.find('table');
if (tables.length !== 2) {
throw Error("Neočekávaný počet tabulek na řádce nalezeného dne: " + tables.length + ", ale očekávány byly 2");
}
const currentDayFood: Food[] = [];
// Polévka - div -> table -> tbody -> tr -> 3x td
const soupCells = $(tables.get(0)).children().first().children().first().children();
if (soupCells.length !== 3) { if (soupCells.length !== 3) {
throw Error("Neočekávaný počet buněk v tabulce polévky: " + soupCells.length + ", ale očekávány byly 3"); throw Error("Neočekávaný počet buněk v tabulce polévky: " + soupCells.length + ", ale očekávány byly 3");
} }
const soupAmount = sanitizeText($(soupCells.get(0)).text());
const soupName = sanitizeText($(soupCells.get(1)).text());
const soupPrice = sanitizeText($(soupCells.get(2)).text().replace(' ', '\xA0'));
// Parsování hlavních jídel
const mainCourseElement = dayChildren.get(1);
const mainCourseTable = $(mainCourseElement).find('table tbody');
const mainCourseRows = mainCourseTable.children('tr');
const currentDayFood: Food[] = [];
// Přidáme polévku do seznamu jídel
currentDayFood.push({ currentDayFood.push({
amount: sanitizeText($(soupCells.get(0)).text()), amount: soupAmount,
name: sanitizeText($(soupCells.get(1)).text()), name: soupName,
price: sanitizeText($(soupCells.get(2)).text().replace(' ', '\xA0')), price: soupPrice,
isSoup: true, isSoup: true,
}); });
// Hlavní jídla - div -> table -> tbody -> 3x tr
const mainCourseRows = $(tables.get(1)).children().first().children(); // Projdeme všechny řádky hlavních jídel
mainCourseRows.each((i, foodRow) => { mainCourseRows.each((i, row) => {
const foodCells = $(foodRow).children(); const cells = $(row).children('td');
if (foodCells.length !== 3) { const amount = sanitizeText($(cells.get(0)).text());
throw Error("Neočekávaný počet buněk v řádku jídla: " + foodCells.length + ", ale očekávány byly 3"); const name = sanitizeText($(cells.get(1)).text());
const price = sanitizeText($(cells.get(2)).text().replace(' ', '\xA0'));
// Přeskočíme prázdné řádky (první řádek může být prázdný)
if (name.trim().length > 0) {
currentDayFood.push({
amount,
name,
price,
isSoup: false,
});
} }
currentDayFood.push({ });
amount: sanitizeText($(foodCells.get(0)).text()),
name: sanitizeText($(foodCells.get(1)).text()),
price: sanitizeText($(foodCells.get(2)).text().replace(' ', '\xA0')),
isSoup: false,
});
})
result[dayIndex] = currentDayFood; result[dayIndex] = currentDayFood;
} }
return result; return result;