From 33b54fb4754241dab008e74efeff6ae10fbf2123 Mon Sep 17 00:00:00 2001 From: leca Date: Fri, 14 Mar 2025 00:44:14 +0300 Subject: [PATCH] added ofd scene, working http server --- CMakeLists.txt | 22 ++- image/checkimage.cpp | 3 +- main.cpp | 16 +- ofdscene.cpp | 138 ++++++++++++++++++ ofdscene.h | 8 + scenes.qrc | 1 + scenes/ofdscene.ui | 6 +- .../solvecaptchadialog.ui | 0 translations/en_US.ts | 90 +++++++++--- translations/ru_RU.ts | 90 +++++++++--- utils/utils.cpp | 74 +++++++++- utils/utils.h | 1 + 12 files changed, 390 insertions(+), 59 deletions(-) rename solvecaptchadialog.ui => scenes/solvecaptchadialog.ui (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 631b6da..58362ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,10 @@ project(checks-parser VERSION 0.1 LANGUAGES CXX) option(BUILD_TRANSLATIONS "Build translations?" ON) +include(FetchContent) + +SET(CMAKE_BUILD_TYPE Debug) + set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC OFF) @@ -40,7 +44,9 @@ set(PROJECT_SOURCES ocrscene.h ocrscene.cpp scenes/ocrscene.ui ofdscene.h ofdscene.cpp scenes/ofdscene.ui outputdialog.h outputdialog.cpp scenes/outputdialog.ui - # adjustpicturedialog.h adjustpicturedialog.cpp scenes/adjustpicturedialog.ui + adjustpicturedialog.h adjustpicturedialog.cpp scenes/adjustpicturedialog.ui + image_redactor/imageredactor.h image_redactor/imageredactor.cpp + solvecaptchadialog.h solvecaptchadialog.cpp scenes/solvecaptchadialog.ui ) set(TRANSLATION_SOURCES @@ -50,7 +56,8 @@ set(TRANSLATION_SOURCES ocrscene.cpp ocrscene.h scenes/ocrscene.ui ofdscene.cpp ofdscene.h scenes/ofdscene.ui outputdialog.h outputdialog.cpp scenes/outputdialog.ui - # adjustpicturedialog.h adjustpicturedialog.cpp scenes/adjustpicturedialog.ui + adjustpicturedialog.h adjustpicturedialog.cpp scenes/adjustpicturedialog.ui + solvecaptchadialog.h solvecaptchadialog.cpp scenes/solvecaptchadialog.ui ) set(TS_FILES @@ -126,14 +133,23 @@ if(WIN32) set(OpenCV_DIR /usr/local/lib/cmake/opencv4) endif() +FetchContent_Declare(httplib SYSTEM + GIT_REPOSITORY https://github.com/yhirose/cpp-httplib + GIT_TAG 2eaa2ea64f9fb12773306534d461d9ed63cb76b6 # v0.14.1 + GIT_SHALLOW TRUE) +FetchContent_MakeAvailable(httplib) + find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs) include_directories( ${OpenCV_INCLUDE_DIRS} ) +target_include_directories(checks-parser PUBLIC ${OpenCV_INCLUDE_DIRS}) target_link_libraries(checks-parser PRIVATE -lzbar) target_link_libraries(checks-parser PRIVATE -ltesseract) target_link_libraries(checks-parser PRIVATE -lcurl) -target_link_libraries(checks-parser PRIVATE ${OpenCV_LIBS} ) +target_link_libraries(checks-parser PRIVATE ${OpenCV_LIBS}) +target_link_libraries(checks-parser PRIVATE httplib) + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8) target_link_libraries(checks-parser PRIVATE -lstdc++fs) endif() diff --git a/image/checkimage.cpp b/image/checkimage.cpp index caddf3d..4210ca6 100644 --- a/image/checkimage.cpp +++ b/image/checkimage.cpp @@ -1,7 +1,6 @@ +#include #include #include -#include -#include #include "checkimage.h" CheckImage::CheckImage(std::string path) { diff --git a/main.cpp b/main.cpp index fda2410..3549a9b 100644 --- a/main.cpp +++ b/main.cpp @@ -21,7 +21,6 @@ #include #include #include - #include static QWidget *loadUI(QWidget *parent, std::string filename) { @@ -61,25 +60,18 @@ int main(int argc, char *argv[]) { }); QObject::connect(ofd_button, &QPushButton::clicked, [&]() { - // OCR scene + // OFD scene sceneLayout->setCurrentIndex(3); sceneLayout->widget(3)->show(); }); - // // Text from email setup - // QWidget *emailtextscene = loadUI(window, ":/scenes/scenes/emailtextscene.ui"); - // emailtextscene->show(); - - //OCR scene - // QWidget *ocrscene = loadUI(window, ":/scenes/scenes/ocrscene.ui"); - - //OFD scene - // QWidget *ofdscene = loadUI(window, ":/scenes/scenes/ofdscene.ui"); - EmailTextScene *emailTextScene = new EmailTextScene(); OCRScene *ocrscene = new OCRScene(); OFDScene *ofdscene = new OFDScene(); + ofdscene->startHttpServer(); + // get_local_ip_address(); + sceneLayout->addWidget(mainwindowscene); sceneLayout->addWidget(emailTextScene); diff --git a/ofdscene.cpp b/ofdscene.cpp index 2e62180..fad6d35 100644 --- a/ofdscene.cpp +++ b/ofdscene.cpp @@ -1,5 +1,17 @@ #include "ofdscene.h" #include "ui_ofdscene.h" +#include "utils/utils.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include OFDScene::OFDScene(QWidget *parent) : QWidget(parent) @@ -10,3 +22,129 @@ OFDScene::OFDScene(QWidget *parent) OFDScene::~OFDScene() { delete ui; } + +void OFDScene::startHttpServer() { + std::string localIp = ""; + try { + localIp = get_local_ip_address(); + } catch(std::exception e) { + std::cerr << e.what() << std::endl; + return; + } + + httplib::Server svr; + //TODO: generate random port from 1024 to 65535 and check if its used. + svr.Get("/", [&](const httplib::Request &, httplib::Response &res){ + res.set_redirect("http://"+ localIp +":8080/", 301); + }); + + svr.listen("0.0.0.0", 8080); +} + +void OFDScene::on_choose_image_button_clicked() { + QString filename = QFileDialog::getOpenFileName(); + + if (filename == "") { + QMessageBox infoDialog; + infoDialog.setText(tr("Please, select a picture where QR code that contains info about check is present")); + infoDialog.setIcon(QMessageBox::Critical); + infoDialog.setWindowTitle(tr("Picture was not selected")); + infoDialog.exec(); + return; + } + + + ui->info_label->setText(tr("Selected image: ") + filename); + + AdjustPictureDialog dialog = AdjustPictureDialog(this, filename.toStdString()); + connect(&dialog, &AdjustPictureDialog::decodedData, this, &OFDScene::onDataDecode); + dialog.exec(); +} + +void OFDScene::onDataDecode(std::string data) { + std::vector dataSplit = split(data, "&"); + + ui->fn_line_edit->setText(QString::fromStdString(split(dataSplit[2], "=")[1])); + ui->fd_line_edit->setText(QString::fromStdString(split(dataSplit[3], "=")[1])); + ui->fi_line_edit->setText(QString::fromStdString(split(dataSplit[4], "=")[1])); + + QString extractedDateTime = QString::fromStdString(split(dataSplit[0], "=")[1]); + //TODO: some QRs contain datetime in format yyyyMMddThhmmss. Perhaps there is more different formats, should write function to detect them. + QDateTime datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmm"); + ui->purchase_datetime_edit->setDateTime(datetime); + + int type = std::stoi(split(dataSplit[5], "=")[1]); + ui->operation_type_combo_box->setCurrentIndex(type - 1); + + std::string total = split(dataSplit[1], "=")[1]; + + ui->total_spin_box->setValue(std::stod(total)); +} + + +void OFDScene::on_parse_button_clicked() { + Net net; + net.get_captcha_from_ofdru(); + + std::string solved_captcha = ""; + bool success = true; + bool is_captcha_solved = true; + Check check; + + do { + SolveCaptchaDialog dialog = SolveCaptchaDialog(this, &solved_captcha); + dialog.exec(); + is_captcha_solved = true; + + try { + std::string check_content = net.fetch_check_data_from_ofdru( + ui->fn_line_edit->text().toStdString(), + ui->fd_line_edit->text().toStdString(), + ui->fi_line_edit->text().toStdString(), + ui->purchase_datetime_edit->dateTime().toString(Qt::ISODate).toStdString(), + ui->operation_type_combo_box->currentIndex() + 1, + // In the request to ofd.ru, total is in a format with 2 last digits represent decimal part of a number. + ui->total_spin_box->text().toDouble() * 100, + solved_captcha); + + check = parseOfdRuAnswer(check_content); + } catch(OfdRequestException e) { + success = false; + if (!strcmp(e.what(), "Incorrect captcha")) { + is_captcha_solved = false; + QMessageBox infoDialog; + infoDialog.setText(tr("Captcha was not solved correctly!")); + infoDialog.setIcon(QMessageBox::Critical); + infoDialog.setWindowTitle(tr("Captcha is incorrect")); + infoDialog.exec(); + break; + } else if (!strcmp(e.what(), "Internal server error")) { + QMessageBox infoDialog; + infoDialog.setText(tr("Internal server error. Please, try again later.")); + infoDialog.setIcon(QMessageBox::Critical); + infoDialog.setWindowTitle(tr("Internal server error")); + infoDialog.exec(); + return; + } else if (!strcmp(e.what(), "Does not exist")) { + QMessageBox infoDialog; + infoDialog.setText(tr("Check not found. Please, ensure correctness of entered data.")); + infoDialog.setIcon(QMessageBox::Critical); + infoDialog.setWindowTitle(tr("Check was not found")); + infoDialog.exec(); + return; + } + } + } while (!is_captcha_solved); + + if (success) { + OutputDialog d = OutputDialog(this, check); + d.exec(); + } + +} + + +void OFDScene::on_binary_eye_button_clicked() { + +} + diff --git a/ofdscene.h b/ofdscene.h index 7de646d..83901a3 100644 --- a/ofdscene.h +++ b/ofdscene.h @@ -14,6 +14,14 @@ class OFDScene : public QWidget public: explicit OFDScene(QWidget *parent = nullptr); ~OFDScene(); + void startHttpServer(); +private slots: + void on_choose_image_button_clicked(); + void onDataDecode(std::string data); + + void on_parse_button_clicked(); + + void on_binary_eye_button_clicked(); private: Ui::OFDScene *ui; diff --git a/scenes.qrc b/scenes.qrc index 026d8a5..4704a99 100644 --- a/scenes.qrc +++ b/scenes.qrc @@ -5,5 +5,6 @@ scenes/ocrscene.ui scenes/mainwindow.ui scenes/ofdscene.ui + scenes/solvecaptchadialog.ui diff --git a/scenes/ofdscene.ui b/scenes/ofdscene.ui index a035c7c..4e44026 100644 --- a/scenes/ofdscene.ui +++ b/scenes/ofdscene.ui @@ -51,7 +51,11 @@ - + + + 4294967296.000000000000000 + + diff --git a/solvecaptchadialog.ui b/scenes/solvecaptchadialog.ui similarity index 100% rename from solvecaptchadialog.ui rename to scenes/solvecaptchadialog.ui diff --git a/translations/en_US.ts b/translations/en_US.ts index fce0d7f..bd82b03 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -4,20 +4,24 @@ AdjustPictureDialog + Dialog - Dialog + Dialog + Please, zoom to qr code and adjust contrast so that qr code looks sharp - Please, zoom to qr code and adjust contrast so that qr code looks sharp + Please, zoom to qr code and adjust contrast so that qr code looks sharp + QR code was not detected on that image. Please edit it again or enter data manually - QR code was not detected on that image. Please edit it again or enter data manually + QR code was not detected on that image. Please edit it again or enter data manually + No QR code - No QR code + No QR code @@ -303,65 +307,110 @@ - + FD (Fiscal Document) FD (Fiscal Document) - + Date and time of purchase - + Funds income Funds income - + Funds return Funds return - + Funds spend Funds spend - + Spends return Spends return - + Use your phone as a QR code scanner - + FN (Fiscal Number) FN (Fiscal Number) - + FI (Fiscal Identifier) FI (Fiscal Identifier) - + Choose image on your PC - + Operation type - + Parse Parse + + + Please, select a picture where QR code that contains info about check is present + Please, select a picture where QR code that contains info about check is present + + + + Picture was not selected + Picture was not selected + + + + Selected image: + + + + + Captcha was not solved correctly! + Captcha was not solved correctly! + + + + Captcha is incorrect + Captcha is incorrect + + + + Internal server error. Please, try again later. + Internal server error. Please, try again later. + + + + Internal server error + Internal server error + + + + Check not found. Please, ensure correctness of entered data. + Check not found. Please, ensure correctness of entered data. + + + + Check was not found + + OutputDialog @@ -490,16 +539,19 @@ SolveCaptchaDialog + Dialog - Dialog + Dialog + Please, enter a valid captcha - Please, enter a valid captcha + Please, enter a valid captcha + No captcha - No captcha + No captcha diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index 4e7fe60..e12d8d7 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -4,20 +4,24 @@ AdjustPictureDialog + Dialog - Диалог + Диалог + Please, zoom to qr code and adjust contrast so that qr code looks sharp - Пожалуйста, приблизьте QR код и настройте контраст, чтобы он читался + Пожалуйста, приблизьте QR код и настройте контраст, чтобы он читался + QR code was not detected on that image. Please edit it again or enter data manually - QR код не найден на этом изображении. Пожалуйста, попытайтесь снова или введите данные вручную + QR код не найден на этом изображении. Пожалуйста, попытайтесь снова или введите данные вручную + No QR code - QR код не найден + QR код не найден @@ -303,65 +307,110 @@ - + FD (Fiscal Document) ФД - + Date and time of purchase - + Funds income Приход средств - + Funds return Возврат средств - + Funds spend Расход средств - + Spends return Возврат расхода - + Use your phone as a QR code scanner - + FN (Fiscal Number) ФН - + FI (Fiscal Identifier) ФП - + Choose image on your PC - + Operation type - + Parse Парсить + + + Please, select a picture where QR code that contains info about check is present + Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке + + + + Picture was not selected + Изображение не было выбрано + + + + Selected image: + + + + + Captcha was not solved correctly! + Капча была решена неверно! + + + + Captcha is incorrect + Капча введена неверно + + + + Internal server error. Please, try again later. + Внутренняя ошибка сервера. Пожалуйста, попробуйте снова позже. + + + + Internal server error + Внутренняя ошибка сервера + + + + Check not found. Please, ensure correctness of entered data. + Чек не найден. Пожалуйста, убедитесь в правильности введённых данных. + + + + Check was not found + Чек не найден + OutputDialog @@ -490,16 +539,19 @@ SolveCaptchaDialog + Dialog - Диалог + Диалог + Please, enter a valid captcha - Пожалуйста, введите верную капчу + Пожалуйста, введите верную капчу + No captcha - Нет капчи + Нет капчи diff --git a/utils/utils.cpp b/utils/utils.cpp index e220616..ec5ab3d 100644 --- a/utils/utils.cpp +++ b/utils/utils.cpp @@ -1,5 +1,6 @@ #include "utils.h" +#include #include #include #include @@ -7,6 +8,37 @@ #include #include #include "../exceptions/ofdrequestexception.h" +#include "settings/settings.h" +#include +#include +#include +#include + +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()); +} std::string to_utf8(std::wstring wide_string) { static std::wstring_convert> utf8_conv; @@ -94,8 +126,9 @@ std::vector find_in_html(std::string& html, std::string regex, std it != end; it++) { std::wstring found_entry = from_utf8(it->str()); + std::wcout << "Found: " << found_entry << std::endl; std::wstring extracted = substring_from_to(found_entry, from_utf8(html_start), from_utf8(html_end)); - + std::wcout << "Extracted: " << extracted << std::endl; parsed.push_back(extracted); } return parsed; @@ -106,11 +139,42 @@ std::vector find_products_in_html(std::string html) { } std::vector find_amounts_in_html(std::string html) { - return find_in_html(html, "\\d+<\\/span>", "", "<\\/span>"); + std::vector founds = find_in_html(html, "
\\d+(\\.|\\,)?\\d{0,3}<\\/span>", "", "<\\/span>"); + for (auto &found : founds) { + std::replace(found.begin(), found.end(), ',', '.'); + } + + return founds; } std::vector find_prices_in_html(std::string html) { - return find_in_html(html, "X <\\/span>\\d+\\.\\d{2}<\\/span>", "X <\\/span>", "<\\/span>"); + std::vector founds = find_in_html(html, "X <\\/span>\\d+(\\.|,)\\d{2}<\\/span>", "X <\\/span>", "<\\/span>"); + for (auto &found : founds) { + std::replace(found.begin(), found.end(), ',', '.'); + } + + return founds; +} + +void dumpVectorsToStderr(std::vector &products, std::vector &amounts, std::vector &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 << "Prices: "; + for (auto &price : prices) { + std::wcerr << price << " "; + } + + std::cerr << std::endl; } Check parseOfdRuAnswer(std::string html) { @@ -133,6 +197,10 @@ Check parseOfdRuAnswer(std::string html) { } if ((products.size() + amounts.size() + prices.size())/products.size() != 3) { + dumpVectorsToStderr(products, amounts, 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); } diff --git a/utils/utils.h b/utils/utils.h index a235f28..7bb7402 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -5,6 +5,7 @@ #include #include "../check/check.h" +std::string get_local_ip_address(); std::string to_utf8(std::wstring wide_string); std::wstring from_utf8(std::string string);