335 lines
12 KiB
C++
335 lines
12 KiB
C++
#include "utils.h"
|
||
#ifdef BUILD_OFD_BINARYEYE_SCAN
|
||
# include <arpa/inet.h>
|
||
# include <qrencode.h>
|
||
# include <ifaddrs.h>
|
||
# include <netinet/in.h>
|
||
#endif
|
||
#ifdef BUILD_OFD_MODE
|
||
# include "../exceptions/ofdrequestexception.h"
|
||
#endif
|
||
#include <codecvt>
|
||
#include <cstring>
|
||
#include <iostream>
|
||
#include <locale>
|
||
#if defined(BUILD_OCR_MODE) || defined(BUILD_OFD_MODE)
|
||
# include <opencv2/core/mat.hpp>
|
||
# include <opencv2/imgcodecs.hpp>
|
||
# include <opencv2/imgproc.hpp>
|
||
#endif
|
||
#include <string>
|
||
#include <QWidget>
|
||
#include <fstream>
|
||
#include <boost/regex.hpp>
|
||
#include <net/net.h>
|
||
#include <parser/parser.h>
|
||
#include <settings/settings.h>
|
||
|
||
|
||
#ifdef BUILD_OFD_BINARYEYE_SCAN
|
||
std::string get_local_ip_address() {
|
||
struct ifaddrs * ifAddrStruct=NULL;
|
||
struct ifaddrs * ifa=NULL;
|
||
void * tmpAddrPtr=NULL;
|
||
|
||
getifaddrs(&ifAddrStruct);
|
||
|
||
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
|
||
if (ifa->ifa_addr == nullptr) continue;
|
||
if (ifa->ifa_addr->sa_family==AF_INET) {
|
||
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
||
char addressBuffer[128];
|
||
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
|
||
|
||
std::string value(addressBuffer);
|
||
if (!strncmp(value.c_str(), "192.168", 7)) {
|
||
return value;
|
||
}
|
||
}
|
||
}
|
||
if (ifAddrStruct!=NULL)
|
||
freeifaddrs(ifAddrStruct);
|
||
|
||
throw std::runtime_error(QWidget::tr("Could not find any usable local IP address. If you beleive that this is problem with the program, please, contact the developer.").toStdString());
|
||
}
|
||
#endif
|
||
|
||
std::string to_utf8(std::wstring wide_string) {
|
||
static std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||
return utf8_conv.to_bytes(wide_string);
|
||
}
|
||
|
||
std::wstring from_utf8(std::string string) {
|
||
static std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
|
||
return utf8_conv.from_bytes(string);
|
||
}
|
||
|
||
std::string get_path_relative_to_home(std::string path) {
|
||
return std::string(std::getenv("HOME")) + "/" + path;
|
||
}
|
||
|
||
template <typename T>
|
||
bool vector_contains_element(const std::vector<T>& vector, const T& to_find) {
|
||
for (const T& element : vector) {
|
||
if (element == to_find) return true;
|
||
}
|
||
return false;
|
||
}
|
||
template<class T>
|
||
bool areAllSizesEqual(const std::vector<T>& v1, const std::vector<T>& v2,
|
||
const std::vector<T>& v3, const std::vector<T>& v4) {
|
||
return (v1.size() == v2.size() && v2.size() == v3.size() && v3.size() == v4.size());
|
||
}
|
||
|
||
|
||
//ужас
|
||
template bool vector_contains_element<std::string>(const std::vector<std::string>& vector, const std::string& to_find);
|
||
template bool areAllSizesEqual(const std::vector<std::string>& v1, const std::vector<std::string>& v2,
|
||
const std::vector<std::string>& v3, const std::vector<std::string>& v4);
|
||
std::vector<std::string> split(std::string s, std::string delimiter) {
|
||
std::vector<std::string> result;
|
||
size_t pos = 0;
|
||
std::string token;
|
||
while ((pos = s.find(delimiter)) != std::string::npos) {
|
||
token = s.substr(0, pos);
|
||
result.push_back(token);
|
||
s.erase(0, pos + delimiter.length());
|
||
}
|
||
result.push_back(s);
|
||
|
||
return result;
|
||
}
|
||
|
||
std::vector<std::wstring> split(std::wstring s, std::wstring delimiter) {
|
||
std::vector<std::wstring> result;
|
||
size_t pos = 0;
|
||
std::wstring token;
|
||
while ((pos = s.find(delimiter)) != std::string::npos) {
|
||
token = s.substr(0, pos);
|
||
result.push_back(token);
|
||
s.erase(0, pos + delimiter.length());
|
||
}
|
||
result.push_back(s);
|
||
|
||
return result;
|
||
}
|
||
|
||
std::wstring substring_from_to(std::wstring& text, std::wstring from, std::wstring to) {
|
||
unsigned int start_pos = 0;
|
||
unsigned int end_pos = 0;
|
||
|
||
std::wstring substring;
|
||
|
||
boost::wregex start_regex(from);
|
||
boost::wregex end_regex(to);
|
||
|
||
for (boost::wsregex_iterator it{text.begin(), text.end(), start_regex}, end{};
|
||
it != end; it++) {
|
||
start_pos = it->position() + it->str().size();
|
||
break;
|
||
}
|
||
|
||
if(text == from_utf8("")) return text;
|
||
substring = text.substr(start_pos, text.size());
|
||
|
||
for (boost::wsregex_iterator it{substring.begin(), substring.end(), end_regex}, end{};
|
||
it != end; it++) {
|
||
end_pos = it->position();
|
||
break;
|
||
}
|
||
|
||
if (end_pos == 0) return substring;
|
||
|
||
substring = substring.substr(0, end_pos);
|
||
|
||
return substring;
|
||
}
|
||
#ifdef BUILD_OFD_MODE
|
||
std::wstring trim_html_response(std::wstring& check) {
|
||
std::wstring begin_check_marker = from_utf8("<!-- Products -->");
|
||
std::wstring end_check_marker = from_utf8("<!-- \\/Products -->");
|
||
std::wstring trimmed = substring_from_to(check, begin_check_marker, end_check_marker);
|
||
trimmed += from_utf8("\n</div>");
|
||
return trimmed;
|
||
}
|
||
|
||
std::vector<std::wstring> find_in_html(std::string& html, std::string regex, std::string html_start, std::string html_end) {
|
||
boost::regex searching_regex(regex);
|
||
|
||
std::vector<std::wstring> parsed;
|
||
for (boost::sregex_iterator it{html.begin(), html.end(), searching_regex}, end{};
|
||
it != end; it++) {
|
||
|
||
std::wstring found_entry = from_utf8(it->str());
|
||
std::cout << "Found: " << to_utf8(found_entry) << std::endl;
|
||
std::wstring extracted = substring_from_to(found_entry, from_utf8(html_start), from_utf8(html_end));
|
||
std::cout << "Extracted: " << to_utf8(extracted) << std::endl;
|
||
parsed.push_back(extracted);
|
||
}
|
||
return parsed;
|
||
}
|
||
|
||
std::vector<std::wstring> find_products_in_html(std::string html) {
|
||
return find_in_html(html, "<div class=\"ifw-col ifw-col-1 text-left\"><b>.{2,100}<\\/b><\\/div>", "<div class=\"ifw-col ifw-col-1 text-left\"><b>", "<\\/b><\\/div>");
|
||
}
|
||
|
||
std::vector<std::wstring> find_amounts_in_html(std::string html) {
|
||
std::vector<std::wstring> founds = find_in_html(html, "<div><span>\\d+(\\.|\\,)?\\d{0,3}<\\/span>", "<span>", "<\\/span>");
|
||
for (auto &found : founds) {
|
||
std::replace(found.begin(), found.end(), ',', '.');
|
||
}
|
||
|
||
return founds;
|
||
}
|
||
|
||
std::vector<std::wstring> find_net_weights_in_names(std::vector<std::wstring> &names) {
|
||
std::vector<std::wstring> result;
|
||
for (std::wstring &name : names ) {
|
||
boost::wregex regexp(from_utf8("((\\d+(\\.|,)?\\d{0,}((м|)л|(к|м|)г|т|ц|шт|(pc|)s|(m|k|)g|(m|)l|t))(\\s|\\t){0,})+"), boost::regex_constants::collate);
|
||
bool found = false;
|
||
for (boost::wsregex_iterator it{name.begin(), name.end(), regexp}, end{}; it != end;
|
||
it++) {
|
||
result.push_back(it->str());
|
||
found = true;
|
||
name.erase(it->position(), it->str().length());
|
||
break;
|
||
}
|
||
if (!found) {
|
||
result.push_back(from_utf8("?"));
|
||
}
|
||
}
|
||
|
||
for (auto &entry : result) {
|
||
std::replace(entry.begin(), entry.end(), ',', '.');
|
||
}
|
||
return result;
|
||
}
|
||
|
||
std::vector<std::wstring> find_prices_in_html(std::string html) {
|
||
std::vector<std::wstring> founds = find_in_html(html, "X <\\/span><span>\\d+(\\.|,)\\d{2}<\\/span>", "X <\\/span><span>", "<\\/span>");
|
||
for (auto &found : founds) {
|
||
std::replace(found.begin(), found.end(), ',', '.');
|
||
}
|
||
|
||
return founds;
|
||
}
|
||
|
||
void dumpVectorsToStderr(std::vector<std::wstring> &products, std::vector<std::wstring> &amounts, std::vector<std::wstring> &net_weights, std::vector<std::wstring> &prices) {
|
||
std::cerr << "Products: ";
|
||
for (auto &product : products) {
|
||
std::cerr << to_utf8(product) << "|[]|";
|
||
}
|
||
std::cerr << std::endl;
|
||
|
||
std::cerr << "Amounts: ";
|
||
for (auto &amount : amounts) {
|
||
std::wcerr << amount << " ";
|
||
}
|
||
std::cerr << std::endl;
|
||
|
||
std::cerr << "Net weights: ";
|
||
for (auto &net_weight : net_weights) {
|
||
std::wcerr << net_weight << " ";
|
||
}
|
||
std::cerr << std::endl;
|
||
|
||
std::cerr << "Prices: ";
|
||
for (auto &price : prices) {
|
||
std::wcerr << price << " ";
|
||
}
|
||
|
||
std::cerr << std::endl;
|
||
}
|
||
|
||
Check parseOfdRuAnswer(std::string html) {
|
||
std::wstring wstr_html = from_utf8(html);
|
||
std::string trimmed = to_utf8(trim_html_response(wstr_html));
|
||
|
||
std::vector<std::wstring> products = find_products_in_html(trimmed);
|
||
std::vector<std::wstring> amounts = find_amounts_in_html(trimmed);
|
||
std::vector<std::wstring> net_weights = find_net_weights_in_names(products);
|
||
std::vector<std::wstring> prices = find_prices_in_html(trimmed);
|
||
|
||
if ((products.size() + amounts.size() + prices.size()) == 0) {
|
||
if (html == "Bad Request4") { // Failed to solve a captcha
|
||
throw OfdRequestException("Incorrect captcha");
|
||
} else if (html.find("500 - Internal server error.") != std::string::npos) {
|
||
throw OfdRequestException("Internal server error");
|
||
} else { // Most likely that the check does not exist
|
||
throw OfdRequestException("Does not exist");
|
||
}
|
||
return Check();
|
||
}
|
||
|
||
if ((products.size() + amounts.size() + prices.size())/products.size() != 3) {
|
||
dumpVectorsToStderr(products, amounts, net_weights, prices);
|
||
//TOOD: make new setting "app_home" and get all path using it.
|
||
std::ofstream error_log(get_path_relative_to_home(".local/share/checks_parser/error_log.txt"), std::ios_base::app);
|
||
error_log << trimmed << std::endl;
|
||
std::cerr << "An error has occured during the parsing of html. Please, contact the developer." << std::endl;
|
||
std::exit(-1);
|
||
}
|
||
|
||
Check c;
|
||
|
||
for (int i = 0; i < products.size(); i ++) {
|
||
std::cout << "Adding to check: ";
|
||
std::cout << to_utf8(products[i]) << " " << to_utf8(prices[i]) << " " << to_utf8(net_weights[i]) << " " << to_utf8(amounts[i]) << std::endl;
|
||
Goods goods(to_utf8(products[i]), std::stod(prices[i]), to_utf8(net_weights[i]), std::stod(amounts[i]));
|
||
c.add_goods(goods);
|
||
}
|
||
|
||
return c;
|
||
}
|
||
#endif // ifdef BUILD_OFD_MODE
|
||
|
||
#ifdef BUILD_OFD_BINARYEYE_SCAN
|
||
void generate_qr_code(std::string data) {
|
||
QRcode *qrCode = QRcode_encodeString(data.c_str(), 2, QR_ECLEVEL_L, QR_MODE_8, 1);
|
||
if (qrCode == NULL) {
|
||
std::cerr << "Error on generating qr code" << std::endl;
|
||
}
|
||
|
||
cv::Mat qrCodeImage = cv::Mat::zeros(qrCode->width, qrCode->width, CV_8UC3);
|
||
|
||
for (int y = 0; y < qrCode->width; y++) {
|
||
for (int x = 0; x < qrCode->width; x++) {
|
||
cv::rectangle(
|
||
qrCodeImage,
|
||
cv::Point(x, y),
|
||
cv::Point(x + 1, y + 1),
|
||
((qrCode->data[y * qrCode->width + x] & 1) ?
|
||
cv::Scalar(255., 255., 255.) : cv::Scalar(0., 0., 0.)
|
||
),
|
||
-1
|
||
);
|
||
}
|
||
}
|
||
cv::imwrite(get_path_relative_to_home(".local/share/checks_parser/binaryeye_connection.png"), qrCodeImage);
|
||
QRcode_free(qrCode);
|
||
}
|
||
#endif // ifdef BUILD_OFD_BINARYEYE_SCAN
|
||
|
||
|
||
void fetch_and_download_modules() {
|
||
Net n;
|
||
Parser p;
|
||
std::string settings_file_path = get_path_relative_to_home(".local/share/checks_parser/settings.json");
|
||
|
||
Settings s(settings_file_path);
|
||
|
||
std::vector<std::string> storesUpdates = p.check_updates();
|
||
for (auto &update : storesUpdates) {
|
||
std::cout << "Downloading "
|
||
<< s.get_setting("stores_modules_url") + update << " to "
|
||
<< get_path_relative_to_home(s.get_setting("stores_modules_dir") +
|
||
"/" + update)
|
||
<< std::endl;
|
||
|
||
n.get_file(s.get_setting("stores_modules_url") + "/" + update,
|
||
get_path_relative_to_home(s.get_setting("stores_modules_dir") +
|
||
"/" + update));
|
||
|
||
}
|
||
}
|