Compare commits

..

No commits in common. "master" and "5a097a5ce799a45f762bd832b6b82eeb239579dd" have entirely different histories.

19 changed files with 528 additions and 433 deletions

4
.gitignore vendored
View File

@ -4,7 +4,3 @@ menuprint
.lvimrc
example.*
dhparam*
.cache
.clang-format
build
compile_commands.json

View File

@ -1,42 +0,0 @@
cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
enable_language(CXX)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBXML REQUIRED libxml-2.0)
pkg_check_modules(LIBXMLPP REQUIRED libxml++-3.0)
find_library(Restbed restbed
PATHS /usr/lib)
find_library(Curl curl
PATHS /usr/lib)
include_directories(/usr/local/include /opt/homebrew/include)
link_directories(/opt/homebrew/lib)
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g")
project(LunchRest)
list(APPEND Restaurants
restaurants/ukarla.cpp
restaurants/tao.cpp
restaurants/mahostina.cpp
restaurants/menicka.cpp
)
add_executable(lunchrest)
target_sources(lunchrest
PRIVATE ${Restaurants}
PRIVATE main.cpp
PRIVATE meal.cpp
PRIVATE menu.cpp
PRIVATE network/network.cpp
PRIVATE environment.cpp
PRIVATE restaurants/functions.cpp
)
target_link_libraries(lunchrest ${LIBXML_LIBRARIES} ${LIBXMLPP_LIBRARIES} ${Restbed} ${Curl})
target_include_directories(lunchrest PUBLIC ${LIBXML_INCLUDE_DIRS} ${LIBXMLPP_INCLUDE_DIRS})

44
Makefile Normal file
View File

@ -0,0 +1,44 @@
CXX ?= g++
CFLAGS ?= -O2 -Wall -Wextra `pkg-config libxml-2.0 --cflags` `pkg-config libxml++-3.0 --cflags` -g
PREFIX ?= /usr/local/bin
LDFLAGS ?= -lcurl -lrestbed `pkg-config libxml-2.0 --libs` `pkg-config libxml++-3.0 --libs`
PARSERS = udrevaka.o padagali.o lightofindia.o ukarla.o alcapone.o plac.o zo.o suzzies.o
.PHONY: default
default: menuprint
menuprint: main.o meal.o menu.o network.o restaurants.o environment.o $(PARSERS)
$(CXX) $(CFLAGS) -o $@ $^ ${LDFLAGS}
main.o: main.cpp restaurant.hpp menu.hpp meal.hpp restaurants/restaurants.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
meal.o: meal.cpp meal.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
menu.o: menu.cpp menu.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
network.o: network/network.cpp network/network.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
restaurants.o: restaurants/restaurants.cpp restaurants/restaurants.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
environment.o: environment.cpp environment.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
udrevaka.o: restaurants/udrevaka.cpp restaurants.o restaurants/restaurants.hpp network/network.hpp htmlparser.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
padagali.o: restaurants/padagali.cpp restaurants.o restaurants/restaurants.hpp network/network.hpp htmlparser.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
lightofindia.o: restaurants/lightofindia.cpp restaurants.o restaurants/restaurants.hpp network/network.hpp htmlparser.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
ukarla.o: restaurants/ukarla.cpp restaurants.o restaurants/restaurants.hpp network/network.hpp htmlparser.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
alcapone.o: restaurants/alcapone.cpp restaurants.o restaurants/restaurants.hpp network/network.hpp htmlparser.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
plac.o: restaurants/plac.cpp restaurants/restaurants.hpp network/network.hpp htmlparser.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
zo.o: restaurants/zo.cpp restaurants/restaurants.hpp network/network.hpp htmlparser.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
suzzies.o: restaurants/suzzies.cpp restaurants/restaurants.hpp network/network.hpp environment.hpp
$(CXX) $(CFLAGS) -c -o $@ $<
clean:
rm -Rf *.o menuprint

161
main.cpp
View File

