Compare commits

..

No commits in common. "a9709a944fa398b417a42ed2ca78781e9a497bf5" and "480fe725f10e9d0ac46e1a28cfa8d5a3a852686f" have entirely different histories.

4 changed files with 70 additions and 69 deletions

View File

@ -415,7 +415,6 @@ 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

@ -1,18 +1,5 @@
#!/bin/bash export NODE_ENV=development
# Spustí server a klienta v samostatných panelech uvnitř stejného tmux okna. cd types && yarn install && yarn openapi-ts
# Pokud už daná tmux session existuje, pouze se k ní připojí. cd server && yarn install && yarn start &
cd client && yarn install && yarn start &
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) wait
SESSION="luncher"
if ! tmux has-session -t $SESSION 2>/dev/null; then
cd types && yarn openapi-ts && cd ..
tmux new-session -d -s $SESSION
tmux send-keys -t $SESSION:0 "cd $SCRIPT_DIR" Enter
tmux split-window -v
tmux send-keys -t $SESSION:0.0 "cd server && export NODE_ENV=development && yarn startReload" Enter
tmux send-keys -t $SESSION:0.1 "cd client && yarn start" Enter
fi
tmux attach-session -t $SESSION

1
server/.gitignore vendored
View File

@ -1,4 +1,3 @@
/data
/dist /dist
/resources/easterEggs /resources/easterEggs
/src/gen /src/gen

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,65 +78,81 @@ 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 menuContentElements = $('#daily-menu-content-list').children('[id^="daily-menu-content-"]'); const list = $('ul.tab-links').children();
// 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 dayChildren = $(menuContentElements[dayIndex]).children(); const currentDate = new Date(firstDayOfWeek);
// Prozatím předpokládáme, že budou mít vždy polévku a hlavní jídla currentDate.setDate(firstDayOfWeek.getDate() + dayIndex);
if (dayChildren.length < 2) { const searchedDayText = `${currentDate.getDate()}.${currentDate.getMonth() + 1}.${capitalize(DAYS_IN_WEEK[dayIndex])}`;
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)"); // Najdeme index pro vstupní datum (např. při svátcích bude posunutý)
// 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;
} }
// Parsování polévky // Dle dohledaného indexu najdeme správný tabpanel
const soupElement = dayChildren.get(0); const rows = $('div.tab-content').children();
const soupTable = $(soupElement).find('table tbody tr'); if (index >= rows.length) {
const soupCells = soupTable.children('td'); throw Error("V HTML nebyl nalezen řádek menu pro index " + index);
}
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: soupAmount, amount: sanitizeText($(soupCells.get(0)).text()),
name: soupName, name: sanitizeText($(soupCells.get(1)).text()),
price: soupPrice, price: sanitizeText($(soupCells.get(2)).text().replace(' ', '\xA0')),
isSoup: true, isSoup: true,
}); });
// Hlavní jídla - div -> table -> tbody -> 3x tr
// Projdeme všechny řádky hlavních jídel const mainCourseRows = $(tables.get(1)).children().first().children();
mainCourseRows.each((i, row) => { mainCourseRows.each((i, foodRow) => {
const cells = $(row).children('td'); const foodCells = $(foodRow).children();
const amount = sanitizeText($(cells.get(0)).text()); if (foodCells.length !== 3) {
const name = sanitizeText($(cells.get(1)).text()); throw Error("Neočekávaný počet buněk v řádku jídla: " + foodCells.length + ", ale očekávány byly 3");
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;