checks-parser/utils/utils.cpp

335 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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));
}
}