@ -1,8 +1,7 @@
#include "environment.hpp"
#include "meal.hpp"
#include "menu.hpp"
#include "restaurant.hpp"
#include "restaurants/menicka.hpp"
#include "menu.hpp"
#include "meal.hpp"
#include "restaurants/restaurants.hpp"
#include <fstream>
@ -15,137 +14,157 @@
std::map<std::string, std::unique_ptr<LunchRest::Restaurant>> restaurants;
void sendResponse(const std::string &response, int status_code,
const std::shared_ptr<restbed::Session> session,
const std::string &content_type = "text/plain") {
session->close(status_code, response,
{ { "Content-Length", std::to_string(response.length()) },
{ "Access-Control-Allow-Origin", "*" },
{ "Content-Type", content_type } });
std::string file_info_name = "data_backup";
struct tm last_save;
void sendResponse(const std::string &response, int status_code, const std::shared_ptr< restbed::Session > session) {
session->close(status_code, response, { { "Content-Length", std::to_string(response.length()) }, { "Access-Control-Allow-Origin", "*" } });
}
void refresh(const std::shared_ptr<restbed::Session> session) {
for (auto &restaurant : restaurants)
void refresh( const std::shared_ptr< restbed::Session > session ) {
for(auto &restaurant : restaurants)
restaurant.second->parse();
std::string response = "Refreshed menus!";
sendResponse("Refreshed menus!", restbed::OK, session);
sendResponse( "Refreshed menus!", restbed::OK, session );
}
std::string get_all_json(const std::vector<int> &days = { 0, 1, 2, 3, 4, 5,
6 }) {
std::string get_all_json() {
std::stringstream ss{};
bool atleastonerest = false;
ss << "[";
for (auto &restaurant : restaurants) {
for(auto &restaurant : restaurants) {
atleastonerest = true;
ss << restaurant.second->jsonify(days) << ",";
ss << restaurant.second->jsonify() << ",";
}
if (atleastonerest)
if(atleastonerest)
ss.seekp(-1, ss.cur);
ss << "]";
return ss.str();
}
void get_all(const std::shared_ptr<restbed::Session> session) {
sendResponse(get_all_json(), restbed::OK, session, "application/json");
void backupData() {
time_t now = time(NULL);
struct tm *local = localtime(&now);
if(last_save.tm_year == local->tm_year && last_save.tm_mon == local->tm_mon && last_save.tm_mday == local->tm_mday)
return;
std::ofstream output_file;
output_file.open(file_info_name, std::ios_base::app);
auto write_buffer = get_all_json();
write_buffer += "\n";
output_file.write(write_buffer.c_str(), write_buffer.size());
last_save.tm_year = local->tm_year;
last_save.tm_mon = local->tm_mon;
last_save.tm_mday = local->tm_mday;
}
void get(const std::shared_ptr<restbed::Session> session) {
void get_all( const std::shared_ptr< restbed::Session > session ) {
sendResponse(get_all_json(), restbed::OK, session);
}
void get( const std::shared_ptr< restbed::Session > session ) {
// TODO better
int day = -1;
std::string restaurant = "";
auto request = session->get_request();
for (const auto &query_param : request->get_query_parameters()) {
if (query_param.first == "day") {
if (query_param.second == "monday") {
for(const auto &query_param : request->get_query_parameters()) {
if(query_param.first == "day") {
if(query_param.second == "monday") {
day = 0;
} else if (query_param.second == "tuesday") {
} else if(query_param.second == "tuesday") {
day = 1;
} else if (query_param.second == "wednesday") {
} else if(query_param.second == "wednesday") {
day = 2;
} else if (query_param.second == "thursday") {
} else if(query_param.second == "thursday") {
day = 3;
} else if (query_param.second == "friday") {
} else if(query_param.second == "friday") {
day = 4;
} else if (query_param.second == "saturday") {
} else if(query_param.second == "saturday") {
day = 5;
} else {
day = 6;
}
}
if (query_param.first == "restaurant")
if(query_param.first == "restaurant")
restaurant = query_param.second;
}
if (day == -1 && restaurant == "") {
sendResponse("DIDN'T SPECIFY DAY OR RESTAURANT", restbed::BAD_REQUEST,
session);
if(day == -1 && restaurant == "") {
sendResponse("DIDN'T SPECIFY DAY OR RESTAURANT", restbed::BAD_REQUEST, session);
return;
}
if (restaurant != "" && restaurants.find(restaurant) == restaurants.end()) {
sendResponse("YOU DIDN'T SPECIFY A VALID RESTAURANT!",
restbed::BAD_REQUEST, session);
if(restaurant != "" && restaurants.find(restaurant) == restaurants.end()) {
sendResponse("YOU DIDN'T SPECIFY A VALID RESTAURANT!", restbed::BAD_REQUEST, session);
return;
}
std::string response{};
if (day != -1 && restaurant != "") {
response = "[" + restaurants[restaurant]->jsonify({ day }) + "]";
} else if (day != -1) {
response = get_all_json({ day });
} else if (restaurant != "") {
response = "[" + restaurants[restaurant]->jsonify() + "]";
std::stringstream ss{};
if(day != -1 && restaurant != "") {
ss << "[" << restaurants[restaurant]->jsonify({day}) << "]";
} else if(day != -1) {
ss << "[";
bool atleastone = false;
for(auto &restaurant : restaurants) {
atleastone = true;
ss << restaurant.second->jsonify({day}) << ",";
}
sendResponse(response, restbed::OK, session, "application/json");
if(atleastone)
ss.seekp(-1, ss.cur);
ss << "]";
} else if(restaurant != "") {
ss << "[" << restaurants[restaurant]->jsonify() << "]";
}
sendResponse(ss.str(), restbed::OK, session);
}
int main(int /*UNUSED*/, char ** /*UNUSED*/, char **env) {
int main(int argc, char **argv, char **env) {
setupEnv(env);
restaurants["udrevaka"] = std::make_unique<LunchRest::MenickaRestaurant>("https://www.menicka.cz/2752-u-drevaka-beergrill.html", "U Dřeváka");
restaurants["padagali"] = std::make_unique<LunchRest::MenickaRestaurant>("https://www.menicka.cz/4116-padagali.html", "Padagali");
restaurants["lightofindia"] = std::make_unique<LunchRest::MenickaRestaurant>("https://www.menicka.cz/5448-light-of-india.html", "Light of India");
restaurants["udrevaka"] = std::make_unique<LunchRest::UDrevakaRestaurant>();
restaurants["padagali"] = std::make_unique<LunchRest::PadagaliRestaurant>();
restaurants["lightofindia"] = std::make_unique<LunchRest::LightOfIndiaRestaurant>();
restaurants["ukarla"] = std::make_unique<LunchRest::UKarlaRestaurant>();
restaurants["alcapone"] = std::make_unique<LunchRest::MenickaRestaurant>("https://www.menicka.cz/2609-pizzeria-al-capone.html", "Al Capone");
restaurants["suzies"] = std::make_unique<LunchRest::MenickaRestaurant>("https://www.menicka.cz/3830-suzies-steak-pub.html", "Suzies");
restaurants["tao"] = std::make_unique<LunchRest::TaoRestaurant>();
restaurants["mahostina"] = std::make_unique<LunchRest::MahostinaRestaurant>();
restaurants["divabara"] = std::make_unique<LunchRest::MenickaRestaurant>("https://www.menicka.cz/6468-diva-bara.html", "Divá Bára");
restaurants["alcapone"] = std::make_unique<LunchRest::AlCaponeRestaurant>();
restaurants["plac"] = std::make_unique<LunchRest::PlacRestaurant>();
restaurants["zo"] = std::make_unique<LunchRest::ZoRestaurant>();
restaurants["suzzies"] = std::make_unique<LunchRest::SuzziesRestaurant>();
std::cout << "Initial parsing" << std::endl;
for (auto &restaurant : restaurants)
for(auto &restaurant : restaurants)
restaurant.second->parse();
std::cout << "Finished parsing" << std::endl;
last_save.tm_year = 0;
last_save.tm_mon = 0;
last_save.tm_mday = 0;
backupData();
restbed::Service service;
auto getallserv = std::make_shared<restbed::Resource>();
auto getallserv = std::make_shared< restbed::Resource >();
getallserv->set_path("/get_all");
getallserv->set_method_handler("GET", get_all);
getallserv->set_method_handler( "GET", get_all );
service.publish(getallserv);
auto getserv = std::make_shared<restbed::Resource>();
auto getserv = std::make_shared< restbed::Resource >();
getserv->set_path("/get");
getserv->set_method_handler("GET", get);
getserv->set_method_handler( "GET", get );
service.publish(getserv);
auto refreshserv = std::make_shared<restbed::Resource>();
auto refreshserv = std::make_shared< restbed::Resource >();
refreshserv->set_path("/refresh");
refreshserv->set_method_handler("GET", refresh);
refreshserv->set_method_handler( "GET", refresh );
service.publish(refreshserv);
/* auto ssl_settings = std::make_shared<restbed::SSLSettings>();
auto ssl_settings = std::make_shared<restbed::SSLSettings>();
ssl_settings->set_http_disabled(true);
ssl_settings->set_private_key(restbed::Uri(
"file:///home/zvon/data/programming/lunch-rest/example.key"));
ssl_settings->set_certificate(restbed::Uri(
"file:///home/zvon/data/programming/lunch-rest/example.crt"));
ssl_settings->set_temporary_diffie_hellman(restbed::Uri(
"file:///home/zvon/data/programming/lunch-rest/dhparams.pem"));
ssl_settings->set_port(1985);*/
ssl_settings->set_private_key(restbed::Uri("file:///home/zvon/data/programming/lunch-rest/example.key"));
ssl_settings->set_certificate(restbed::Uri("file:///home/zvon/data/programming/lunch-rest/example.crt"));
ssl_settings->set_temporary_diffie_hellman(restbed::Uri("file:///home/zvon/data/programming/lunch-rest/dhparams.pem"));
ssl_settings->set_port(1985);
auto settings = std::make_shared<restbed::Settings>();
auto settings = std::make_shared< restbed::Settings >();
settings->set_port(1984);
settings->set_default_header("Connection", "close");
// settings->set_ssl_settings(ssl_settings);
settings->set_default_header( "Connection", "close" );
settings->set_ssl_settings(ssl_settings);
service.start(settings);

55
restaurants/alcapone.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
std::string removeAlergens(const std::string &name) {
int pos = name.length() - 1;
while(name[pos] == ',' || std::isdigit(name[pos]))
pos--;
return name.substr(0,pos);
}
void LunchRest::AlCaponeRestaurant::parse() {
Request r;
auto html = r.get(_url);
if(html == "")
return;
clearMenus();
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto rows = root.find("//table[@class='table table-responsive']/tbody/tr");
int cur_day = -1;
for(auto &row : rows) {
if(row->find("./td[@class='bg1']").size() != 0) {
std::string day = nodeToText(row->find("./td[@class='bg1']/text()")[0]);
if(day.find("pondělí") != std::string::npos)
cur_day = 0;
else if(day.find("úterý") != std::string::npos)
cur_day = 1;
else if(day.find("středa") != std::string::npos)
cur_day = 2;
else if(day.find("čtvrtek") != std::string::npos)
cur_day = 3;
else if(day.find("pátek") != std::string::npos)
cur_day = 4;
else if(day.find("sobota") != std::string::npos)
cur_day = 5;
else if(day.find("neděle") != std::string::npos)
cur_day = 6;
menus[cur_day].setInvalidMenu(false);
continue;
}
if(cur_day < 0)
continue;
auto menu_info = row->find("./td/text()");
auto meal_data = row->find("./td/h3/text()");
std::string menu = nodeToText(menu_info[0]);
std::string name = nodeToText(meal_data[0]);
name = removeAlergens(name);
if(menu == "Polévka") {
menus[cur_day].addMeal(true, name, "", 0);
} else {
menus[cur_day].addMeal(false, name, "", std::stoi(nodeToText(meal_data[1])));
}
}
}

View File

@ -1,10 +0,0 @@
#ifndef LUNCH_REST_FUNCTIONS
#define LUNCH_REST_FUNCTIONS
#include <string>
#include <libxml++/libxml++.h>
namespace LunchRest {
std::string nodeToText(xmlpp::Node *node);
std::string trim(const std::string &input);
}
#endif

View File

@ -0,0 +1,44 @@
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
bool isWhiteSpaceOnly(const std::string &text) {
if(text == "")
return true;
for(auto &x : text) {
if(!std::isspace(x))
return false;
}
return true;
}
void LunchRest::LightOfIndiaRestaurant::parse() {
Request r;
auto html = r.get(_url);
if(html == "")
return;
clearMenus();
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto container = root.find("//div[@id='content_container']")[0];
auto texts = container->find(".//td/text()");
int index = -1;
for(auto text : texts) {
std::string text_text = nodeToText(text);
if(isWhiteSpaceOnly(text_text) || text_text.find("Week") != std::string::npos)
continue;
if(text_text[0] == '1')
index++;
auto end = text_text.find(')');
if(end == std::string::npos)
continue;
auto possible_end = text_text.find('g', end);
if(possible_end != std::string::npos)
end = possible_end;
std::string name = text_text.substr(4, end - 3);
int price = std::stoi(text_text.substr(end+1));
bool soup = name.find("soup") == std::string::npos ? false : true;
menus[index].addMeal(soup, name, "", price);
menus[index].setInvalidMenu(false);
}
}

View File

@ -1,46 +0,0 @@
#include "functions.hpp"
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
#include <iostream>
void LunchRest::MahostinaRestaurant::parse() {
Request r;
auto html = r.get(_url);
if (html == "")
return;
clearMenus();
try {
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto today_lists = root.find(
"//div[@id='dnesnibasta-section']//ul[@data-rte-list='default']");
if (today_lists.empty()) {
std::cout << "No meals :(" << std::endl;
return;
}
time_t t = time(nullptr);
tm *timePtr = localtime(&t);
auto day = (timePtr->tm_wday + 6) % 7;
for (auto &meal : today_lists[0]->find("./li/p/text()")) {
auto text = nodeToText(meal);
auto price_end = text.find(",-");
auto price_start = price_end - 1;
while (text[price_start] >= '0' && text[price_start] <= '9') {
price_start -= 1;
}
price_start += 1;
menus[day].addMeal(
false, text.substr(0, price_start - 1), "",
std::stoi(text.substr(price_start, price_end - price_start)));
}
menus[day].setInvalidMenu(false);
} catch (std::exception &/*UNUSED*/) {
clearMenus();
for(int i = 0; i < menus.size(); i++) {
menus[i].addMeal(false, parseError, parseInfo, 0);
}
}
}

View File

@ -1,81 +0,0 @@
#include "menicka.hpp"
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
#include "functions.hpp"
std::pair<std::string, int> getNameAndPrice(xmlpp::Node *meal) {
auto souptext = meal->find("./div[@class='polozka']/text()");
auto soupprice = meal->find("./div[@class='cena']/text()");
std::string name = "";
int price = -1;
if(souptext.size() != 0) {
name = LunchRest::nodeToText(souptext[0]);
}
if(soupprice.size() != 0) {
price = std::stoi(LunchRest::nodeToText(soupprice[0]));
}
return {name, price};
}
void LunchRest::MenickaRestaurant::parse() {
Request r;
auto html = r.get(_url);
if(html == "")
return;
clearMenus();
try {
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto days = root.find("//div[@class='menicka']");
for(auto &day : days) {
auto daytext = day->find("./div[@class='nadpis']/text()");
auto soup = day->find("./ul/li[@class='polevka']");
auto meals = day->find("./ul/li[@class='jidlo']");
if(daytext.size() == 0) {
continue;
}
auto daystr = nodeToText(daytext[0]);
int cur_day = 0;
switch (daystr[0]) {
case 'P':
if (daystr[1] != 'o') {
cur_day = 4;
}
break;
case "Ú"[0]:
cur_day = 1;
break;
case 'S':
cur_day = 2;
break;
case "Č"[0]:
cur_day = 3;
default:
break;
}
if(soup.size() != 0) {
auto soupData = getNameAndPrice(soup[0]);
if(!soupData.first.empty()) {
menus[cur_day].addMeal(Meal(true, soupData.first, "", soupData.second));
}
}
for(auto &meal : meals) {
auto mealData = getNameAndPrice(meal);
if(!mealData.first.empty()) {
menus[cur_day].addMeal(Meal(false, mealData.first, "", mealData.second));
}
}
if(!menus[cur_day].getMeals().empty()) {
menus[cur_day].setInvalidMenu(false);
}
}
} catch (std::exception &/*UNUSED*/) {
clearMenus();
for(int i = 0; i < menus.size(); i++) {
menus[i].addMeal(false, parseError, parseInfo, 0);
}
}
}

View File

@ -1,13 +0,0 @@
#ifndef LUNCH_REST_MENICKA
#define LUNCH_REST_MENICKA
#include "../restaurant.hpp"
namespace LunchRest {
class MenickaRestaurant : public Restaurant {
public:
MenickaRestaurant(const std::string &url, const std::string &name) : Restaurant(url, name) {}
virtual ~MenickaRestaurant() = default;
virtual void parse() override;
};
}
#endif

53
restaurants/padagali.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
int dayToNum(const std::string &day) {
if(day.find("POND") != std::string::npos) {
return 0;
}
if (day.find("ÚTER") != std::string::npos) {
return 1;
}
if (day.find("STŘE") != std::string::npos) {
return 2;
}
if (day.find("ČTVR") != std::string::npos) {
return 3;
}
if (day.find("PÁTE") != std::string::npos) {
return 4;
}
return -1;
}
void LunchRest::PadagaliRestaurant::parse() {
Request r;
auto html = r.get(_url);
if(html.empty()) {
return;
}
clearMenus();
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto days = root.find("//div[@class='glf-mor-restaurant-menu-category']");
auto menu_index = dayToNum(nodeToText(days[0]->find("./h3/text()")[0]));
if(menu_index == -1) {
return;
}
auto max_div = 5 - menu_index;
for(int i = 0; i < max_div; i++) {
auto *day = days[i];
auto meals = day->find("./div");
for(auto &meal : meals) {
auto info = meal->find("./div/div/div");
std::string desc = nodeToText(info[1]->find("./text()")[0]);
std::string name = nodeToText(info[0]->find("./h5/text()")[0]);
int price = std::stoi(nodeToText(info[0]->find("./div/span/text()")[0]));
bool soup = name.find("Soup") != std::string::npos;
menus[menu_index].addMeal(soup, name, desc, price);
menus[menu_index].setInvalidMenu(false);
}
menu_index++;
}
}

50
restaurants/plac.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
void LunchRest::PlacRestaurant::parse() {
Request r;
auto html = r.get(_url);
if(html == "")
return;
clearMenus();
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto pizzas = root.find("//div[@class='mt-c cf']//div[@class='mt-i cf']");
if(pizzas.size() == 0)
return;
auto soups = pizzas[0]->find(".//div[@class='b b-text cf']");
int soup_price = std::stoi(nodeToText(soups[1]->find(".//strong/text()")[0]));
int cur_day = -1;
for(auto &soup : soups[0]->find(".//p/text()")) {
std::string soup_text = nodeToText(soup);
auto soup_day = soup_text.substr(0,2);
if(soup_day == "Po")
cur_day = 0;
if(cur_day < 0)
continue;
menus[cur_day].setInvalidMenu(false);
auto soupname = soup_text.substr(5);
while(std::isspace(soupname[0]))
soupname = soupname.substr(1);
menus[cur_day].addMeal(true, soupname, "", soup_price);
cur_day++;
if(cur_day > 4)
break;
}
for(unsigned long int i = 2; i < pizzas.size(); i++) {
auto content = pizzas[i]->find(".//div[@class='b b-text cf']");
auto name_candidates = content[0]->find(".//h3/text()");
std::string name = nodeToText(name_candidates[0]);
auto desc_candidates = content[0]->find(".//p//text()");
std::string desc = nodeToText(desc_candidates[0]);
auto price_candidates = content[1]->find(".//p/strong/text()");
int price = std::stoi(nodeToText(price_candidates[0]));
name = trim(name);
desc = trim(desc);
addPermanent(false, name, desc, price);
}
}

View File

@ -1,4 +1,4 @@
#include "functions.hpp"
#include "restaurants.hpp"
std::string LunchRest::nodeToText(xmlpp::Node *node) {
return dynamic_cast<const xmlpp::ContentNode *>(node)->get_content();

View File

@ -2,25 +2,57 @@
#include <libxml++/libxml++.h>
namespace LunchRest {
const std::string parseError = "Could not retreive menu";
const std::string parseInfo = "Please contact the developer, likely the restaurant website changed and requires a new parser";
std::string nodeToText(xmlpp::Node *node);
std::string trim(const std::string &input);
class UDrevakaRestaurant : public Restaurant {
public:
UDrevakaRestaurant() : Restaurant("https://udrevaka.cz/denni-menu/", "U Dřeváka") {}
virtual ~UDrevakaRestaurant() = default;
virtual void parse() override;
};
class PadagaliRestaurant : public Restaurant {
public:
PadagaliRestaurant() : Restaurant("https://padagali.cz/denni-menu/", "Padagali") {}
virtual ~PadagaliRestaurant() = default;
virtual void parse() override;
};
class LightOfIndiaRestaurant : public Restaurant {
public:
LightOfIndiaRestaurant() : Restaurant("http://lightofindia.cz/lang-en/denni-menu", "Light of India") {}
virtual ~LightOfIndiaRestaurant() = default;
virtual void parse() override;
};
class UKarlaRestaurant : public Restaurant {
public:
UKarlaRestaurant() : Restaurant("https://ukarlabrno.cz/denni-menu/", "U Karla") {}
virtual ~UKarlaRestaurant() = default;
virtual void parse() override;
};
class TaoRestaurant : public Restaurant {
class AlCaponeRestaurant : public Restaurant {
public:
TaoRestaurant() : Restaurant("https://www.taorestaurant.cz/tydenni_menu/nabidka/", "Táo Viet Nam") {}
virtual ~TaoRestaurant() = default;
AlCaponeRestaurant() : Restaurant("https://www.pizzaalcapone.cz/cz/poledni-menu", "Al Capone") {}
virtual ~AlCaponeRestaurant() = default;
virtual void parse() override;
};
class MahostinaRestaurant : public Restaurant {
class PlacRestaurant : public Restaurant {
public:
MahostinaRestaurant() : Restaurant("https://www.mahostina.cz/", "Má Hostina") {}
virtual ~MahostinaRestaurant() = default;
PlacRestaurant() : Restaurant("https://www.bistroplac.cz/poledni-nabidka/", "Bistro Plac") {}
virtual ~PlacRestaurant() = default;
virtual void parse() override;
};
class ZoRestaurant : public Restaurant {
public:
ZoRestaurant();
virtual ~ZoRestaurant() = default;
virtual void parse() override {};
};
class SuzziesRestaurant : public Restaurant {
public:
SuzziesRestaurant() : Restaurant("http://suzies.cz/poledni-menu.html", "Suzzie's") {}
virtual ~SuzziesRestaurant() = default;
virtual void parse() override;
private:
std::string api_key;
};
} // end of namespace LunchRest

61
restaurants/suzzies.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
int weekDay(const std::string &date_str) {
struct tm date_tm;
strptime(date_str.c_str(), "%Y-%m-%d %H:%M:%S", &date_tm);
return (date_tm.tm_wday + 6) % 7;
}
void LunchRest::SuzziesRestaurant::parse() {
Request r;
auto html = r.get(_url);
if(html == "")
return;
clearMenus();
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto days = root.find("//div[@class='day']");
for(auto &day : days) {
auto *daynum = xmlGetProp(day->cobj(), (xmlChar*)"data-day");
int cur_day = 0;
switch(daynum[0]) {
case '2':
cur_day = 1;
break;
case '3':
cur_day = 2;
break;
case '4':
cur_day = 3;
break;
case '5':
cur_day = 4;
default:
break;
}
for(auto &meal : day->find(".//div[@class='item']")) {
Meal meal_obj{};
auto type = trim(nodeToText(meal->find(".//h6/text()")[0]));
auto name_nodes = meal->find(".//div[@class='title']/text()");
auto text_nodes = meal->find(".//div[@class='text']/text()");
auto price_nodes = meal->find(".//div[@class='price']/text()");
if(price_nodes.size() > 0) {
// not soup
auto name = type;
if(name_nodes.size() > 0)
name += " - " + trim(nodeToText(name_nodes[0]));
meal_obj.setName(name);
if(text_nodes.size() > 0)
meal_obj.setDesc(trim(nodeToText(text_nodes[0])));
meal_obj.setPrice(std::stoi(trim(nodeToText(price_nodes[0]))));
} else {
meal_obj.setName(trim(nodeToText(name_nodes[0])));
meal_obj.setSoup();
}
menus[cur_day].addMeal(meal_obj);
}
menus[cur_day].setInvalidMenu(false);
}
}

View File

@ -1,122 +0,0 @@
#include "functions.hpp"
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
#include <iostream>
std::pair<size_t, size_t> getPricePosFromText(const std::string &text) {
size_t price_pos = 0, possible_price = 0;
while (possible_price != std::string::npos) {
price_pos = possible_price;
possible_price = text.find('k', possible_price + 1);
}
if(price_pos == 0 || price_pos == std::string::npos) {
return {-1, -1};
}
auto end_pos = price_pos;
price_pos -= 1;
while (text[price_pos] >= '0' && text[price_pos] <= '9') {
price_pos -= 1;
}
price_pos += 1;
return { price_pos, end_pos - price_pos };
}
size_t getEndOfTextPos(const std::string &text, size_t price_pos) {
auto end_pos = price_pos -= 1;
while (text[end_pos] == '.' || text[end_pos] == ' ') {
end_pos -= 1;
}
end_pos += 1;
return end_pos;
}
void LunchRest::TaoRestaurant::parse() {
Request r;
auto html = r.get(_url);
if (html == "")
return;
clearMenus();
try {
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto week_meals_html =
root.find("//div[@class='ct-div-block']/div[@class='ct-div-block "
"tydenni-menu-div']");
if (week_meals_html.empty()) {
std::cout << "No week meals :(" << std::endl;
return;
}
auto daily_meals_html =
root.find("//div[@class='ct-section-inner-wrap']/"
"div[@class='ct-div-block tydenni-menu-div']");
if (daily_meals_html.empty()) {
std::cout << "No daily meals :(" << std::endl;
return;
}
std::vector<Meal> week_meals{};
for (auto &meal : week_meals_html) {
auto texts = meal->find(".//span/text()");
if (!texts.empty()) {
auto text = nodeToText(texts[0]);
auto price_positions = getPricePosFromText(text);
if(price_positions.first == (size_t)-1) {
week_meals.emplace_back(false, text, "", 0);
continue;
}
auto end_pos = getEndOfTextPos(text, price_positions.first);
week_meals.emplace_back(
false, text.substr(0, end_pos), "",
std::stoi(text.substr(price_positions.first,
price_positions.second)));
}
}
for (auto &meal : daily_meals_html) {
auto texts = meal->find(".//span/text()");
auto days = meal->find(".//div/b/text()");
if (!texts.empty() && !days.empty()) {
auto text = nodeToText(texts[0]);
auto day = nodeToText(days[0]);
auto price_positions = getPricePosFromText(text);
if(price_positions.first == (size_t)-1) {
continue;
}
auto end_pos = getEndOfTextPos(text, price_positions.first);
int cur_day = 0;
switch (day[0]) {
case 'P':
if (day[1] != 'o') {
cur_day = 4;
}
break;
case "Ú"[0]:
cur_day = 1;
break;
case 'S':
cur_day = 2;
break;
case "Č"[0]:
cur_day = 3;
default:
break;
}
Meal cur_meal(false, text.substr(0, end_pos), "",
std::stoi(text.substr(price_positions.first,
price_positions.second)));
menus[cur_day].addMeal(cur_meal);
for (auto &meal : week_meals) {
menus[cur_day].addMeal(meal);
}
menus[cur_day].setInvalidMenu(false);
}
}
} catch (std::exception &/*UNUSED*/) {
clearMenus();
for(int i = 0; i < menus.size(); i++) {
menus[i].addMeal(false, parseError, parseInfo, 0);
}
}
}

36
restaurants/udrevaka.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
void LunchRest::UDrevakaRestaurant::parse() {
int menu_index = 0;
Request r;
auto html = r.get(_url);
if(html == "")
return;
clearMenus();
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto days = root.find("//li[@class='item-day']");
for(auto &day : days) {
auto meals = day->find("./div[@class='row']");
for(auto &meal : meals) {
auto divs = meal->find(".//div/text()");
Meal meal_obj{};
std::string name = trim(nodeToText(divs[0]));
auto soup_pos = name.find("Polévka");
if(soup_pos != std::string::npos) {
meal_obj.setSoup();
meal_obj.setName(name.substr(10, name.find('(') - 11));
} else {
meal_obj.setName(name.substr(3, name.find('(') - 4));
}
if(divs.size() > 1) {
meal_obj.setPrice(std::stoi(trim(nodeToText(divs[1]))));
}
menus[menu_index].addMeal(meal_obj);
menus[menu_index].setInvalidMenu(false);
}
menu_index++;
}
}

View File

@ -1,4 +1,3 @@
#include "functions.hpp"
#include "restaurants.hpp"
#include "../network/network.hpp"
#include "../htmlparser.hpp"
@ -10,7 +9,6 @@ void LunchRest::UKarlaRestaurant::parse() {
if(html == "")
return;
clearMenus();
try {
HtmlParser hparse(html);
auto &root = hparse.getRoot();
auto days = root.find("//li[@class='item-day']");
@ -21,10 +19,6 @@ void LunchRest::UKarlaRestaurant::parse() {
for(auto &meal : meals) {
auto soup = false;
auto texts = meal->find("./div/text()");
if(texts.empty()) {
menus[menu_index].addMeal(false, parseError, parseInfo, 0);
continue;
}
std::string name = trim(nodeToText(texts[0]));
if(name[0] == 'P') {
soup = true;
@ -40,10 +34,4 @@ void LunchRest::UKarlaRestaurant::parse() {
}
menu_index++;
}
} catch (std::exception &/*UNUSED*/) {
clearMenus();
for(int i = 0; i < menus.size(); i++) {
menus[i].addMeal(false, parseError, parseInfo, 0);
}
}
}

31
restaurants/zo.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "restaurants.hpp"
LunchRest::ZoRestaurant::ZoRestaurant() : Restaurant("", "Zo!") {
clearMenus();
addPermanent(true, "Pho Bo", "Rýžové nudle pho v hovězím vývaru, hovězí maso, vietnamské bylinky", 120);
addPermanent(true, "Pho Bo Tai Lan", "Rýžové nudle pho v hovězím vývaru, RESTOVANÉ hovězí maso, vietnamské bylinky", 120);
addPermanent(true, "Bun Bo Hue", "Rýžové nudle bun v hovězím vývaru po Hue, masové kuličky, krevetová pasta", 120);
addPermanent(true, "Bun Tom", "Rýžové nudle bun s krevety", 120);
addPermanent(true, "Bun Dau", "Rýžové nudle bun v zeleninovém vývaru, tofu", 120);
addPermanent(true, "Pho Bo Bam", "Rýžové nudle pho, syrové nasekané hovězí maso zalité hovězím vývarem, vietnamské bylinky", 135);
addPermanent(true, "Bun Moc", "Rýžové nudle bun, kuřecí vývar s masovými kuličky, houbami a bylinkami", 125);
addPermanent(false, "Bun Bo Nam Bo", "Rýžové nudle bun, hovězí maso, vietnamské bylinky, sladkokyselá zálivka", 135);
addPermanent(false, "Bun Ga Tron", "Rýžové nudle bun s restovaným kuřecím masem, vietnamské bylinky, omáčka ZO!", 135);
addPermanent(false, "Bun Dau Zo", "Podobné jako Bun Bo Nam Bo, ale s tofu", 120);
addPermanent(false, "Bun Tom Tron", "Rýžové nudle bun s krevety, vietnamské bylinky, omáčka ZO!", 135);
addPermanent(false, "Zo Bowl", "Miska plná rýžových nudlí a zeleniny, hrášek, houby shitake, brokolice, mungo, arašídová omáčka", 135);
addPermanent(false, "Mi Orizo", "Orestované nudle z naší kuchyně, Vyberte si: kuřecí (120), hovězí (130)", 120);
addPermanent(false, "Ban Da Xao", "Orestované jižanské placaté rýžové nudle, Vyberte si: kuřecí (120), hovězí (135)", 120);
addPermanent(false, "Mi Udozo", "Orestované udon nudle po našem stylu, Vyberte si: kuřecí, hovězí", 135);
addPermanent(false, "Bun Ngan Nuong", "Grilované kachní maso, rýžové nudle bun, vietnamské bylinky, sojová omáčka", 155);
addPermanent(false, "Bun Cha", "Grilované vepřové maso, rýžové nudle bun, vietnamské bylinky, sladkokyselá zálivka", 135);
addPermanent(false, "Salát Bo Zo", "Smažené obalené kousky avokáda, zelenina, ředkev", 135);
addPermanent(false, "Com Xuat dle šéfkuchaře", "Vyberte si základ (kuřecí, vepřové, hovězí, krevetové, tofu), my domyslíme zbytek, rýže, zelenina", 135);
addPermanent(false, "Rýže s kari omáčkou", "Vyberte si: kuřecí, hovězí, krevety, tofu", 120);
addPermanent(false, "Vitský", "Smažená kachna podávaná s bambusovými výhonky a rýží", 155);
addPermanent(false, "Nem Ran 2ks", "Smažené rolky", 50);
addPermanent(false, "Nem Cuon Tom 2ks", "Smažené rolky plněné krevetami", 50);
addPermanent(false, "Nem Cuon Bo 2ks", "Nesmažené rolky plněné avokádem podle Zo", 70);
addPermanent(false, "Nem Cuon Vege 2ks", "Nesmažené rolky plněné tofu", 50);
addPermanent(false, "Nem Ran Vege 2ks", "Smažené rolky plněné zeleninou (bez masa)", 50);
}