From 439ab534d7d5151bebb7c0dafc5b222552bf2490 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Sun, 22 Jun 2025 19:51:44 +0200 Subject: [PATCH 01/20] Better event plugin currentStageId() implementation --- .../plugins/Event/src/eventplugin.cpp | 42 +++++++++---------- .../plugins/Event/src/eventplugin.h | 2 +- .../Event/src/services/qx/qxclientservice.cpp | 10 +---- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp index 65504987b..980cf746f 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp @@ -175,16 +175,16 @@ int EventPlugin::stageCount() void EventPlugin::setCurrentStageId(int stage_id) { - int ix = m_cbxStage->currentIndex(); - if(ix == stage_id-1) + if(m_currentStageId == stage_id) { return; - m_cbxStage->setCurrentIndex(stage_id - 1); + } + m_currentStageId = stage_id; emit currentStageIdChanged(stage_id); } int EventPlugin::currentStageId() { - return m_cbxStage->currentIndex() + 1; + return m_currentStageId; } int EventPlugin::stageIdForRun(int run_id) @@ -339,8 +339,12 @@ void EventPlugin::onInstalled() bt_stage->setCheckable(true); tb->addWidget(bt_stage); m_cbxStage = new QComboBox(); - connect(m_cbxStage, &QComboBox::activated, this, &EventPlugin::onCbxStageActivated); - connect(this, &EventPlugin::currentStageIdChanged, bt_stage, [bt_stage](int stage_id) { + connect(m_cbxStage, &QComboBox::activated, this, [this](int ix) { + setCurrentStageId(ix + 1); + }); + connect(this, &EventPlugin::currentStageIdChanged, bt_stage, [this, bt_stage](int stage_id) { + QSignalBlocker b(m_cbxStage); + m_cbxStage->setCurrentIndex(stage_id - 1); bt_stage->setText(tr("Current stage E%1").arg(stage_id)); }); QAction *act_stage = tb->addWidget(m_cbxStage); @@ -415,16 +419,12 @@ void EventPlugin::updateWindowTitle() fwk->setWindowTitle(title); } -void EventPlugin::onCbxStageActivated(int ix) -{ - emit this->currentStageIdChanged(ix + 1); -} - void EventPlugin::loadCurrentStageId() { - int stage_id = 0; - if(!eventName().isEmpty()) + int stage_id = 1; + if(!eventName().isEmpty()) { stage_id = eventConfig()->currentStageId(); + } setCurrentStageId(stage_id); } @@ -610,16 +610,16 @@ void EventPlugin::onEventOpened() if(!isEventOpen()) return; qfLogFuncFrame() << "stage count:" << stageCount(); - m_cbxStage->blockSignals(true); - m_cbxStage->clear(); - int stage_cnt = stageCount(); - for (int i = 0; i < stage_cnt; ++i) { - m_cbxStage->addItem("E" + QString::number(i + 1), i + 1); + { + QSignalBlocker b(m_cbxStage); + m_cbxStage->clear(); + int stage_cnt = stageCount(); + for (int i = 0; i < stage_cnt; ++i) { + m_cbxStage->addItem("E" + QString::number(i + 1), i + 1); + } + m_cbxStage->setCurrentIndex(-1); } - m_cbxStage->setCurrentIndex(-1); - m_cbxStage->blockSignals(false); loadCurrentStageId(); - //emit this->currentStageIdChanged(currentStageId()); } EventPlugin::ConnectionType EventPlugin::connectionType() const diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.h b/quickevent/app/quickevent/plugins/Event/src/eventplugin.h index 7e716d663..42d15e1c3 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.h +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.h @@ -124,7 +124,6 @@ class EventPlugin : public qf::qmlwidgets::framework::Plugin Q_SLOT void onEventOpened(); Q_SLOT void connectToSqlServer(); - Q_SLOT void onCbxStageActivated(int ix); Q_SLOT void loadCurrentStageId(); Q_SLOT void saveCurrentStageId(int current_stage); Q_SLOT void editStage(); @@ -155,6 +154,7 @@ class EventPlugin : public qf::qmlwidgets::framework::Plugin Event::EventConfig *m_eventConfig = nullptr; bool m_sqlServerConnected = false; QComboBox *m_cbxStage = nullptr; + int m_currentStageId = 0; QMap m_stageCache; QMap m_classNameCache; diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 65acbd0a3..74e9dc52c 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -50,14 +50,6 @@ QxClientService::QxClientService(QObject *parent) : Super(QxClientService::serviceId(), parent) { auto *event_plugin = getPlugin(); - - connect(event_plugin, &EventPlugin::eventOpenChanged, this, [](bool is_open) { - if (is_open) { - } - else { - } - }); - connect(event_plugin, &Event::EventPlugin::dbEventNotify, this, &QxClientService::onDbEventNotify, Qt::QueuedConnection); } @@ -222,6 +214,8 @@ int QxClientService::eventId() const QByteArray QxClientService::apiToken() const { + // API token must not be cached to enable service point + // always to current stage event on qxhttpd auto *event_plugin = getPlugin(); auto current_stage = event_plugin->currentStageId(); return event_plugin->stageData(current_stage).qxApiToken().toUtf8(); From b9606c5a9a3950a5e99c93ab610db103ff963738 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 3 Jul 2025 11:57:43 +0200 Subject: [PATCH 02/20] Fix upload start list to qxhttpd --- .clang-tidy | 1 + .../Event/src/services/qx/qxclientservice.cpp | 2 +- .../Event/src/services/qx/qxclientservice.h | 3 +- .../src/services/qx/qxclientservicewidget.cpp | 2 +- .../src/services/qx/qxclientservicewidget.ui | 88 +++++++++---------- 5 files changed, 49 insertions(+), 47 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 314ec521f..7f9662c4d 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -20,6 +20,7 @@ Checks: -clang-analyzer-optin.cplusplus.VirtualCall, -clang-analyzer-security.FloatLoopCounter, -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-non-const-global-variables, diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 74e9dc52c..2f691bff6 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -259,7 +259,7 @@ void QxClientService::uploadSpecFile(SpecFile file, QByteArray data, QObject *co { switch (file) { case SpecFile::StartListIofXml3: - postFileCompressed("/api/event/current/upload/startlist", {}, data, context, call_back); + postFileCompressed({}, "startlist-iof3.xml", data, context, call_back); break; case SpecFile::RunsCsv: postFileCompressed({}, "runs.csv", data, context, call_back); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h index c1516938c..a40ed9883 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h @@ -71,11 +71,12 @@ class QxClientService : public Service void exportRuns(QObject *context, std::function call_back = nullptr); QNetworkReply* loadQxChanges(int from_id); + + QByteArray apiToken() const; private: int eventId() const; void loadSettings() override; qf::qmlwidgets::framework::DialogWidget *createDetailWidget() override; - QByteArray apiToken() const; QUrl exchangeServerUrl() const; void postFileCompressed(std::optional path, std::optional name, QByteArray data, QObject *context, std::function call_back = nullptr); enum class SpecFile {StartListIofXml3, RunsCsv}; diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp index b1dc649d1..476a267b2 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp @@ -36,7 +36,7 @@ QxClientServiceWidget::QxClientServiceWidget(QWidget *parent) auto current_stage = event_plugin->currentStageId(); auto settings = svc->settings(); ui->edServerUrl->setText(settings.exchangeServerUrl()); - ui->edApiToken->setText(event_plugin->stageData(current_stage).qxApiToken()); + ui->edApiToken->setText(svc->apiToken()); ui->edCurrentStage->setValue(current_stage); connect(ui->btTestConnection, &QAbstractButton::clicked, this, &QxClientServiceWidget::testConnection); connect(ui->btExportEventInfo, &QAbstractButton::clicked, this, &QxClientServiceWidget::exportEventInfo); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.ui b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.ui index 2928129b3..d8824707e 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.ui +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.ui @@ -7,7 +7,7 @@ 0 0 404 - 376 + 419 @@ -16,6 +16,16 @@ + + + + Exchange server url + + + + + + @@ -26,36 +36,20 @@ - - - - true - - - - - - - false - - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - true - - - Not loaded + + + + Fill this url into HTTP POST synchonization method in O-CheckList - - 0 + + Current stage - + false @@ -72,46 +66,52 @@ - + Fill this url into HTTP POST synchonization method in O-CheckList - Current stage + Event ID - - - - Fill this url into HTTP POST synchonization method in O-CheckList + + + + false - - OCheckList Url + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - - - Exchange server url + + true + + + Not loaded + + + 0 - + Fill this url into HTTP POST synchonization method in O-CheckList - Event ID + OCheckList Url - - + + + + true + + - + Fill this url into HTTP POST synchonization method in O-CheckList @@ -121,7 +121,7 @@ - + true From 0dcfd877f043b0bf0869892ad72174668cd60be7 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 3 Jul 2025 11:59:58 +0200 Subject: [PATCH 03/20] Simplify dbVersion() and appVersion() --- .../plugins/Core/src/coreplugin.cpp | 11 +++---- .../plugins/Event/src/eventplugin.cpp | 18 +++++++---- .../plugins/Event/src/eventplugin.h | 1 + quickevent/app/quickevent/src/application.cpp | 21 ------------- quickevent/app/quickevent/src/application.h | 5 ---- quickevent/app/quickevent/src/mainwindow.cpp | 30 ++----------------- quickevent/app/quickevent/src/mainwindow.h | 4 --- 7 files changed, 21 insertions(+), 69 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Core/src/coreplugin.cpp b/quickevent/app/quickevent/plugins/Core/src/coreplugin.cpp index e0c1f9db6..0a58bb117 100644 --- a/quickevent/app/quickevent/plugins/Core/src/coreplugin.cpp +++ b/quickevent/app/quickevent/plugins/Core/src/coreplugin.cpp @@ -3,6 +3,8 @@ #include "widgets/settingsdialog.h" #include "widgets/reportssettingspage.h" +#include + #include #include #include @@ -193,13 +195,8 @@ void CorePlugin::launchSqlTool() void CorePlugin::aboutQuickEvent() { qff::MainWindow *fwk = qff::MainWindow::frameWork(); - QString version_string; - QMetaObject::invokeMethod(fwk, "versionString", Qt::DirectConnection - , Q_RETURN_ARG(QString, version_string)); - QString db_version_string; - QMetaObject::invokeMethod(fwk, "dbVersionString", Qt::DirectConnection - , Q_RETURN_ARG(QString, db_version_string)); - + QString version_string = QCoreApplication::applicationVersion(); + QString db_version_string = Event::EventPlugin::dbVersionString(); auto commit_sha = QStringLiteral(COMMIT_SHA); if (commit_sha.isEmpty()) { commit_sha = "N/A"; diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp index 980cf746f..733b513bd 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp @@ -545,11 +545,19 @@ DbSchema *EventPlugin::dbSchema() int EventPlugin::dbVersion() { - qff::MainWindow *fwk = qff::MainWindow::frameWork(); - int db_version = 0; - QMetaObject::invokeMethod(fwk, "dbVersion", Qt::DirectConnection - , Q_RETURN_ARG(int, db_version)); - return db_version; + // equals to minimal app version compatible with this DB + return 30200; +} + +QString EventPlugin::dbVersionString() +{ + int dbv = dbVersion(); + int rev = dbv % 100; + dbv /= 100; + int min = dbv % 100; + int maj = dbv / 100; + + return QString("%1.%2.%3").arg(maj).arg(min).arg(rev); } void EventPlugin::onDbEvent(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload) diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.h b/quickevent/app/quickevent/plugins/Event/src/eventplugin.h index 42d15e1c3..7c79a9038 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.h +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.h @@ -107,6 +107,7 @@ class EventPlugin : public qf::qmlwidgets::framework::Plugin DbSchema* dbSchema(); static int dbVersion(); + static QString dbVersionString(); Q_SLOT void onInstalled(); diff --git a/quickevent/app/quickevent/src/application.cpp b/quickevent/app/quickevent/src/application.cpp index 78e697629..5cfb5b7b1 100644 --- a/quickevent/app/quickevent/src/application.cpp +++ b/quickevent/app/quickevent/src/application.cpp @@ -68,24 +68,3 @@ Application *Application::instance(bool must_exist) return ret; } -int Application::dbVersion() -{ - // equals to minimal app version compatible with this DB - return 30401; -} - -QString Application::versionString() const -{ - return QCoreApplication::applicationVersion(); -} - -QString Application::dbVersionString() const -{ - int dbv = dbVersion(); - int rev = dbv % 100; - dbv /= 100; - int min = dbv % 100; - int maj = dbv / 100; - - return QString("%1.%2.%3").arg(maj).arg(min).arg(rev); -} diff --git a/quickevent/app/quickevent/src/application.h b/quickevent/app/quickevent/src/application.h index a8e17acd7..8fbeab0bb 100644 --- a/quickevent/app/quickevent/src/application.h +++ b/quickevent/app/quickevent/src/application.h @@ -19,13 +19,8 @@ class Application : public qf::qmlwidgets::framework::Application static Application* instance(bool must_exist = true); - static int dbVersion(); - AppCliOptions* cliOptions() {return m_cliOptions;} - Q_INVOKABLE QString versionString() const; - Q_INVOKABLE QString dbVersionString() const; - Q_SIGNAL void newLogEntry(const qf::core::LogEntryMap &le); void emitNewLogEntry(const qf::core::LogEntryMap &le) { emit newLogEntry(le); } private: diff --git a/quickevent/app/quickevent/src/mainwindow.cpp b/quickevent/app/quickevent/src/mainwindow.cpp index 6a80c2ab1..de91ed429 100644 --- a/quickevent/app/quickevent/src/mainwindow.cpp +++ b/quickevent/app/quickevent/src/mainwindow.cpp @@ -1,5 +1,4 @@ #include "mainwindow.h" -#include "application.h" #include "loggerwidget.h" #include @@ -17,40 +16,17 @@ #include #include -#include +#include MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : Super(parent, flags) { - setWindowTitle(tr("Quick Event ver. %1").arg(versionString())); + setWindowTitle(tr("Quick Event ver. %1").arg(QCoreApplication::applicationVersion())); setWindowIcon(QIcon(":/quickevent/images/quickevent64.png")); } -MainWindow::~MainWindow() -{ -} - -QString MainWindow::versionString() const -{ - return Application::instance()->versionString(); -} - -QString MainWindow::dbVersionString() const -{ - return Application::instance()->dbVersionString(); -} +MainWindow::~MainWindow() = default; -int MainWindow::dbVersion() const -{ - return Application::instance()->dbVersion(); -} -/* -QString MainWindow::settingsPrefix_application_locale_language() -{ - static const QString s = SETTINGS_PREFIX_APPLICATION_LOCALE_LANGUAGE; - return s; -} -*/ void MainWindow::onPluginsLoaded() { { diff --git a/quickevent/app/quickevent/src/mainwindow.h b/quickevent/app/quickevent/src/mainwindow.h index 7e22863e3..a1de24f40 100644 --- a/quickevent/app/quickevent/src/mainwindow.h +++ b/quickevent/app/quickevent/src/mainwindow.h @@ -12,10 +12,6 @@ class MainWindow : public qf::qmlwidgets::framework::MainWindow explicit MainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); ~MainWindow() override; - Q_INVOKABLE QString versionString() const; - Q_INVOKABLE QString dbVersionString() const; - Q_INVOKABLE int dbVersion() const; - void loadPlugins() override; protected: void onPluginsLoaded() Q_DECL_OVERRIDE; From 4a4c2bcbeb917c5a36ea7346a57867bcf80d7da5 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 3 Jul 2025 16:20:44 +0200 Subject: [PATCH 04/20] Fix qsqlmon empty SQL error message box --- libqf/libqfcore/src/model/sqltablemodel.cpp | 2 + tools/qsqlmon/src/appversion.h | 2 +- tools/qsqlmon/src/mainwindow.cpp | 49 +++++++++------------ 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/libqf/libqfcore/src/model/sqltablemodel.cpp b/libqf/libqfcore/src/model/sqltablemodel.cpp index dc2d178bb..4d574ae84 100644 --- a/libqf/libqfcore/src/model/sqltablemodel.cpp +++ b/libqf/libqfcore/src/model/sqltablemodel.cpp @@ -641,9 +641,11 @@ bool SqlTableModel::reloadTable(const QString &query_str) qf::core::sql::Connection sql_conn = sqlConnection(); qfs::Query q(sql_conn); m_recentlyExecutedQueryString = query_str; + m_recentlyExecutedQueryError = {}; bool ok = q.exec(query_str); if(!ok) { qfError() << QString("SQL Error: %1\n%2").arg(q.lastError().text()).arg(query_str); + m_recentlyExecutedQueryError = q.lastError().text(); return false; } if(q.isSelect()) { diff --git a/tools/qsqlmon/src/appversion.h b/tools/qsqlmon/src/appversion.h index b5016ea8d..70a8e8cee 100644 --- a/tools/qsqlmon/src/appversion.h +++ b/tools/qsqlmon/src/appversion.h @@ -1,4 +1,4 @@ #pragma once -#define APP_VERSION "2.0.0" +#define APP_VERSION "2.0.1" diff --git a/tools/qsqlmon/src/mainwindow.cpp b/tools/qsqlmon/src/mainwindow.cpp index de1b6eff8..2b602e2d8 100644 --- a/tools/qsqlmon/src/mainwindow.cpp +++ b/tools/qsqlmon/src/mainwindow.cpp @@ -348,47 +348,47 @@ void MainWindow::createActions() */ a = new QAction(tr("&Config"), this); a->setStatusTip(tr("Open the aplication config window")); - connect(a, SIGNAL(triggered()), this, SLOT(configure())); + connect(a, &QAction::triggered, this, &MainWindow::configure); actionMap["config"] = a; a = new QAction(tr("&Quit"), this); a->setShortcut(tr("Ctrl+Q")); a->setStatusTip(tr("Exit the application")); - connect(a, SIGNAL(triggered()), this, SLOT(close())); + connect(a, &QAction::triggered, this, &MainWindow::close); actionMap["exit"] = a; a = new QAction(QPixmap(":/images/lightning.png"), "&Execute SQL", this); a->setShortcut(tr("Shift+Enter")); a->setStatusTip(tr("Execute SQL command")); - connect(a, SIGNAL(triggered()), this, SLOT(executeSql())); + connect(a, &QAction::triggered, this, &MainWindow::executeSql); actionMap["executeSql"] = a; a = new QAction(QPixmap(":/images/lightning-file.png"), "&Execute SQL script", this); //a->setShortcut(tr("Shift+Enter")); a->setStatusTip(tr("Execute SQL script")); - connect(a, SIGNAL(triggered()), this, SLOT(executeSqlScript())); + connect(a, &QAction::triggered, this, &MainWindow::executeSqlScript); actionMap["executeSqlScript"] = a; a = new QAction(QPixmap(":/images/lightning-selection.png"), "&Execute selected lines", this); //a->setShortcut(tr("Shift+Enter")); a->setStatusTip(tr("Execute selected lines")); - connect(a, SIGNAL(triggered()), this, SLOT(executeSelectedLines())); + connect(a, &QAction::triggered, this, &MainWindow::executeSelectedLines); actionMap["executeSelectedLines"] = a; a = new QAction("&Set SQL delimiter", this); //a->setShortcut(tr("Shift+Enter")); a->setStatusTip(tr("Set SQL delimiter")); - connect(a, SIGNAL(triggered()), this, SLOT(setSqlDelimiter())); + connect(a, &QAction::triggered, this, &MainWindow::setSqlDelimiter); actionMap["setSqlDelimiter"] = a; a = new QAction("Show SQL journal", this); - connect(a, SIGNAL(triggered()), this, SLOT(showSqlJournal())); + connect(a, &QAction::triggered, this, &MainWindow::showSqlJournal); actionMap["showSqlJournal"] = a; a = new QAction(QPixmap(":/images/tear-off.png"), tr("Tear off SQL table"), this); a->setShortcut(tr("Ctrl+T")); a->setToolTip(tr("Tear off SQL table in a new modeless window")); - connect(a, SIGNAL(triggered()), this, SLOT(tearOffTable())); + connect(a, &QAction::triggered, this, &MainWindow::tearOffTable); actionMap["tearOffTable"] = a; a = new QAction(QPixmap(":/libqfgui/images/wordwrap.png"), tr("Word wrap"), this); @@ -396,48 +396,48 @@ void MainWindow::createActions() a->setChecked(true); //a->setShortcut(tr("Ctrl+T")); //a->setToolTip(tr("Tear off SQL table in a new modeless window")); - connect(a, SIGNAL(triggered(bool)), this, SLOT(wordWrapSqlEditor(bool))); + connect(a, &QAction::triggered, this, &MainWindow::wordWrapSqlEditor); actionMap["wordWrapSqlEditor"] = a; - //qfDebug("%s: %i",__FILE__, __LINE__); + //qfDebug("%s: %i",__FILE__, __LINE__); a = new QAction(tr("&About"), this); a->setStatusTip(tr("Show the application's About box")); - connect(a, SIGNAL(triggered()), this, SLOT(about())); + connect(a, &QAction::triggered, this, &MainWindow::about); actionMap["about"] = a; a = new QAction(tr("&About Qt"), this); //a->setStatusTip(tr("Show the application's About box")); - connect(a, SIGNAL(triggered()), this, SLOT(aboutQt())); + connect(a, &QAction::triggered, this, &MainWindow::aboutQt); actionMap["aboutQt"] = a; a = new QAction(tr("&Change Log"), this); - connect(a, SIGNAL(triggered()), this, SLOT(changeLog())); + connect(a, &QAction::triggered, this, &MainWindow::changeLog); actionMap["changeLog"] = a; a = new QAction(tr("&MySQL syntax"), this); - connect(a, SIGNAL(triggered()), this, SLOT(mysqlSyntax())); + connect(a, &QAction::triggered, this, &MainWindow::mysqlSyntax); actionMap["mysqlSyntax"] = a; a = new QAction(tr("&SQLite syntax"), this); - connect(a, SIGNAL(triggered()), this, SLOT(sqliteSyntax())); + connect(a, &QAction::triggered, this, &MainWindow::sqliteSyntax); actionMap["sqliteSyntax"] = a; a = new QAction(tr("&Available drivers"), this); - connect(a, SIGNAL(triggered()), this, SLOT(availableDrivers())); + connect(a, &QAction::triggered, this, &MainWindow::availableDrivers); actionMap["availableDrivers"] = a; a = new QAction(tr("&Check drivers"), this); - connect(a, SIGNAL(triggered()), this, SLOT(checkDrivers())); + connect(a, &QAction::triggered, this, &MainWindow::checkDrivers); actionMap["checkDrivers"] = a; /* a = new QAction(tr("About &Qt"), this); a->setStatusTip(tr("Show the Qt library's About box")); - connect(a, SIGNAL(triggered()), this, SLOT(aboutQt())); + connect(a, &QAction::triggered, this, &MainWindow::aboutQt())); actionMap["aboutQt"] = a; */ a = new QAction(tr("Add connection"), this); a->setStatusTip(tr("Add database connection to the tree")); - connect(a, SIGNAL(triggered()), this, SLOT(addServer())); + connect(a, &QAction::triggered, this, [this]() { addServer(); }); actionMap["addServer"] = a; } @@ -569,14 +569,8 @@ bool MainWindow::execQuery(const QString& query_str) m->setQuery(qs); ok = m->reload(); if(ok) { - if(is_select) { - /// if query was select - ui.queryView->tableView()->resizeColumnsToContents(); - ui.queryView->setInfo(qs); - } - else { - appendInfo(tr("affected rows: %1").arg(m->recentlyExecutedQueryRowsAffected())); - } + ui.queryView->tableView()->resizeColumnsToContents(); + ui.queryView->setInfo(qs); } else { QString msg = m->recentlyExecutedQueryError().text(); @@ -649,7 +643,6 @@ static bool is_white_space(QChar c) void MainWindow::executeSql() { - /// extract command under cursor position (commands are separated by ;) SqlTextEdit *ed = sqlDock->ui.txtSql; qf::core::String s = ed->toPlainText(); From e8ff03e37535073f571ca5fd5486be6475d6a1b2 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 3 Jul 2025 16:23:18 +0200 Subject: [PATCH 05/20] Move map count to courses table --- .../plugins/Classes/src/classeswidget.cpp | 6 +- .../plugins/Classes/src/classeswidget.ui | 9 ++- .../plugins/Classes/src/editcourseswidget.cpp | 68 ++++++++++++++++--- .../quickevent/plugins/Event/qml/DbSchema.qml | 4 +- .../plugins/Event/src/eventplugin.cpp | 2 +- .../plugins/Runs/src/runswidget.cpp | 2 +- quickevent/app/quickevent/src/application.cpp | 5 ++ quickevent/app/quickevent/src/application.h | 2 + 8 files changed, 80 insertions(+), 18 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp index 4a350f507..00801e670 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp +++ b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp @@ -187,7 +187,7 @@ ClassesWidget::ClassesWidget(QWidget *parent) : m->addColumn("classdefs.vacantsAfter", tr("VA")).setToolTip(tr("Vacants after")); m->addColumn("classdefs.lastStartTimeMin", tr("Last")).setToolTip(tr("Start time of last competitor in class.")); m->addColumn("runsCount", tr("Count")).setToolTip(tr("Runners count")); - m->addColumn("classdefs.mapCount", tr("Maps")); + // m->addColumn("classdefs.mapCount", tr("Maps")); m->addColumn("classdefs.courseId", tr("Course")).setAlignment(Qt::AlignLeft); //m->addColumn("courses.name", tr("Course")).setReadOnly(true); m->addColumn("courses.length", tr("Length")); @@ -214,7 +214,7 @@ ClassesWidget::ClassesWidget(QWidget *parent) : m_courseCodesModel = m; } connect(ui->tblClasses, &qf::qmlwidgets::TableView::currentRowChanged, this, &ClassesWidget::reloadCourseCodes); - connect(ui->chkUseAllMaps, &QCheckBox::toggled, this, [this](bool checked) { + connect(ui->chkUseAllMaps_NIY, &QCheckBox::toggled, this, [this](bool checked) { auto evplugin = getPlugin(); auto data = evplugin->stageData(selectedStageId()); data.setUseAllMaps(checked); @@ -410,7 +410,7 @@ void ClassesWidget::reload() } m_courseItemDelegate->setCourses(courses); } - ui->chkUseAllMaps->setChecked(getPlugin()->stageData(stage_id).isUseAllMaps()); + ui->chkUseAllMaps_NIY->setChecked(getPlugin()->stageData(stage_id).isUseAllMaps()); reloadCourseCodes(); } diff --git a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui index 06190ca03..2578467a3 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui +++ b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui @@ -38,7 +38,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -82,7 +82,10 @@ - + + + false + 0 @@ -90,7 +93,7 @@ - When checked, all the maps defined for class will be used during drawing + This feature is not implemented in QE3 Use all maps diff --git a/quickevent/app/quickevent/plugins/Classes/src/editcourseswidget.cpp b/quickevent/app/quickevent/plugins/Classes/src/editcourseswidget.cpp index a8b0c000c..516e1aa5b 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/editcourseswidget.cpp +++ b/quickevent/app/quickevent/plugins/Classes/src/editcourseswidget.cpp @@ -7,6 +7,7 @@ #include #include #include + #include namespace qfc = qf::core; @@ -14,6 +15,54 @@ namespace qfw = qf::qmlwidgets; namespace qfm = qf::core::model; namespace qfs = qf::core::sql; +class CoursesTableModel : public qfm::SqlTableModel +{ + Q_OBJECT +private: + using Super = qfm::SqlTableModel; +public: + enum Columns { + Col_id, + Col_name, + Col_length, + Col_climb, + Col_mapCount, + Col_runCount, + Col_note, + Col_codeCount, + Col_codeList, + Col_COUNT + }; + + CoursesTableModel(QObject *parent) : Super(parent) + { + clearColumns(Col_COUNT); + setColumn(Col_id, qf::core::model::TableModel::ColumnDefinition("id", tr("Id")).setReadOnly(true)); + setColumn(Col_name, qf::core::model::TableModel::ColumnDefinition("courses.name", tr("Name"))); + setColumn(Col_length, qf::core::model::TableModel::ColumnDefinition("courses.length", tr("Length"))); + setColumn(Col_climb, qf::core::model::TableModel::ColumnDefinition("courses.climb", tr("Climb"))); + setColumn(Col_mapCount, qf::core::model::TableModel::ColumnDefinition("courses.mapCount", tr("Maps"))); + setColumn(Col_runCount, qf::core::model::TableModel::ColumnDefinition("run_count", tr("Runners"))); + setColumn(Col_note, qf::core::model::TableModel::ColumnDefinition("courses.note", tr("Note"))); + setColumn(Col_codeCount, qf::core::model::TableModel::ColumnDefinition("code_count", tr("Code count")).setReadOnly(true)); + setColumn(Col_codeList, qf::core::model::TableModel::ColumnDefinition("code_list", tr("Codes")).setReadOnly(true)); + } +public: + QVariant data(const QModelIndex &index, int role) const override + { + if(index.column() == Col_runCount) { + if(role == Qt::BackgroundRole) { + auto run_count = index.data().toInt(); + QModelIndex ix = index.sibling(index.row(), Col_mapCount); + auto map_count = ix.data().toInt(); + if(run_count > map_count) + return QColor("salmon"); + } + } + return Super::data(index, role); + } +}; + EditCoursesWidget::EditCoursesWidget(QWidget *parent) : Super(parent) , ui(new Ui::EditCoursesWidget) @@ -30,14 +79,7 @@ EditCoursesWidget::EditCoursesWidget(QWidget *parent) editCourseCodes(ix); } }); - auto *m = new qfm::SqlTableModel(this); - m->addColumn("id").setReadOnly(true); - m->addColumn("courses.name", tr("Name")); - m->addColumn("courses.length", tr("Length")); - m->addColumn("courses.climb", tr("Climb")); - m->addColumn("courses.note", tr("Note")); - m->addColumn("code_count", tr("Cnt")).setToolTip(tr("Control count")); - m->addColumn("code_list", tr("Codes")); + auto *m = new CoursesTableModel(this); ui->tblCourses->setTableModel(m); m_coursesModel = m; } @@ -73,10 +115,18 @@ EditCoursesWidget::EditCoursesWidget(QWidget *parent) qfs::QueryBuilder qb; qb.select2("courses", "*") + .select("COUNT(runs.id) AS run_count") .select(code_list_query + "AS code_list") .select(qb_code_count.toString()) .from("courses") + .join("courses.id", "classdefs.courseId", qf::core::sql::QueryBuilder::INNER_JOIN) + .join("classdefs.classId", "classes.id") + .join("classes.id", "competitors.classId") + .joinRestricted("competitors.id", "runs.competitorId", "runs.isRunning") + .where("classdefs.stageId=runs.stageId") + .groupBy("courses.id") .orderBy("courses.name"); + m_coursesModel->setQueryBuilder(qb, false); m_coursesModel->reload(); } @@ -113,3 +163,5 @@ void EditCoursesWidget::editCourseCodes(const QModelIndex &ix) dlg.setCentralWidget(w); dlg.exec(); } + +#include "editcourseswidget.moc" diff --git a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml index 0e6d2b77f..ae2e8bb7e 100644 --- a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml +++ b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml @@ -54,7 +54,8 @@ Schema { Field { name: 'name'; type: String { } }, Field { name: 'length'; type: Int { } }, Field { name: 'climb'; type: Int { } }, - Field { name: 'note'; type: String { } } + Field { name: 'note'; type: String { } }, + Field { name: 'mapCount'; type: Int { } } ] indexes: [ Index {fields: ['name']; unique: false } // for relays @@ -132,7 +133,6 @@ Schema { Field { name: 'vacantsAfter'; type: Int { } comment: 'place n vacants gap after last competitor in class start list' }, - Field { name: 'mapCount'; type: Int { } }, Field { name: 'resultsCount'; type: Int { } comment: 'number of finished competitors, when the results were printed' }, diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp index 733b513bd..3c86c561c 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp @@ -546,7 +546,7 @@ DbSchema *EventPlugin::dbSchema() int EventPlugin::dbVersion() { // equals to minimal app version compatible with this DB - return 30200; + return 30300; } QString EventPlugin::dbVersionString() diff --git a/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp b/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp index bcb2dff86..c5cad8a28 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp @@ -1107,7 +1107,7 @@ void RunsWidget::report_competitorsStatistics() qfs::QueryBuilder qb; qb.select2("classes", "name") .select("COUNT(runs.id) AS runCount") - .select("MAX(classdefs.mapCount) as mapCount") // classdefs.mapCount must be in any agregation function in PSQL, MIN can be here as well + .select("0 as mapCount") // classdefs.mapCount must be in any agregation function in PSQL, MIN can be here as well .from("classes") .joinRestricted("classes.id", "classdefs.classid", "classdefs.stageId={{stage_id}}") .join("classes.id", "competitors.classId") diff --git a/quickevent/app/quickevent/src/application.cpp b/quickevent/app/quickevent/src/application.cpp index 5cfb5b7b1..f2b3f3435 100644 --- a/quickevent/app/quickevent/src/application.cpp +++ b/quickevent/app/quickevent/src/application.cpp @@ -68,3 +68,8 @@ Application *Application::instance(bool must_exist) return ret; } +QString Application::versionString() const +{ + return QCoreApplication::applicationVersion(); +} + diff --git a/quickevent/app/quickevent/src/application.h b/quickevent/app/quickevent/src/application.h index 8fbeab0bb..811f32456 100644 --- a/quickevent/app/quickevent/src/application.h +++ b/quickevent/app/quickevent/src/application.h @@ -21,6 +21,8 @@ class Application : public qf::qmlwidgets::framework::Application AppCliOptions* cliOptions() {return m_cliOptions;} + Q_INVOKABLE QString versionString() const; + Q_SIGNAL void newLogEntry(const qf::core::LogEntryMap &le); void emitNewLogEntry(const qf::core::LogEntryMap &le) { emit newLogEntry(le); } private: From 3f725ff191a65c5b762d7c9f4b75a4da9b03c227 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Sun, 6 Jul 2025 16:24:12 +0200 Subject: [PATCH 06/20] Enable both map counting, per class and also per course --- .../app/quickevent/plugins/Classes/src/classeswidget.cpp | 6 +++--- .../app/quickevent/plugins/Classes/src/classeswidget.ui | 9 +++------ quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml | 1 + 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp index 00801e670..4a350f507 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp +++ b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp @@ -187,7 +187,7 @@ ClassesWidget::ClassesWidget(QWidget *parent) : m->addColumn("classdefs.vacantsAfter", tr("VA")).setToolTip(tr("Vacants after")); m->addColumn("classdefs.lastStartTimeMin", tr("Last")).setToolTip(tr("Start time of last competitor in class.")); m->addColumn("runsCount", tr("Count")).setToolTip(tr("Runners count")); - // m->addColumn("classdefs.mapCount", tr("Maps")); + m->addColumn("classdefs.mapCount", tr("Maps")); m->addColumn("classdefs.courseId", tr("Course")).setAlignment(Qt::AlignLeft); //m->addColumn("courses.name", tr("Course")).setReadOnly(true); m->addColumn("courses.length", tr("Length")); @@ -214,7 +214,7 @@ ClassesWidget::ClassesWidget(QWidget *parent) : m_courseCodesModel = m; } connect(ui->tblClasses, &qf::qmlwidgets::TableView::currentRowChanged, this, &ClassesWidget::reloadCourseCodes); - connect(ui->chkUseAllMaps_NIY, &QCheckBox::toggled, this, [this](bool checked) { + connect(ui->chkUseAllMaps, &QCheckBox::toggled, this, [this](bool checked) { auto evplugin = getPlugin(); auto data = evplugin->stageData(selectedStageId()); data.setUseAllMaps(checked); @@ -410,7 +410,7 @@ void ClassesWidget::reload() } m_courseItemDelegate->setCourses(courses); } - ui->chkUseAllMaps_NIY->setChecked(getPlugin()->stageData(stage_id).isUseAllMaps()); + ui->chkUseAllMaps->setChecked(getPlugin()->stageData(stage_id).isUseAllMaps()); reloadCourseCodes(); } diff --git a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui index 2578467a3..06190ca03 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui +++ b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.ui @@ -38,7 +38,7 @@ - Qt::Orientation::Horizontal + Qt::Horizontal @@ -82,10 +82,7 @@ - - - false - + 0 @@ -93,7 +90,7 @@ - This feature is not implemented in QE3 + When checked, all the maps defined for class will be used during drawing Use all maps diff --git a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml index ae2e8bb7e..6434842ec 100644 --- a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml +++ b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml @@ -133,6 +133,7 @@ Schema { Field { name: 'vacantsAfter'; type: Int { } comment: 'place n vacants gap after last competitor in class start list' }, + Field { name: 'mapCount'; type: Int { } }, Field { name: 'resultsCount'; type: Int { } comment: 'number of finished competitors, when the results were printed' }, From ebbd09c3a0a165d4086fa6aad6f2cdad262ffcd0 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Sun, 6 Jul 2025 22:45:50 +0200 Subject: [PATCH 07/20] Better import of late registrations --- .../CardReader/src/cardreaderplugin.cpp | 1 - .../Competitors/src/competitordocument.cpp | 3 +- .../quickevent/plugins/Event/qml/DbSchema.qml | 5 +- .../Event/src/services/qx/qxclientservice.cpp | 95 +++++++++++++++--- .../Event/src/services/qx/qxclientservice.h | 10 +- .../src/services/qx/qxclientservicewidget.cpp | 29 +++--- .../src/services/qx/qxclientservicewidget.h | 3 +- .../services/qx/qxlateregistrationswidget.cpp | 87 ++++++++++++++--- .../services/qx/qxlateregistrationswidget.ui | 96 ++++++++++++++++++- .../plugins/Event/src/services/service.h | 2 +- 10 files changed, 278 insertions(+), 53 deletions(-) diff --git a/quickevent/app/quickevent/plugins/CardReader/src/cardreaderplugin.cpp b/quickevent/app/quickevent/plugins/CardReader/src/cardreaderplugin.cpp index e59d5999c..d906ce196 100644 --- a/quickevent/app/quickevent/plugins/CardReader/src/cardreaderplugin.cpp +++ b/quickevent/app/quickevent/plugins/CardReader/src/cardreaderplugin.cpp @@ -532,7 +532,6 @@ bool CardReaderPlugin::processCardToRunAssignment(int card_id, int run_id) QVariantList param { next_leg_run_id, QVariantMap { - {"runs.id", next_leg_run_id}, {"runs.startTimeMs", new_next_leg_start_time}, } }; diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitordocument.cpp b/quickevent/app/quickevent/plugins/Competitors/src/competitordocument.cpp index a2cd0bc62..4551c718f 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitordocument.cpp +++ b/quickevent/app/quickevent/plugins/Competitors/src/competitordocument.cpp @@ -101,8 +101,9 @@ bool CompetitorDocument::saveData() else { competitor_id = dataId().toInt(); } - if(m_isEmitDbEventsOnSave) + if(m_isEmitDbEventsOnSave) { getPlugin()->emitDbEvent(Event::EventPlugin::DBEVENT_COMPETITOR_EDITED, competitor_id); + } } return ret; } diff --git a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml index 6434842ec..5645984d5 100644 --- a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml +++ b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml @@ -490,15 +490,14 @@ Schema { Field { name: 'id'; type: Serial { primaryKey: true } }, Field { name: 'stage_id'; type: Int { } }, Field { name: 'change_id'; type: Int { } }, - Field { name: 'run_id'; type: Int { } }, + Field { name: 'data_id'; type: Int { } }, Field { name: 'data_type'; type: String { } }, Field { name: 'data'; type: String { } }, Field { name: 'source'; type: String { } }, Field { name: 'user_id'; type: String { } }, Field { name: 'status'; type: String { } }, Field { name: 'status_message'; type: String { } }, - Field { name: 'created'; type: DateTime { } }, - Field { name: 'row_lock'; type: Int { } } + Field { name: 'created'; type: DateTime { } } ] indexes: [ Index {fields: ['stage_id', 'change_id']; unique: true } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 2f691bff6..6e0ce6d8e 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -71,7 +71,7 @@ void QxClientService::run() { auto data = reply->readAll(); auto doc = QJsonDocument::fromJson(data); EventInfo event_info(doc.toVariant().toMap()); - setStatusMessage(event_info.name()); + setStatusMessage(event_info.name() + (event_info.stage_count() > 1? QStringLiteral(" E%1").arg(event_info.stage()): QString())); m_eventId = event_info.id(); connectToSSE(m_eventId); if (!m_pollChangesTimer) { @@ -115,11 +115,11 @@ void QxClientService::loadSettings() void QxClientService::onDbEventNotify(const QString &domain, int connection_id, const QVariant &data) { - if (status() != Status::Running) { - return; - } Q_UNUSED(connection_id) Q_UNUSED(data) + if (!isRunning()) { + return; + } if(domain == QLatin1String(Event::EventPlugin::DBEVENT_CARD_PROCESSED_AND_ASSIGNED)) { //auto checked_card = quickevent::core::si::CheckedCard(data.toMap()); //int competitor_id = getPlugin()->competitorForRun(checked_card.runId()); @@ -130,9 +130,33 @@ void QxClientService::onDbEventNotify(const QString &domain, int connection_id, //onCompetitorChanged(competitor_id); } else if (domain == Event::EventPlugin::DBEVENT_RUN_CHANGED) { - //if (auto *node = m_rootNode->findChild(); node) { - // node->sendRunChangedSignal(data); - //} + auto lst = data.toList(); + auto run_id = lst.value(0).toInt(); + auto ei = eventInfo(); + bool this_stage = false; + qf::core::sql::Query q; + q.execThrow(QStringLiteral("SELECT COUNT(*) FROM runs WHERE id=%1 AND stageId=%2").arg(run_id).arg(ei.stage())); + if (q.next()) { + this_stage = q.value(0).toInt() == 1; + } + if (!this_stage) { + return; + } + auto qe_run_rec = lst.value(1).toMap(); + auto qx_run_rec = QVariantMap(); + auto remap_key = [&qe_run_rec, &qx_run_rec](const QString &qe_key, const QString &qx_key) { + if (qe_run_rec.contains(qe_key)) { + qx_run_rec[qx_key] = qe_run_rec.value(qe_key); + } + }; + + qfInfo() << "DBEVENT_RUN_CHANGED run id:" << run_id << "data:" << QString::fromUtf8(QJsonDocument::fromVariant(qe_run_rec).toJson(QJsonDocument::Compact)); + + remap_key("runs.siId", "si_id"); + remap_key("competitors.registration", "registration"); + if (!qx_run_rec.isEmpty()) { + httpPostJson( "/api/event/current/changes/run-updated", QStringLiteral("run_id=%1").arg(run_id), qx_run_rec); + } } } @@ -255,7 +279,7 @@ void QxClientService::postFileCompressed(std::optional path, std::optio }); } -void QxClientService::uploadSpecFile(SpecFile file, QByteArray data, QObject *context, std::function call_back) +void QxClientService::uploadSpecFile(SpecFile file, QByteArray data, QObject *context, const std::function &call_back) { switch (file) { case SpecFile::StartListIofXml3: @@ -276,6 +300,46 @@ QByteArray QxClientService::zlibCompress(QByteArray data) return compressedData; } +void QxClientService::httpPostJson(const QString &path, const QString &query, QVariantMap json, QObject *context, const std::function &call_back) +{ + if (!isRunning()) { + return; + } + auto url = exchangeServerUrl(); + + url.setPath(path); + url.setQuery(query); + + QNetworkRequest request; + request.setUrl(url); + request.setRawHeader(QX_API_TOKEN, apiToken()); + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json")); + auto data = QJsonDocument::fromVariant(json).toJson(QJsonDocument::Compact); + qfInfo() << "HTTP POST JSON:" << url.toString() << "data:" << QString::fromUtf8(data); + QNetworkReply *reply = networkManager()->post(request, data); + if (context) { + connect(reply, &QNetworkReply::finished, context, [reply, url, call_back]() { + QString err; + if(reply->error()) { + err = reply->errorString(); + qfWarning() << "HTTP POST:" << url.toString() << "error:" << err; + } + if (call_back) { + call_back(err); + } + reply->deleteLater(); + }); + } + else { + connect(reply, &QNetworkReply::finished, [reply, url, call_back]() { + if(reply->error()) { + qfWarning() << "HTTP POST:" << url.toString() << "error:" << reply->errorString(); + } + reply->deleteLater(); + }); + } +} + void QxClientService::connectToSSE(int event_id) { Q_UNUSED(event_id); @@ -316,13 +380,13 @@ void QxClientService::pollQxChanges() } int stage_id = event_plugin->currentStageId(); try { - int start_id = 0; + int max_change_id = 0; qf::core::sql::Query q; - q.execThrow("SELECT MAX(id) FROM qxchanges"); + q.execThrow("SELECT MAX(change_id) FROM qxchanges"); if (q.next()) { - start_id = q.value(0).toInt() + 1; + max_change_id = q.value(0).toInt(); } - auto *reply = loadQxChanges(start_id); + auto *reply = loadQxChanges(max_change_id + 1); connect(reply, &QNetworkReply::finished, this, [reply, stage_id]() { QString err; if(reply->error()) { @@ -339,8 +403,8 @@ void QxClientService::pollQxChanges() else { auto records = json.array().toVariantList(); qf::core::sql::Query q; - q.prepare("INSERT INTO qxchanges (data_type, data, run_id, source, user_id, status, status_message, stage_id, change_id, created)" - " VALUES (:data_type, :data, :run_id, :source, :user_id, :status, :status_message, :stage_id, :change_id, :created)" + q.prepare("INSERT INTO qxchanges (data_type, data, data_id, source, user_id, status, status_message, stage_id, change_id, created)" + " VALUES (:data_type, :data, :data_id, :source, :user_id, :status, :status_message, :stage_id, :change_id, :created)" " RETURNING id"); for (const auto &v : records) { auto rec = v.toMap(); @@ -348,7 +412,7 @@ void QxClientService::pollQxChanges() auto data = QString::fromUtf8(ba); q.bindValue(":data_type", rec.value("data_type")); q.bindValue(":data", data); - q.bindValue(":run_id", rec.value("run_id")); + q.bindValue(":data_id", rec.value("data_id")); q.bindValue(":source", rec.value("source")); q.bindValue(":user_id", rec.value("user_id")); q.bindValue(":status", rec.value("status")); @@ -389,6 +453,7 @@ EventInfo QxClientService::eventInfo() const auto *event_config = event_plugin->eventConfig(); EventInfo ei; ei.set_stage(event_plugin->currentStageId()); + ei.set_stage_count(event_plugin->stageCount()); ei.set_name(event_config->eventName()); ei.set_place(event_config->eventPlace()); ei.set_start_time(event_plugin->stageStartDateTime(event_plugin->currentStageId()).toString(Qt::ISODate)); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h index a40ed9883..b20f6b99a 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h @@ -9,15 +9,14 @@ class QTimer; namespace Event::services::qx { static constexpr auto COL_ID = "id"; -static constexpr auto COL_DATA = "data"; static constexpr auto COL_DATA_TYPE = "data_type"; +static constexpr auto COL_DATA_ID = "data_id"; +static constexpr auto COL_DATA = "data"; static constexpr auto COL_STATUS = "status"; static constexpr auto COL_STATUS_MESSAGE = "status_message"; static constexpr auto COL_SOURCE = "source"; -static constexpr auto COL_RUN_ID = "run_id"; static constexpr auto COL_USER_ID = "user_id"; static constexpr auto COL_CREATED = "created"; -static constexpr auto COL_CROW_LOCK = "row_lock"; static constexpr auto STATUS_PENDING = "Pending"; @@ -37,6 +36,7 @@ class EventInfo : public QVariantMap QF_VARIANTMAP_FIELD(int, i, set_i, d) QF_VARIANTMAP_FIELD(int, s, set_s, tage) + QF_VARIANTMAP_FIELD(int, s, set_s, tage_count) QF_VARIANTMAP_FIELD(QString, n, set_n, ame) QF_VARIANTMAP_FIELD(QString, p, set_p, lace) QF_VARIANTMAP_FIELD(QString, s, set_s, tart_time) @@ -80,9 +80,11 @@ class QxClientService : public Service QUrl exchangeServerUrl() const; void postFileCompressed(std::optional path, std::optional name, QByteArray data, QObject *context, std::function call_back = nullptr); enum class SpecFile {StartListIofXml3, RunsCsv}; - void uploadSpecFile(SpecFile file, QByteArray data, QObject *context, std::function call_back = nullptr); + void uploadSpecFile(SpecFile file, QByteArray data, QObject *context, const std::function &call_back = nullptr); QByteArray zlibCompress(QByteArray data); + void httpPostJson(const QString &path, const QString &query, QVariantMap json, QObject *context = nullptr, const std::function &call_back = nullptr); + void connectToSSE(int event_id); void disconnectSSE(); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp index 476a267b2..2f589c0a6 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp @@ -49,17 +49,22 @@ QxClientServiceWidget::~QxClientServiceWidget() delete ui; } -void QxClientServiceWidget::setMessage(const QString &msg, bool is_error) +void QxClientServiceWidget::setMessage(const QString &msg, MessageType msg_type) { if (msg.isEmpty()) { ui->lblStatus->setStyleSheet({}); } else { - if (is_error) { - ui->lblStatus->setStyleSheet("background: salmon"); - } - else { - ui->lblStatus->setStyleSheet("background: lightgreen"); + switch (msg_type) { + case MessageType::Ok: + ui->lblStatus->setStyleSheet("background: lightgreen"); + break; + case MessageType::Error: + ui->lblStatus->setStyleSheet("background: salmon"); + break; + case MessageType::Progress: + ui->lblStatus->setStyleSheet("background: orange"); + break; } } ui->lblStatus->setText(msg); @@ -120,7 +125,7 @@ void QxClientServiceWidget::testConnection() setMessage(tr("Connected OK")); } else { - setMessage(tr("Connection error: %1").arg(reply->errorString()), true); + setMessage(tr("Connection error: %1").arg(reply->errorString()), MessageType::Error); } }); } @@ -139,7 +144,7 @@ void QxClientServiceWidget::exportEventInfo() setMessage(tr("Event info updated OK")); } else { - setMessage(tr("Event info update error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data)), true); + setMessage(tr("Event info update error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data)), MessageType::Error); } }); } @@ -149,13 +154,13 @@ void QxClientServiceWidget::exportStartList() auto *svc = service(); Q_ASSERT(svc); saveSettings(); - setMessage(tr("Start list export started ...")); + setMessage(tr("Start list export started ..."), MessageType::Progress); svc->exportStartListIofXml3(this, [this](auto err) { if (err.isEmpty()) { setMessage(tr("Start list exported Ok")); } else { - setMessage(err, true); + setMessage(err, MessageType::Error); } }); } @@ -165,13 +170,13 @@ void QxClientServiceWidget::exportRuns() auto *svc = service(); Q_ASSERT(svc); saveSettings(); - setMessage(tr("Runs export started ...")); + setMessage(tr("Runs export started ..."), MessageType::Progress); svc->exportRuns(this, [this](auto err) { if (err.isEmpty()) { setMessage(tr("Runs exported Ok")); } else { - setMessage(err, true); + setMessage(err, MessageType::Error); } }); } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.h index edea4c139..f5840d512 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.h @@ -19,7 +19,8 @@ class QxClientServiceWidget : public qf::qmlwidgets::framework::DialogWidget explicit QxClientServiceWidget(QWidget *parent = nullptr); ~QxClientServiceWidget() override; private: - void setMessage(const QString &msg = {}, bool is_error = false); + enum class MessageType { Ok, Error, Progress }; + void setMessage(const QString &msg = {}, MessageType msg_type = MessageType::Ok); QxClientService* service(); bool saveSettings(); void updateOCheckListPostUrl(); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp index 90a31199a..e34cd2ed8 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace qfm = qf::core::model; namespace qfs = qf::core::sql; @@ -22,34 +23,28 @@ QxLateRegistrationsWidget::QxLateRegistrationsWidget(QWidget *parent) : ui->setupUi(this); ui->tableView->setPersistentSettingsId("tblQxLateRegistrations"); - ui->tableView->setInsertRowEnabled(true); + ui->tableView->setInsertRowEnabled(false); ui->tableView->setCloneRowEnabled(false); - ui->tableView->setRemoveRowEnabled(true); + ui->tableView->setRemoveRowEnabled(false); ui->tableView->setDirtyRowsMenuSectionEnabled(false); ui->toolbar->setTableView(ui->tableView); m_model = new qfm::SqlTableModel(this); //m->setObjectName("classes.classesModel"); m_model->addColumn(COL_ID).setReadOnly(true).setAlignment(Qt::AlignLeft); + m_model->addColumn(COL_STATUS, tr("Status")); m_model->addColumn(COL_DATA_TYPE, tr("Type")); + m_model->addColumn(COL_DATA_ID, tr("Data ID")).setAlignment(Qt::AlignLeft); m_model->addColumn(COL_DATA, tr("Data"));//.setToolTip(tr("Locked for drawing")); m_model->addColumn(COL_SOURCE, tr("Source")); - m_model->addColumn(COL_RUN_ID, tr("Run")).setAlignment(Qt::AlignLeft); m_model->addColumn(COL_USER_ID, tr("User")); - m_model->addColumn(COL_STATUS, tr("Status")); m_model->addColumn(COL_STATUS_MESSAGE, tr("Status message")); m_model->addColumn(COL_CREATED, tr("Created")); - m_model->addColumn(COL_CROW_LOCK, tr("Locked")); ui->tableView->setTableModel(m_model); - showMessage({}); setEnabled(false); - //connect(ui->btReload, &QAbstractButton::clicked, this, &QxLateRegistrationsWidget::reload); - //connect(ui->btResizeColumns, &QAbstractButton::clicked, this, &QxLateRegistrationsWidget::resizeColumns); - //connect(ui->btApply, &QAbstractButton::clicked, this, &QxLateRegistrationsWidget::applyCurrentChange); - auto *svc = service(); connect(svc, &Service::statusChanged, this, [this](Service::Status new_status){ switch (new_status) { @@ -65,6 +60,41 @@ QxLateRegistrationsWidget::QxLateRegistrationsWidget(QWidget *parent) : }); connect(getPlugin(), &Event::EventPlugin::dbEventNotify, this, &QxLateRegistrationsWidget::onDbEventNotify, Qt::QueuedConnection); + + { + auto *lst = ui->lstType; + lst->addItem("All"); + lst->addItem("RunUpdateRequest"); + lst->addItem("RunUpdated"); + lst->addItem("OcChange"); + lst->addItem("RadioPunch"); + lst->addItem("CardReadout"); + lst->setCurrentIndex(0); + connect(lst, &QComboBox::currentIndexChanged, this, &QxLateRegistrationsWidget::reload); + } + connect(ui->chkNull, &QCheckBox::checkStateChanged, this, &QxLateRegistrationsWidget::reload); + connect(ui->chkPending, &QCheckBox::checkStateChanged, this, &QxLateRegistrationsWidget::reload); + connect(ui->chkLocked, &QCheckBox::checkStateChanged, this, &QxLateRegistrationsWidget::reload); + connect(ui->chkAccepted, &QCheckBox::checkStateChanged, this, &QxLateRegistrationsWidget::reload); + connect(ui->chkRejected, &QCheckBox::checkStateChanged, this, &QxLateRegistrationsWidget::reload); + + connect(ui->btAll, &QPushButton::clicked, this, [this]() { + QSignalBlocker sb1(ui->lstType); + ui->lstType->setCurrentIndex(0); + + QSignalBlocker sb2(ui->chkNull); + ui->chkNull->setChecked(true); + QSignalBlocker sb3(ui->chkPending); + ui->chkPending->setChecked(true); + QSignalBlocker sb4(ui->chkLocked); + ui->chkLocked->setChecked(true); + QSignalBlocker sb5(ui->chkAccepted); + ui->chkAccepted->setChecked(true); + QSignalBlocker sb6(ui->chkRejected); + ui->chkRejected->setChecked(true); + + reload(); + }); } QxLateRegistrationsWidget::~QxLateRegistrationsWidget() @@ -132,7 +162,30 @@ void QxLateRegistrationsWidget::reload() qb.select2("qxchanges", "*") .from("qxchanges") .where("stage_id=" + QString::number(stage_id)) - .orderBy("id");//.limit(10); + .orderBy("id"); + QStringList status_cond_list; + if (ui->chkNull->isChecked()) { + status_cond_list << "status IS NULL"; + } + if (ui->chkPending->isChecked()) { + status_cond_list << "status='Pending'"; + } + if (ui->chkLocked->isChecked()) { + status_cond_list << "status LIKE 'Locked%'"; + } + if (ui->chkAccepted->isChecked()) { + status_cond_list << "status='Accepted'"; + } + if (ui->chkRejected->isChecked()) { + status_cond_list << "status='Rejected'"; + } + if (!status_cond_list.isEmpty()) { + qb.where('(' + status_cond_list.join(" OR ") + ')'); + } + if (auto ix = ui->lstType->currentIndex(); ix > 0) { + auto data_type = ui->lstType->currentText(); + qb.where(QStringLiteral("data_type='%1'").arg(data_type)); + } qfDebug() << qb.toString(); m_model->setQueryBuilder(qb, false); m_model->reload(); @@ -140,10 +193,20 @@ void QxLateRegistrationsWidget::reload() void QxLateRegistrationsWidget::addQxChangeRow(int sql_id) { - qfDebug() << "reloading change id:" << sql_id << "col id:" << COL_ID; + qfDebug() << "reloading qxchanges row id:" << sql_id << "col id:" << COL_ID; if(sql_id <= 0) { return; } + + auto qb = m_model->queryBuilder(); + qb.where(QStringLiteral("id=%1").arg(sql_id)); + qf::core::sql::Query q; + q.execThrow(qb.toString()); + if (!q.next()) { + // inserted row is filtered out + return; + } + m_model->insertRow(0); m_model->setValue(0, COL_ID, sql_id); int cnt = m_model->reloadRow(0); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.ui b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.ui index 31923ae79..a1ebf08eb 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.ui +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 300 + 733 + 331 @@ -28,7 +28,97 @@ - + + + + + + 0 + 0 + + + + + + + + All + + + + + + + Type + + + + + + + + + + Pending + + + true + + + + + + + QFrame::Shape::NoFrame + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Locked + + + + + + + Accepted + + + + + + + Rejected + + + + + + + Null + + + + + + + diff --git a/quickevent/app/quickevent/plugins/Event/src/services/service.h b/quickevent/app/quickevent/plugins/Event/src/services/service.h index c73260dba..78856e6f6 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/service.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/service.h @@ -55,12 +55,12 @@ class Service : public QObject Q_SIGNAL void statusChanged(Status new_status); void setRunning(bool on); + bool isRunning() const { return status() == Status::Running; } static void addService(Service *service); static int serviceCount() {return m_services.count();} static Service* serviceAt(int ix); static Service* serviceByName(const QString &service_name); - //Q_SIGNAL void serviceCountChanged(int new_count); protected: virtual void loadSettings(); QString settingsGroup() const; From 2345217712c1406a6319e0fa0e3b47bd713fa4f0 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 3 Jul 2025 11:59:58 +0200 Subject: [PATCH 08/20] Simplify dbVersion() and appVersion() --- quickevent/app/quickevent/src/application.cpp | 5 ----- quickevent/app/quickevent/src/application.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/quickevent/app/quickevent/src/application.cpp b/quickevent/app/quickevent/src/application.cpp index f2b3f3435..5cfb5b7b1 100644 --- a/quickevent/app/quickevent/src/application.cpp +++ b/quickevent/app/quickevent/src/application.cpp @@ -68,8 +68,3 @@ Application *Application::instance(bool must_exist) return ret; } -QString Application::versionString() const -{ - return QCoreApplication::applicationVersion(); -} - diff --git a/quickevent/app/quickevent/src/application.h b/quickevent/app/quickevent/src/application.h index 811f32456..8fbeab0bb 100644 --- a/quickevent/app/quickevent/src/application.h +++ b/quickevent/app/quickevent/src/application.h @@ -21,8 +21,6 @@ class Application : public qf::qmlwidgets::framework::Application AppCliOptions* cliOptions() {return m_cliOptions;} - Q_INVOKABLE QString versionString() const; - Q_SIGNAL void newLogEntry(const qf::core::LogEntryMap &le); void emitNewLogEntry(const qf::core::LogEntryMap &le) { emit newLogEntry(le); } private: From d7a760321f479c3a5a055d2da85d912d219f84b1 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Wed, 9 Jul 2025 23:44:42 +0200 Subject: [PATCH 09/20] Fix load class drawing layout load --- libqf/libqfcore/src/core/utils.cpp | 19 +++++++++++++++++++ libqf/libqfcore/src/core/utils.h | 3 +++ .../plugins/Classes/src/drawing/ganttitem.cpp | 18 +++++++++++------- .../plugins/Event/src/eventplugin.cpp | 9 ++++++++- .../app/quickevent/plugins/Event/src/stage.h | 2 -- quickevent/app/quickevent/src/appversion.h | 2 +- 6 files changed, 42 insertions(+), 11 deletions(-) diff --git a/libqf/libqfcore/src/core/utils.cpp b/libqf/libqfcore/src/core/utils.cpp index bd93b6c4c..63e9014f6 100644 --- a/libqf/libqfcore/src/core/utils.cpp +++ b/libqf/libqfcore/src/core/utils.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace qf::core { @@ -266,4 +267,22 @@ QStringList Utils::parseProgramAndArgumentsList(const QString &command_line) return args; } +QVariant Utils::jsonToQVariant(const QString &json) +{ + QJsonParseError error; + auto doc = QJsonDocument::fromJson(json.toUtf8(), &error); + if (error.error != QJsonParseError::NoError) { + qfError() << json << "Json parse error:" << error.errorString(); + return {}; + } + return doc.toVariant(); +} + +QString Utils::qvariantToJson(const QVariant &v, bool compact) +{ + auto doc = QJsonDocument::fromVariant(v); + auto ba = doc.toJson(compact? QJsonDocument::Compact: QJsonDocument::Indented); + return QString::fromUtf8(ba); +} + } diff --git a/libqf/libqfcore/src/core/utils.h b/libqf/libqfcore/src/core/utils.h index 8c460f129..7290b23bb 100644 --- a/libqf/libqfcore/src/core/utils.h +++ b/libqf/libqfcore/src/core/utils.h @@ -210,6 +210,9 @@ class QFCORE_DECL_EXPORT Utils } static QStringList parseProgramAndArgumentsList(const QString &command_line); + + static QVariant jsonToQVariant(const QString &json); + static QString qvariantToJson(const QVariant &v, bool compact = true); }; }} diff --git a/quickevent/app/quickevent/plugins/Classes/src/drawing/ganttitem.cpp b/quickevent/app/quickevent/plugins/Classes/src/drawing/ganttitem.cpp index 68f3d1f59..5afc179f2 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/drawing/ganttitem.cpp +++ b/quickevent/app/quickevent/plugins/Classes/src/drawing/ganttitem.cpp @@ -28,7 +28,7 @@ GanttItem::GanttItem(QGraphicsItem *parent) int GanttItem::startSlotItemIndex(const StartSlotItem *it) const { - return m_startSlotItems.indexOf((StartSlotItem *)it); + return m_startSlotItems.indexOf(it); } StartSlotItem *GanttItem::startSlotItemAt(int ix, bool throw_ex) @@ -65,7 +65,7 @@ void GanttItem::load(int stage_id) qfLogFuncFrame(); Event::StageData stage_data = getPlugin()->stageData(stage_id); DrawingConfig dc(stage_data.drawingConfig()); - QVariantList stsllst = dc.startSlots(); + QVariantList start_slot_list = dc.startSlots(); qfs::Query q(qfs::Connection::forName()); qf::core::sql::QueryBuilder qb1; @@ -104,7 +104,7 @@ void GanttItem::load(int stage_id) if(slot_ix > curr_slot_ix) { slot_item = addStartSlotItem(); curr_slot_ix = slot_ix; - StartSlotData sd(stsllst.value(slot_ix).toMap()); + StartSlotData sd(start_slot_list.value(slot_ix).toMap()); slot_item->setData(sd); } } @@ -114,8 +114,13 @@ void GanttItem::load(int stage_id) slot_item = addStartSlotItem(); } } - auto *class_it = slot_item->addClassItem(); - class_it->setData(cd); + if (slot_item) { + auto *class_it = slot_item->addClassItem(); + class_it->setData(cd); + } + else { + QF_EXCEPTION("internal error"); + } } updateGeometry(); checkClassClash(); @@ -134,8 +139,7 @@ void GanttItem::save(int stage_id) start_slots << sd; } dc.setStartSlots(start_slots); - QJsonDocument jsd = QJsonDocument::fromVariant(dc); - QString dc_str = QString::fromUtf8(jsd.toJson(QJsonDocument::Compact)); + auto dc_str = qf::core::Utils::qvariantToJson(dc); QString qs = "UPDATE stages SET drawingConfig=:drawingConfig WHERE id=:id"; qfs::Query q(qfs::Connection::forName()); diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp index 1e19eedf1..d482d0243 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp @@ -245,7 +245,14 @@ StageData EventPlugin::stageData(int stage_id) doc.load(stage_id); StageData data; for (const auto &[k, v] : doc.values().asKeyValueRange()) { - data[k.toLower()] = v; + auto key = k.toLower(); + if (key == "drawingconfig" && v.userType() == qMetaTypeId()) { + // convert json to variantmap + data[key] = qf::core::Utils::jsonToQVariant(v.toString()); + } + else { + data[key] = v; + } } m_stageCache[stage_id] = StageData(data); } diff --git a/quickevent/app/quickevent/plugins/Event/src/stage.h b/quickevent/app/quickevent/plugins/Event/src/stage.h index 7aac6aea0..f52c79399 100644 --- a/quickevent/app/quickevent/plugins/Event/src/stage.h +++ b/quickevent/app/quickevent/plugins/Event/src/stage.h @@ -14,9 +14,7 @@ class StageData : public QVariantMap private: typedef QVariantMap Super; - // QF_VARIANTMAP_KEY_FIELD(int, id, i, setI, d) QF_VARIANTMAP_KEY_FIELD(bool, useallmaps, is, set, UseAllMaps) - //QF_VARIANTMAP_KEY_FIELD(QDateTime, startdatetime, s, setS, tartDateTime) QF_VARIANTMAP_KEY_FIELD(QVariantMap, drawingconfig, d, setD, rawingConfig) QF_VARIANTMAP_KEY_FIELD(QString, qxapitoken, qx, setQx, ApiToken) public: diff --git a/quickevent/app/quickevent/src/appversion.h b/quickevent/app/quickevent/src/appversion.h index 2f3fd2d2b..d4474fcb4 100644 --- a/quickevent/app/quickevent/src/appversion.h +++ b/quickevent/app/quickevent/src/appversion.h @@ -1,4 +1,4 @@ #pragma once -#define APP_VERSION "3.4.2" +#define APP_VERSION "3.4.3" From a011208d57c2553d561360d472ff38eddebc1eaa Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 10 Jul 2025 00:59:54 +0200 Subject: [PATCH 10/20] Add classes to EventInfo --- .../Event/src/services/qx/qxclientservice.cpp | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 2f691bff6..0130b0c27 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -171,7 +171,7 @@ QNetworkReply *QxClientService::postEventInfo(const QString &qxhttp_host, const return nm->post(request, data); } -void QxClientService::exportStartListIofXml3(QObject *context, std::function call_back) +void QxClientService::postStartListIofXml3(QObject *context, std::function call_back) { auto *ep = getPlugin(); int current_stage = ep->currentStageId(); @@ -182,7 +182,7 @@ void QxClientService::exportStartListIofXml3(QObject *context, std::function call_back) +void QxClientService::postRuns(QObject *context, std::function call_back) { auto *ep = getPlugin(); int current_stage = ep->currentStageId(); @@ -392,6 +392,43 @@ EventInfo QxClientService::eventInfo() const ei.set_name(event_config->eventName()); ei.set_place(event_config->eventPlace()); ei.set_start_time(event_plugin->stageStartDateTime(event_plugin->currentStageId()).toString(Qt::ISODate)); + + QStringList columns{"name", "control_count", "length", "climb", "start_time", "interval", "start_slot_count"}; + qf::core::sql::Query q; + q.execThrow(QStringLiteral("SELECT classes.name AS name, COUNT(codes.id) AS control_count, length, climb, startTimeMin AS start_time, startIntervalMin as interval, lastStartTimeMin AS last_time" + " FROM classes" + " LEFT JOIN classdefs ON classes.id=classdefs.classId AND classdefs.stageId=%1" + " LEFT JOIN courses ON classdefs.courseId=courses.id" + " LEFT JOIN coursecodes ON coursecodes.courseId=courses.id" + " LEFT JOIN codes ON coursecodes.codeId=codes.id AND codes.code>=%2 AND codes.code<=%3" + " GROUP BY classes.id, classes.name, length, climb, startTimeMin, startIntervalMin, lastStartTimeMin" + " ORDER BY classes.name") + .arg(ei.stage()) + .arg(quickevent::core::CodeDef::PUNCH_CODE_MIN) + .arg(quickevent::core::CodeDef::PUNCH_CODE_MAX) + ); + QVariantList classes; + classes.insert(classes.length(), columns); + while (q.next()) { + QVariantList values; + auto rec = q.record(); + for (auto i = 0; i < rec.count(); ++i) { + values << q.value(i); + } + auto interval = q.value("interval").toInt(); + if (interval > 0) { + auto start_time = q.value("start_time").toInt(); + auto last_time = q.value("last_time").toInt(); + auto start_slot_count = 1 + ((last_time - start_time) / interval); + values.last() = start_slot_count; + } + else { + values.last() = 0; + } + classes.insert(classes.length(), values); + } + ei.set_classes(classes); + qfInfo() << qf::core::Utils::qvariantToJson(ei, false); return ei; } From d26938b084ee96f6d6a017bf6b6f63609150d454 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 10 Jul 2025 01:13:09 +0200 Subject: [PATCH 11/20] Fix QE version missing in reports --- libqf/libqfqmlwidgets/src/framework/application.cpp | 5 +++++ libqf/libqfqmlwidgets/src/framework/application.h | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libqf/libqfqmlwidgets/src/framework/application.cpp b/libqf/libqfqmlwidgets/src/framework/application.cpp index fa7520c9a..4a72da71c 100644 --- a/libqf/libqfqmlwidgets/src/framework/application.cpp +++ b/libqf/libqfqmlwidgets/src/framework/application.cpp @@ -17,6 +17,11 @@ Application::Application(int &argc, char **argv) : { } +QString Application::versionString() const +{ + return QCoreApplication::applicationVersion(); +} + Application::~Application() = default; diff --git a/libqf/libqfqmlwidgets/src/framework/application.h b/libqf/libqfqmlwidgets/src/framework/application.h index 30bb60396..f37a2b317 100644 --- a/libqf/libqfqmlwidgets/src/framework/application.h +++ b/libqf/libqfqmlwidgets/src/framework/application.h @@ -23,7 +23,9 @@ class QFQMLWIDGETS_DECL_EXPORT Application : public QApplication typedef QApplication Super; public: explicit Application(int & argc, char ** argv); - ~Application() Q_DECL_OVERRIDE; + ~Application() override; + + Q_INVOKABLE QString versionString() const; public: static Application* instance(bool must_exist = true); MainWindow* frameWork(); From d23a5aa475b69a8cec7a1f0d409d2bf8d5b61be3 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 10 Jul 2025 01:13:41 +0200 Subject: [PATCH 12/20] Hide relays columns in classes table if event is not relay --- .../quickevent/plugins/Classes/src/classeswidget.cpp | 10 +++++++--- .../app/quickevent/plugins/Runs/src/runstablemodel.cpp | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp index 4a350f507..dbe559878 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp +++ b/quickevent/app/quickevent/plugins/Classes/src/classeswidget.cpp @@ -192,9 +192,13 @@ ClassesWidget::ClassesWidget(QWidget *parent) : //m->addColumn("courses.name", tr("Course")).setReadOnly(true); m->addColumn("courses.length", tr("Length")); m->addColumn("courses.climb", tr("Climb")); - m->addColumn("relaysCount", tr("Count")).setToolTip(tr("Relays count")); - m->addColumn("classdefs.relayStartNumber", tr("Rel.num")).setToolTip(tr("Relay start number")); - m->addColumn("classdefs.relayLegCount", tr("Legs")).setToolTip(tr("Relay leg count")); + + if (auto is_relays = getPlugin()->eventConfig()->isRelays(); is_relays) { + m->addColumn("relaysCount", tr("Rel. count")).setToolTip(tr("Relays count")); + m->addColumn("classdefs.relayStartNumber", tr("Rel. num")).setToolTip(tr("Relay start number")); + m->addColumn("classdefs.relayLegCount", tr("Legs")).setToolTip(tr("Relay leg count")); + } + ui->tblClasses->setTableModel(m); m_courseItemDelegate = new CourseItemDelegate(this); diff --git a/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp b/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp index 768764f91..a51609545 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp @@ -40,7 +40,7 @@ RunsTableModel::RunsTableModel(QObject *parent) setColumn(col_runs_penaltyTimeMs, ColumnDefinition("runs.penaltyTimeMs", tr("Penalty")).setCastType(qMetaTypeId())); setColumn(col_runFlags, ColumnDefinition("runFlags", tr("Run flags")).setReadOnly(true)); setColumn(col_cardFlags, ColumnDefinition("cardFlags", tr("Card flags")).setReadOnly(true)); - setColumn(col_runs_rankingPos, ColumnDefinition("ranking", tr("Ranking pos")).setToolTip(tr("Runner's position in CZ ranking.")).setReadOnly(true)); + setColumn(col_runs_rankingPos, ColumnDefinition("ranking", tr("Ranking pos")).setToolTip(tr("Runner's position in CZ ranking.")).setReadOnly(false)); setColumn(col_iofId, ColumnDefinition("iofId", tr("IOF ID")).setReadOnly(true)); setColumn(col_competitors_note, ColumnDefinition("competitors.note", tr("Note"))); From 11f10489815c5844563231785a8ca40c2a5a4b8f Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 10 Jul 2025 01:14:34 +0200 Subject: [PATCH 13/20] Export classes with EventInfo to qxhttpd --- .../quickevent/plugins/Event/qml/DbSchema.qml | 5 +++- .../Event/src/services/qx/qxclientservice.cpp | 23 ++++++++----------- .../Event/src/services/qx/qxclientservice.h | 6 +++-- .../src/services/qx/qxclientservicewidget.cpp | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml index 6434842ec..e647f0abe 100644 --- a/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml +++ b/quickevent/app/quickevent/plugins/Event/qml/DbSchema.qml @@ -493,12 +493,15 @@ Schema { Field { name: 'run_id'; type: Int { } }, Field { name: 'data_type'; type: String { } }, Field { name: 'data'; type: String { } }, + Field { name: 'orig_data'; type: String { } + comment: 'Store data overriden by change here to enable change rollback.' + }, Field { name: 'source'; type: String { } }, Field { name: 'user_id'; type: String { } }, Field { name: 'status'; type: String { } }, Field { name: 'status_message'; type: String { } }, Field { name: 'created'; type: DateTime { } }, - Field { name: 'row_lock'; type: Int { } } + Field { name: 'lock_number'; type: Int { } } ] indexes: [ Index {fields: ['stage_id', 'change_id']; unique: true } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 0130b0c27..bc8b1ef95 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -389,13 +389,13 @@ EventInfo QxClientService::eventInfo() const auto *event_config = event_plugin->eventConfig(); EventInfo ei; ei.set_stage(event_plugin->currentStageId()); + ei.set_stage_count(event_plugin->stageCount()); ei.set_name(event_config->eventName()); ei.set_place(event_config->eventPlace()); ei.set_start_time(event_plugin->stageStartDateTime(event_plugin->currentStageId()).toString(Qt::ISODate)); - QStringList columns{"name", "control_count", "length", "climb", "start_time", "interval", "start_slot_count"}; qf::core::sql::Query q; - q.execThrow(QStringLiteral("SELECT classes.name AS name, COUNT(codes.id) AS control_count, length, climb, startTimeMin AS start_time, startIntervalMin as interval, lastStartTimeMin AS last_time" + q.execThrow(QStringLiteral("SELECT classes.name AS name, COUNT(codes.id) AS control_count, length, climb, startTimeMin AS start_time, startIntervalMin as interval, lastStartTimeMin AS start_slot_count" " FROM classes" " LEFT JOIN classdefs ON classes.id=classdefs.classId AND classdefs.stageId=%1" " LEFT JOIN courses ON classdefs.courseId=courses.id" @@ -408,27 +408,22 @@ EventInfo QxClientService::eventInfo() const .arg(quickevent::core::CodeDef::PUNCH_CODE_MAX) ); QVariantList classes; - classes.insert(classes.length(), columns); while (q.next()) { - QVariantList values; - auto rec = q.record(); - for (auto i = 0; i < rec.count(); ++i) { - values << q.value(i); - } + auto rec = q.values(); auto interval = q.value("interval").toInt(); if (interval > 0) { - auto start_time = q.value("start_time").toInt(); - auto last_time = q.value("last_time").toInt(); + auto start_time = rec.value("start_time").toInt(); + auto last_time = rec.value("start_slot_count").toInt(); auto start_slot_count = 1 + ((last_time - start_time) / interval); - values.last() = start_slot_count; + rec["start_slot_count"] = start_slot_count; } else { - values.last() = 0; + rec["start_slot_count"] = 0; } - classes.insert(classes.length(), values); + classes << rec; } ei.set_classes(classes); - qfInfo() << qf::core::Utils::qvariantToJson(ei, false); + // qfInfo() << qf::core::Utils::qvariantToJson(ei, false); return ei; } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h index a40ed9883..a3affb179 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h @@ -37,9 +37,11 @@ class EventInfo : public QVariantMap QF_VARIANTMAP_FIELD(int, i, set_i, d) QF_VARIANTMAP_FIELD(int, s, set_s, tage) + QF_VARIANTMAP_FIELD(int, s, set_s, tage_count) QF_VARIANTMAP_FIELD(QString, n, set_n, ame) QF_VARIANTMAP_FIELD(QString, p, set_p, lace) QF_VARIANTMAP_FIELD(QString, s, set_s, tart_time) + QF_VARIANTMAP_FIELD(QVariantList, c, set_c, lasses) public: EventInfo(const QVariantMap &data = QVariantMap()) : QVariantMap(data) {} }; @@ -67,8 +69,8 @@ class QxClientService : public Service QNetworkReply* getRemoteEventInfo(const QString &qxhttp_host, const QString &api_token); QNetworkReply* postEventInfo(const QString &qxhttp_host, const QString &api_token); - void exportStartListIofXml3(QObject *context, std::function call_back = nullptr); - void exportRuns(QObject *context, std::function call_back = nullptr); + void postStartListIofXml3(QObject *context, std::function call_back = nullptr); + void postRuns(QObject *context, std::function call_back = nullptr); QNetworkReply* loadQxChanges(int from_id); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp index 476a267b2..81f396764 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp @@ -150,7 +150,7 @@ void QxClientServiceWidget::exportStartList() Q_ASSERT(svc); saveSettings(); setMessage(tr("Start list export started ...")); - svc->exportStartListIofXml3(this, [this](auto err) { + svc->postStartListIofXml3(this, [this](auto err) { if (err.isEmpty()) { setMessage(tr("Start list exported Ok")); } @@ -166,7 +166,7 @@ void QxClientServiceWidget::exportRuns() Q_ASSERT(svc); saveSettings(); setMessage(tr("Runs export started ...")); - svc->exportRuns(this, [this](auto err) { + svc->postRuns(this, [this](auto err) { if (err.isEmpty()) { setMessage(tr("Runs exported Ok")); } From a14e3118dc4e6305f5798e9a67362c799229455e Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Thu, 10 Jul 2025 22:40:37 +0200 Subject: [PATCH 14/20] Export runs and classes in EventInfo in JSON CSV --- libqf/libqfcore/src/core/utils.cpp | 7 ++- libqf/libqfcore/src/core/utils.h | 1 + .../Event/src/services/qx/qxclientservice.cpp | 61 +++++++++++++++---- .../Event/src/services/qx/qxclientservice.h | 2 +- .../plugins/Runs/src/runsplugin.cpp | 55 ++++++++++++++++- .../quickevent/plugins/Runs/src/runsplugin.h | 3 +- 6 files changed, 114 insertions(+), 15 deletions(-) diff --git a/libqf/libqfcore/src/core/utils.cpp b/libqf/libqfcore/src/core/utils.cpp index 63e9014f6..5e62a301e 100644 --- a/libqf/libqfcore/src/core/utils.cpp +++ b/libqf/libqfcore/src/core/utils.cpp @@ -279,10 +279,15 @@ QVariant Utils::jsonToQVariant(const QString &json) } QString Utils::qvariantToJson(const QVariant &v, bool compact) +{ + return QString::fromUtf8(qvariantToJsonUtf8(v, compact)); +} + +QByteArray Utils::qvariantToJsonUtf8(const QVariant &v, bool compact) { auto doc = QJsonDocument::fromVariant(v); auto ba = doc.toJson(compact? QJsonDocument::Compact: QJsonDocument::Indented); - return QString::fromUtf8(ba); + return ba; } } diff --git a/libqf/libqfcore/src/core/utils.h b/libqf/libqfcore/src/core/utils.h index 7290b23bb..8f1633edb 100644 --- a/libqf/libqfcore/src/core/utils.h +++ b/libqf/libqfcore/src/core/utils.h @@ -213,6 +213,7 @@ class QFCORE_DECL_EXPORT Utils static QVariant jsonToQVariant(const QString &json); static QString qvariantToJson(const QVariant &v, bool compact = true); + static QByteArray qvariantToJsonUtf8(const QVariant &v, bool compact = true); }; }} diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index bc8b1ef95..7945676fa 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace qf::core; using namespace qf::qmlwidgets; @@ -71,7 +72,7 @@ void QxClientService::run() { auto data = reply->readAll(); auto doc = QJsonDocument::fromJson(data); EventInfo event_info(doc.toVariant().toMap()); - setStatusMessage(event_info.name()); + setStatusMessage(tr("%1 E%2").arg(event_info.name()).arg(event_info.stage())); m_eventId = event_info.id(); connectToSSE(m_eventId); if (!m_pollChangesTimer) { @@ -188,8 +189,9 @@ void QxClientService::postRuns(QObject *context, std::function c int current_stage = ep->currentStageId(); bool is_relays = ep->eventConfig()->isRelays(); if (!is_relays) { - auto runs = getPlugin()->qxExportRunsCsv(current_stage); - uploadSpecFile(SpecFile::RunsCsv, runs.toUtf8(), context, call_back); + auto runs = getPlugin()->qxExportRunsCsvJson(current_stage); + auto json = qf::core::Utils::qvariantToJsonUtf8(runs, false); + uploadSpecFile(SpecFile::RunsCsvJson, json, context, call_back); } } @@ -261,8 +263,8 @@ void QxClientService::uploadSpecFile(SpecFile file, QByteArray data, QObject *co case SpecFile::StartListIofXml3: postFileCompressed({}, "startlist-iof3.xml", data, context, call_back); break; - case SpecFile::RunsCsv: - postFileCompressed({}, "runs.csv", data, context, call_back); + case SpecFile::RunsCsvJson: + postFileCompressed({}, "runs.csv.json", data, context, call_back); break; } } @@ -407,24 +409,61 @@ EventInfo QxClientService::eventInfo() const .arg(quickevent::core::CodeDef::PUNCH_CODE_MIN) .arg(quickevent::core::CodeDef::PUNCH_CODE_MAX) ); + QVariantList classes; + { + // QStringList columns{"name", "control_count", "length", "climb", "start_time", "interval", "start_slot_count"}; + QStringList columns; + auto rec = q.record(); + for (auto i = 0; i < rec.count(); ++i) { + columns << rec.field(i).name(); + } + classes.insert(classes.length(), columns); + } while (q.next()) { - auto rec = q.values(); + QVariantList values; + auto rec = q.record(); + for (auto i = 0; i < rec.count(); ++i) { + values << q.value(i); + } auto interval = q.value("interval").toInt(); if (interval > 0) { - auto start_time = rec.value("start_time").toInt(); - auto last_time = rec.value("start_slot_count").toInt(); + auto start_time = q.value("start_time").toInt(); + auto last_time = q.value("last_time").toInt(); auto start_slot_count = 1 + ((last_time - start_time) / interval); - rec["start_slot_count"] = start_slot_count; + values.last() = start_slot_count; } else { - rec["start_slot_count"] = 0; + values.last() = 0; } - classes << rec; + classes.insert(classes.length(), values); } ei.set_classes(classes); // qfInfo() << qf::core::Utils::qvariantToJson(ei, false); return ei; } +auto query_to_json_csv(QSqlQuery &q) +{ + QVariantList csv; + { + // QStringList columns{"name", "control_count", "length", "climb", "start_time", "interval", "start_slot_count"}; + QStringList columns; + auto rec = q.record(); + for (auto i = 0; i < rec.count(); ++i) { + columns << rec.field(i).name(); + } + csv.insert(csv.length(), columns); + } + while (q.next()) { + QVariantList values; + auto rec = q.record(); + for (auto i = 0; i < rec.count(); ++i) { + values << q.value(i); + } + csv.insert(csv.length(), values); + } + return csv; +} + } // namespace Event::services::qx diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h index a3affb179..452239911 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h @@ -81,7 +81,7 @@ class QxClientService : public Service qf::qmlwidgets::framework::DialogWidget *createDetailWidget() override; QUrl exchangeServerUrl() const; void postFileCompressed(std::optional path, std::optional name, QByteArray data, QObject *context, std::function call_back = nullptr); - enum class SpecFile {StartListIofXml3, RunsCsv}; + enum class SpecFile {StartListIofXml3, RunsCsvJson}; void uploadSpecFile(SpecFile file, QByteArray data, QObject *context, std::function call_back = nullptr); QByteArray zlibCompress(QByteArray data); diff --git a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp index 01fa69ed8..b4b3ce0c5 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp @@ -421,6 +421,59 @@ int RunsPlugin::competitorForRun(int run_id) return competitor_id; } +QVariantList RunsPlugin::qxExportRunsCsvJson(int stage_id) +{ + QVariantList csv; + auto append_list = [&csv](QVariantList lst) { + csv.insert(csv.size(), lst); + }; + enum Column {run_id = 0, class_name, registration, si_id, first_name, last_name, start_time, check_time, finish_time, status, COUNT}; + constexpr std::array colNames = { + "run_id", "class_name", "registration", "si_id", "first_name", "last_name", "start_time", "check_time", "finish_time", "status" + }; + auto col_name = [&colNames](Column col) { return colNames.at(col); }; + { + QVariantList cols; + for (auto i = 0; i < COUNT; ++i) { + cols << col_name(static_cast(i)); + } + append_list(cols); + } + QDateTime start00 = getPlugin()->stageStartDateTime(stage_id); + auto msec_to_date_time = [start00](const QVariant &msec) { + if (msec.isNull()) { + return QVariant(); + } + auto dt = start00.addMSecs(msec.toInt()); + return QVariant::fromValue(dt); + }; + qfs::QueryBuilder qb; + qb.select2("runs", "*") + .select2("competitors", "registration, licence, lastName, firstName") + .select2("classes", "name") + .from("runs") + .innerJoinRestricted("runs.competitorId", "competitors.id", "runs.stageId=" QF_IARG(stage_id) " AND runs.isRunning") + .join("competitors.classId", "classes.id"); + qfs::Query q; + q.execThrow(qb.toString()); + while (q.next()) { + QVariantList rec(COUNT); + rec[run_id] = q.value("runs.id"); + rec[class_name] = q.value("classes.name"); + rec[registration] = q.value("registration"); + rec[si_id] = q.value("runs.siId"); + rec[first_name] = q.value("firstName"); + rec[last_name] = q.value("lastName"); + rec[start_time] = msec_to_date_time(q.value("startTimeMs")); + rec[check_time] = msec_to_date_time(q.value("checkTimeMs")); + rec[finish_time] = msec_to_date_time(q.value("finishTimeMs")); + auto run_status = quickevent::core::RunStatus::fromQuery(q); + rec[status] = run_status.toHtmlExportString(); + append_list(rec); + } + return csv; +} +/* QString RunsPlugin::qxExportRunsCsv(int stage_id) { QString csv; @@ -489,7 +542,7 @@ QString RunsPlugin::qxExportRunsCsv(int stage_id) return csv; } - +*/ qf::core::utils::Table RunsPlugin::nstagesClassResultsTable(int stages_count, int class_id, int places, bool exclude_disq) { qfs::QueryBuilder qb; diff --git a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h index 6948886e3..b5ef7b04e 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h +++ b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h @@ -78,7 +78,8 @@ class RunsPlugin : public qf::qmlwidgets::framework::Plugin bool exportStartListCurrentStageCsvSime(const QString &file_name, bool bibs, QString sql_where); bool exportStartListCurrentStageTvGraphics(const QString &file_name); - QString qxExportRunsCsv(int stage_id); + QVariantList qxExportRunsCsvJson(int stage_id); + // QString qxExportRunsCsv(int stage_id); //bool exportResultsHtmlStage(int stage_id, const QString &file_name); Q_INVOKABLE bool exportResultsIofXml30Stage(int stage_id, const QString &file_name); From 1ff9c210a8aaae67cff7fc16076cdbba8cc34702 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Fri, 11 Jul 2025 02:59:43 +0200 Subject: [PATCH 15/20] Add run change dialog --- quickevent/app/quickevent/CMakeLists.txt | 2 + .../Competitors/src/competitorwidget.ui | 23 ++ .../Event/src/services/qx/qxclientservice.cpp | 6 + .../Event/src/services/qx/qxclientservice.h | 16 +- .../src/services/qx/qxclientservicewidget.cpp | 2 + .../services/qx/qxlateregistrationswidget.cpp | 66 +++ .../services/qx/qxlateregistrationswidget.h | 4 + .../Event/src/services/qx/runchange.cpp | 33 ++ .../plugins/Event/src/services/qx/runchange.h | 45 ++ .../Event/src/services/qx/runchangedialog.cpp | 149 +++++++ .../Event/src/services/qx/runchangedialog.h | 40 ++ .../Event/src/services/qx/runchangedialog.ui | 388 ++++++++++++++++++ 12 files changed, 761 insertions(+), 13 deletions(-) create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h create mode 100644 quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui diff --git a/quickevent/app/quickevent/CMakeLists.txt b/quickevent/app/quickevent/CMakeLists.txt index 900cce054..36d1828fa 100644 --- a/quickevent/app/quickevent/CMakeLists.txt +++ b/quickevent/app/quickevent/CMakeLists.txt @@ -95,6 +95,8 @@ add_executable(quickevent plugins/Event/src/services/qx/qxlateregistrationswidget.h plugins/Event/src/services/qx/qxlateregistrationswidget.cpp plugins/Event/src/services/qx/qxlateregistrationswidget.ui + plugins/Event/src/services/qx/runchangedialog.h plugins/Event/src/services/qx/runchangedialog.cpp plugins/Event/src/services/qx/runchangedialog.ui + plugins/Event/src/services/qx/runchange.h plugins/Event/src/services/qx/runchange.cpp plugins/Oris/src/chooseoriseventdialog.cpp plugins/Oris/src/chooseoriseventdialog.ui diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui index 7f356a3be..232cd8ee4 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui +++ b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui @@ -259,6 +259,29 @@ + + + + ID + + + edSiId + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + true + + + competitors.id + + + diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 6b9d3db59..31710a9d9 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -530,4 +531,9 @@ auto query_to_json_csv(QSqlQuery &q) return csv; } +int QxClientService::currentConnectionId() +{ + return qf::core::sql::Connection::forName().connectionId(); +} + } // namespace Event::services::qx diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h index fe7eb395d..5205b339e 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h @@ -8,18 +8,6 @@ class QTimer; namespace Event::services::qx { -static constexpr auto COL_ID = "id"; -static constexpr auto COL_DATA_TYPE = "data_type"; -static constexpr auto COL_DATA_ID = "data_id"; -static constexpr auto COL_DATA = "data"; -static constexpr auto COL_STATUS = "status"; -static constexpr auto COL_STATUS_MESSAGE = "status_message"; -static constexpr auto COL_SOURCE = "source"; -static constexpr auto COL_USER_ID = "user_id"; -static constexpr auto COL_CREATED = "created"; - -static constexpr auto STATUS_PENDING = "Pending"; - class QxClientServiceSettings : public ServiceSettings { using Super = ServiceSettings; @@ -74,11 +62,13 @@ class QxClientService : public Service QNetworkReply* loadQxChanges(int from_id); QByteArray apiToken() const; + static int currentConnectionId(); + QUrl exchangeServerUrl() const; + private: int eventId() const; void loadSettings() override; qf::qmlwidgets::framework::DialogWidget *createDetailWidget() override; - QUrl exchangeServerUrl() const; void postFileCompressed(std::optional path, std::optional name, QByteArray data, QObject *context, std::function call_back = nullptr); enum class SpecFile {StartListIofXml3, RunsCsvJson}; void uploadSpecFile(SpecFile file, QByteArray data, QObject *context, const std::function &call_back = nullptr); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp index e472c48d3..58ef01b6a 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservicewidget.cpp @@ -127,6 +127,7 @@ void QxClientServiceWidget::testConnection() else { setMessage(tr("Connection error: %1").arg(reply->errorString()), MessageType::Error); } + reply->deleteLater(); }); } @@ -146,6 +147,7 @@ void QxClientServiceWidget::exportEventInfo() else { setMessage(tr("Event info update error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data)), MessageType::Error); } + reply->deleteLater(); }); } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp index e34cd2ed8..feff97cc4 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp @@ -2,6 +2,8 @@ #include "ui_qxlateregistrationswidget.h" #include "qxclientservice.h" +#include "runchangedialog.h" +#include "runchange.h" #include @@ -10,18 +12,45 @@ #include #include +#include +#include + namespace qfm = qf::core::model; namespace qfs = qf::core::sql; +namespace qfw = qf::qmlwidgets; using qf::qmlwidgets::framework::getPlugin; namespace Event::services::qx { +constexpr auto COL_ID = "id"; +constexpr auto COL_DATA_TYPE = "data_type"; +constexpr auto COL_DATA_ID = "data_id"; +constexpr auto COL_DATA = "data"; +constexpr auto COL_STATUS = "status"; +constexpr auto COL_STATUS_MESSAGE = "status_message"; +constexpr auto COL_SOURCE = "source"; +constexpr auto COL_USER_ID = "user_id"; +constexpr auto COL_CREATED = "created"; +constexpr auto COL_LOCK_NUMBER = "lock_number"; + +constexpr auto STATUS_PENDING = "Pending"; +constexpr auto STATUS_LOCKED = "Locked"; + +constexpr auto DATA_TYPE_RUN_UPDATE_REQUEST = "RunUpdateRequest"; + +constexpr auto SOURCE_WWW = "www"; + QxLateRegistrationsWidget::QxLateRegistrationsWidget(QWidget *parent) : QWidget(parent), ui(new Ui::QxLateRegistrationsWidget) { ui->setupUi(this); + ui->tableView->setReadOnly(true); + ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->tableView, &qfw::TableView::customContextMenuRequested, this, &QxLateRegistrationsWidget::onTableCustomContextMenuRequest); + connect(ui->tableView, &qfw::TableView::doubleClicked, this, &QxLateRegistrationsWidget::onTableDoubleClicked); + ui->tableView->setPersistentSettingsId("tblQxLateRegistrations"); ui->tableView->setInsertRowEnabled(false); ui->tableView->setCloneRowEnabled(false); @@ -40,6 +69,7 @@ QxLateRegistrationsWidget::QxLateRegistrationsWidget(QWidget *parent) : m_model->addColumn(COL_USER_ID, tr("User")); m_model->addColumn(COL_STATUS_MESSAGE, tr("Status message")); m_model->addColumn(COL_CREATED, tr("Created")); + m_model->addColumn(COL_LOCK_NUMBER, tr("Lock")); ui->tableView->setTableModel(m_model); showMessage({}); @@ -233,5 +263,41 @@ void QxLateRegistrationsWidget::applyCurrentChange() } +void QxLateRegistrationsWidget::onTableCustomContextMenuRequest(const QPoint &pos) +{ + QAction a_neco(tr("Neco"), nullptr); + QList lst; + lst << &a_neco; + QAction *a = QMenu::exec(lst, ui->tableView->viewport()->mapToGlobal(pos)); + if(a == &a_neco) { + //printSelectedCards(); + } +} + +void QxLateRegistrationsWidget::onTableDoubleClicked(const QModelIndex &ix) +{ + auto row = ix.row(); + auto data_type = m_model->value(row, COL_DATA_TYPE).toString(); + if (data_type != DATA_TYPE_RUN_UPDATE_REQUEST) { + return; + } + auto source = m_model->value(row, COL_SOURCE).toString(); + if (source != SOURCE_WWW) { + return; + } + auto change_id = m_model->value(row, COL_ID).toInt(); + auto status = m_model->value(row, COL_STATUS).toString(); + auto connection_id = QxClientService::currentConnectionId(); + auto lock_number = m_model->value(row, COL_LOCK_NUMBER).toInt(); + auto data_id = m_model->value(row, COL_DATA_ID).toInt(); + auto data = m_model->value(row, COL_DATA).toString().toUtf8(); + auto change_rec = QJsonDocument::fromJson(data).toVariant().toMap(); + auto run_change = RunChange::fromVariantMap(change_rec.value(DATA_TYPE_RUN_UPDATE_REQUEST).toMap()); + if (status == STATUS_PENDING || (status == STATUS_LOCKED && lock_number == connection_id)) { + RunChangeDialog dlg(change_id, data_id, lock_number, run_change, this); + dlg.exec(); + } +} + } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.h index c5bfc3233..e6b15317a 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.h @@ -30,6 +30,10 @@ class QxLateRegistrationsWidget : public QWidget void resizeColumns(); void showMessage(const QString &msg, bool is_error = false); void applyCurrentChange(); + + void onTableCustomContextMenuRequest(const QPoint &pos); + void onTableDoubleClicked(const QModelIndex &ix); + private: Ui::QxLateRegistrationsWidget *ui; qf::core::model::SqlTableModel *m_model; diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp new file mode 100644 index 000000000..dd48c45ad --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp @@ -0,0 +1,33 @@ +#include "runchange.h" + +#include +#include + +namespace Event { +namespace services { +namespace qx { + +// RunChange RunChange::fromJsonString(const QString &json) +// { +// RunChange ret; +// return ret; +// } + +RunChange RunChange::fromVariantMap(const QVariantMap &map) +{ + RunChange ret; + + if (auto v = map.value("class_name"); v.isValid()) { ret.class_name = v.toString(); } + if (auto v = map.value("first_name"); v.isValid()) { ret.first_name = v.toString(); } + if (auto v = map.value("last_name"); v.isValid()) { ret.last_name = v.toString(); } + if (auto v = map.value("registration"); v.isValid()) { ret.registration = v.toString(); } + if (auto v = map.value("si_id"); v.isValid()) { ret.si_id = v.toInt(); } + // if (auto v = map.value("si_id_rent"); v.isValid()) { ret.si_id_rent = v.toBool(); } + ret.note = map.value("note").toString(); + + return ret; +} + +} // namespace qx +} // namespace services +} // namespace Event diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h new file mode 100644 index 000000000..406bcefd5 --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +namespace Event::services::qx { + +struct RunChange +{ + std::optional class_name; + std::optional first_name; + std::optional last_name; + std::optional registration; + std::optional si_id; + // std::optional si_id_rent; + QString note; + + // static RunChange fromJsonString(const QString &json); + static RunChange fromVariantMap(const QVariantMap &map); +}; +struct OrigRunRecord +{ + QString first_name; + QString last_name; + QString registration; + int si_id; + // bool si_id_rent; +}; + +// struct ChangesRecord +// { +// int64_t id; +// QString source; +// QString data_type, +// int64_t data_id; +// data: ChangeData, +// user_id: Option, +// status: Option, +// created: QxDateTime, +// lock_number: Option, +// }; + +} // namespace Event::services::qx + + + diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp new file mode 100644 index 000000000..ef6aadf25 --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp @@ -0,0 +1,149 @@ +#include "runchangedialog.h" +#include "ui_runchangedialog.h" + +#include "qxclientservice.h" +#include "runchange.h" + +#include + +#include +#include + +#include +#include +#include +#include + +namespace Event::services::qx { + +RunChangeDialog::RunChangeDialog(int change_id, int run_id, int lock_number, const RunChange &run_change, QWidget *parent) + : QDialog(parent) + , ui(new Ui::RunChangeDialog) + , m_changeId(change_id) + , m_runId(run_id) +{ + ui->setupUi(this); + + ui->btAccept->setDisabled(true); + ui->btReject->setDisabled(true); + + ui->edClassName->setText(run_change.class_name.value_or(QString())); + ui->edNote->setText(run_change.note); + + ui->edRunId->setValue(run_id); + ui->edLockNumber->setValue(lock_number); + + ui->grpFirstName->setChecked(run_change.first_name.has_value()); + ui->edFirstName->setText(run_change.first_name.has_value()? run_change.first_name.value(): QString()); + + ui->grpLastName->setChecked(run_change.last_name.has_value()); + ui->edLastName->setText(run_change.last_name.has_value()? run_change.last_name.value(): QString()); + + ui->grpRegistration->setChecked(run_change.registration.has_value()); + ui->edRegistration->setText(run_change.registration.has_value()? run_change.registration.value(): QString()); + + ui->grpSiCard->setChecked(run_change.si_id.has_value()); + ui->edSiCard->setValue(run_change.si_id.has_value()? run_change.si_id.value(): 0); + + if (run_id > 0) { + loadOrigValues(); + } + lockChange(); +} + +RunChangeDialog::~RunChangeDialog() +{ + delete ui; +} + +QxClientService *RunChangeDialog::service() +{ + auto *svc = qobject_cast(Service::serviceByName(QxClientService::serviceId())); + Q_ASSERT(svc); + return svc; +} + +void RunChangeDialog::setMessage(const QString &msg, bool error) +{ + if (msg.isEmpty()) { + ui->lblError->setStyleSheet({}); + } + else if (error) { + ui->lblError->setStyleSheet("background: salmon"); + } + else { + ui->lblError->setStyleSheet({}); + } + ui->lblError->setText(msg); +} + +void RunChangeDialog::loadOrigValues() +{ + Q_ASSERT(m_runId > 0); + + qf::core::sql::Query q; + q.execThrow(QStringLiteral("SELECT classes.name AS class_name, competitors.id AS competitor_id" + " FROM runs" + " JOIN competitors ON competitors.id=runs.competitorId AND runs.id=%1" + " LEFT JOIN classes ON competitors.classId=classes.id") + .arg(m_runId) + ); + int competitor_id = 0; + if (q.next()) { + ui->edClassName->setText(q.value("class_name").toString()); + competitor_id = q.value("competitor_id").toInt(); + } + + using namespace qf::core::model; + + Competitors::CompetitorDocument doc; + doc.load(competitor_id, DataDocument::ModeView); + + ui->edFirstNameOrig->setText(doc.value("firstName").toString()); + ui->edLastNameOrig->setText(doc.value("lastName").toString()); + ui->edRegistrationOrig->setText(doc.value("registration").toString()); + ui->edSiCardOrig->setValue(doc.value("siId").toInt()); +} + +void RunChangeDialog::lockChange() +{ + auto *svc = service(); + auto *nm = svc->networkManager(); + QNetworkRequest request; + auto url = svc->exchangeServerUrl(); + // qfInfo() << "url " << url.toString(); + url.setPath("/api/event/current/changes/lock-change"); + QUrlQuery query; + query.addQueryItem("change_id", QString::number(m_changeId)); + auto connection_id = QxClientService::currentConnectionId(); + query.addQueryItem("lock_number", QString::number(connection_id)); + url.setQuery(query); + // qfInfo() << "GET " << url.toString(); + request.setUrl(url); + request.setRawHeader(QxClientService::QX_API_TOKEN, svc->apiToken()); + auto *reply = nm->get(request); + connect(reply, &QNetworkReply::finished, this, [this, reply, connection_id]() { + auto data = reply->readAll(); + if (reply->error() == QNetworkReply::NetworkError::NoError) { + auto id = data.toInt(); + qfInfo() << "lock reply:" << QString::fromUtf8(data) << "id:" << id; + if (id == connection_id) { + ui->btAccept->setDisabled(false); + ui->btReject->setDisabled(false); + + ui->edLockNumber->setValue(id); + } + else { + setMessage(tr("Change is locked already by other client: %1, current client id:.%2").arg(id).arg(connection_id), false); + } + } + else { + setMessage(tr("Lock change error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data))); + } + reply->deleteLater(); + }); +} + +} // namespace Event::services::qx + + diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h new file mode 100644 index 000000000..3b6ac184b --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h @@ -0,0 +1,40 @@ +#ifndef RUNCHANGEDIALOG_H +#define RUNCHANGEDIALOG_H + +#include + +namespace Event::services::qx { + +namespace Ui { +class RunChangeDialog; +} + +struct RunChange; +class QxClientService; + +class RunChangeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit RunChangeDialog(int change_id, int run_id, int lock_number, const RunChange &run_change, QWidget *parent = nullptr); + ~RunChangeDialog() override; + +private: + QxClientService* service(); + void setMessage(const QString &msg = {}, bool error = false); + + void loadOrigValues(); + void lockChange(); + +private: + Ui::RunChangeDialog *ui; + int m_changeId = 0; + int m_runId = 0; +}; + + +} // namespace Event::services::qx + + +#endif // RUNCHANGEDIALOG_H diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui new file mode 100644 index 000000000..a0a26f4ec --- /dev/null +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui @@ -0,0 +1,388 @@ + + + Event::services::qx::RunChangeDialog + + + + 0 + 0 + 712 + 569 + + + + Dialog + + + + + + + + Class + + + + + + + true + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Run ID + + + + + + + + 0 + 0 + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + true + + + None + + + 999999999 + + + + + + + Lock number + + + + + + + + 0 + 0 + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + true + + + None + + + 999999999 + + + + + + + + + First name + + + true + + + + + + + + + -> + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + + + + Last name + + + true + + + + + + + + + -> + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + + + + Registration + + + true + + + + + + + + + -> + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + + + + SI Card + + + true + + + + + + + 0 + 0 + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + None + + + 999999999 + + + + + + + Rent + + + + + + + -> + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + 0 + 0 + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + None + + + 999999999 + + + + + + + Rent + + + + + + + + + + + + Note + + + + + + + true + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + Message + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Reject + + + + + + + Accept + + + + + + + + + grpFirstName + edFirstNameOrig + edFirstName + grpLastName + edLastNameOrig + edLastName + grpRegistration + edRegistrationOrig + edRegistration + grpSiCard + edSiCardOrig + edSiCardRentOrig + edSiCard + edSiCardRent + edStatusMessage + btCancel + btReject + btAccept + + + + + btCancel + clicked() + Event::services::qx::RunChangeDialog + reject() + + + 500 + 533 + + + 332 + 539 + + + + + From 8c3813afd5aefae7af4ed1ba75c3c2ff044c6795 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Fri, 11 Jul 2025 03:36:51 +0200 Subject: [PATCH 16/20] Update lock status in qxchanges when lock is acquired --- .../src/services/qx/qxlateregistrationswidget.cpp | 4 ++-- .../plugins/Event/src/services/qx/runchange.cpp | 10 ++++------ .../plugins/Event/src/services/qx/runchangedialog.cpp | 11 +++++++++++ .../plugins/Event/src/services/qx/runchangedialog.ui | 7 +++++++ .../app/quickevent/plugins/Runs/src/runsplugin.cpp | 4 ++-- .../app/quickevent/plugins/Runs/src/runsplugin.h | 2 +- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp index feff97cc4..709894715 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp @@ -287,15 +287,15 @@ void QxLateRegistrationsWidget::onTableDoubleClicked(const QModelIndex &ix) } auto change_id = m_model->value(row, COL_ID).toInt(); auto status = m_model->value(row, COL_STATUS).toString(); - auto connection_id = QxClientService::currentConnectionId(); auto lock_number = m_model->value(row, COL_LOCK_NUMBER).toInt(); auto data_id = m_model->value(row, COL_DATA_ID).toInt(); auto data = m_model->value(row, COL_DATA).toString().toUtf8(); auto change_rec = QJsonDocument::fromJson(data).toVariant().toMap(); auto run_change = RunChange::fromVariantMap(change_rec.value(DATA_TYPE_RUN_UPDATE_REQUEST).toMap()); - if (status == STATUS_PENDING || (status == STATUS_LOCKED && lock_number == connection_id)) { + if (status == STATUS_PENDING || status == STATUS_LOCKED) { RunChangeDialog dlg(change_id, data_id, lock_number, run_change, this); dlg.exec(); + ui->tableView->reloadRow(row); } } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp index dd48c45ad..a2c768d75 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp @@ -3,9 +3,7 @@ #include #include -namespace Event { -namespace services { -namespace qx { +namespace Event::services::qx { // RunChange RunChange::fromJsonString(const QString &json) // { @@ -28,6 +26,6 @@ RunChange RunChange::fromVariantMap(const QVariantMap &map) return ret; } -} // namespace qx -} // namespace services -} // namespace Event +} // namespace Event::services::qx + + diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp index ef6aadf25..b621cdad0 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp @@ -27,6 +27,11 @@ RunChangeDialog::RunChangeDialog(int change_id, int run_id, int lock_number, con ui->btAccept->setDisabled(true); ui->btReject->setDisabled(true); + connect(ui->chkForce, &QCheckBox::checkStateChanged, this, [this]() { + ui->btAccept->setDisabled(!ui->chkForce->isChecked()); + ui->btReject->setDisabled(!ui->chkForce->isChecked()); + }); + ui->edClassName->setText(run_change.class_name.value_or(QString())); ui->edNote->setText(run_change.note); @@ -132,6 +137,12 @@ void RunChangeDialog::lockChange() ui->btReject->setDisabled(false); ui->edLockNumber->setValue(id); + + qf::core::sql::Query q; + q.execThrow(QStringLiteral("UPDATE qxchanges SET lock_number=%1, status='Locked' WHERE id=%2") + .arg(connection_id) + .arg(m_changeId) + ); } else { setMessage(tr("Change is locked already by other client: %1, current client id:.%2").arg(id).arg(connection_id), false); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui index a0a26f4ec..c63e442d3 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui @@ -308,6 +308,13 @@ + + + + Force + + + diff --git a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp index b4b3ce0c5..d260c19b4 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp @@ -1144,9 +1144,9 @@ static QString make_width(const QString &s, int width) return ret; } -void RunsPlugin::writeCSOSHeader(QTextStream &ts) +void RunsPlugin::writeCSOSHeader(QTextStream &ts) const { - Event::EventPlugin *evp = getPlugin(); + auto *evp = getPlugin(); int stage_id = selectedStageId(); QDateTime start_dt = evp->stageStartDateTime(stage_id); Event::EventConfig *ec = evp->eventConfig(); diff --git a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h index b5ef7b04e..ef2e1f65b 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h +++ b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h @@ -134,7 +134,7 @@ class RunsPlugin : public qf::qmlwidgets::framework::Plugin int courseForRun_Relays(int run_id); QString getClubAbbrFromName(QString name); - void writeCSOSHeader(QTextStream &ts); + void writeCSOSHeader(QTextStream &ts) const; void addStartTimeTextToClass(qf::core::utils::TreeTable &tt2, const qint64 start00_epoch_sec, const quickevent::gui::ReportOptionsDialog::StartTimeFormat start_time_format); void addStartTimeTextToClass(qf::core::utils::TreeTable &tt2, const int stages_count, const QVector &start00_epoch_sec, const quickevent::gui::ReportOptionsDialog::StartTimeFormat start_time_format); From 98e9d0c85961aa273d04ca1fb88e9946ff482f94 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Fri, 11 Jul 2025 04:56:33 +0200 Subject: [PATCH 17/20] Update competitor from qxhttpd changes --- .../Event/src/services/qx/qxclientservice.cpp | 2 +- .../services/qx/qxlateregistrationswidget.cpp | 4 +- .../Event/src/services/qx/runchange.cpp | 10 ++ .../plugins/Event/src/services/qx/runchange.h | 2 + .../Event/src/services/qx/runchangedialog.cpp | 144 ++++++++++++++++-- .../Event/src/services/qx/runchangedialog.h | 10 ++ quickevent/app/quickevent/src/appversion.h | 2 +- 7 files changed, 156 insertions(+), 18 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 31710a9d9..18189404c 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -494,7 +494,7 @@ EventInfo QxClientService::eventInfo() const auto interval = q.value("interval").toInt(); if (interval > 0) { auto start_time = q.value("start_time").toInt(); - auto last_time = q.value("last_time").toInt(); + auto last_time = q.value("start_slot_count").toInt(); auto start_slot_count = 1 + ((last_time - start_time) / interval); values.last() = start_slot_count; } diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp index 709894715..0b5a4957c 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp @@ -26,6 +26,7 @@ constexpr auto COL_ID = "id"; constexpr auto COL_DATA_TYPE = "data_type"; constexpr auto COL_DATA_ID = "data_id"; constexpr auto COL_DATA = "data"; +constexpr auto COL_ORIG_DATA = "orig_data"; constexpr auto COL_STATUS = "status"; constexpr auto COL_STATUS_MESSAGE = "status_message"; constexpr auto COL_SOURCE = "source"; @@ -64,12 +65,13 @@ QxLateRegistrationsWidget::QxLateRegistrationsWidget(QWidget *parent) : m_model->addColumn(COL_STATUS, tr("Status")); m_model->addColumn(COL_DATA_TYPE, tr("Type")); m_model->addColumn(COL_DATA_ID, tr("Data ID")).setAlignment(Qt::AlignLeft); - m_model->addColumn(COL_DATA, tr("Data"));//.setToolTip(tr("Locked for drawing")); m_model->addColumn(COL_SOURCE, tr("Source")); m_model->addColumn(COL_USER_ID, tr("User")); m_model->addColumn(COL_STATUS_MESSAGE, tr("Status message")); m_model->addColumn(COL_CREATED, tr("Created")); m_model->addColumn(COL_LOCK_NUMBER, tr("Lock")); + m_model->addColumn(COL_DATA, tr("Data"));//.setToolTip(tr("Locked for drawing")); + m_model->addColumn(COL_ORIG_DATA, tr("Data"));//.setToolTip(tr("Locked for drawing")); ui->tableView->setTableModel(m_model); showMessage({}); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp index a2c768d75..c9a84e23b 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.cpp @@ -26,6 +26,16 @@ RunChange RunChange::fromVariantMap(const QVariantMap &map) return ret; } +QVariantMap OrigRunRecord::toVariantMap() const +{ + QVariantMap ret; + ret["first_name"] = first_name; + ret["last_name"] = last_name; + ret["registration"] = registration; + ret["si_id"] = si_id; + return ret; +} + } // namespace Event::services::qx diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h index 406bcefd5..22605a833 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h @@ -24,6 +24,8 @@ struct OrigRunRecord QString registration; int si_id; // bool si_id_rent; + + QVariantMap toVariantMap() const; }; // struct ChangesRecord diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp index b621cdad0..4a9904919 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp @@ -13,6 +13,9 @@ #include #include #include +#include + +using namespace qf::core::model; namespace Event::services::qx { @@ -31,6 +34,12 @@ RunChangeDialog::RunChangeDialog(int change_id, int run_id, int lock_number, con ui->btAccept->setDisabled(!ui->chkForce->isChecked()); ui->btReject->setDisabled(!ui->chkForce->isChecked()); }); + connect(ui->btAccept, &QPushButton::clicked, this, [this]() { + resolveChanges(true); + }); + connect(ui->btReject, &QPushButton::clicked, this, [this]() { + resolveChanges(false); + }); ui->edClassName->setText(run_change.class_name.value_or(QString())); ui->edNote->setText(run_change.note); @@ -53,6 +62,9 @@ RunChangeDialog::RunChangeDialog(int change_id, int run_id, int lock_number, con if (run_id > 0) { loadOrigValues(); } + else { + loadClassId(); + } lockChange(); } @@ -93,21 +105,40 @@ void RunChangeDialog::loadOrigValues() " LEFT JOIN classes ON competitors.classId=classes.id") .arg(m_runId) ); - int competitor_id = 0; if (q.next()) { ui->edClassName->setText(q.value("class_name").toString()); - competitor_id = q.value("competitor_id").toInt(); + m_competitorId = q.value("competitor_id").toInt(); } - using namespace qf::core::model; - Competitors::CompetitorDocument doc; - doc.load(competitor_id, DataDocument::ModeView); + doc.load(m_competitorId, DataDocument::ModeView); + + m_origValues.first_name = doc.value("firstName").toString(); + m_origValues.last_name = doc.value("lastName").toString(); + m_origValues.registration = doc.value("registration").toString(); + m_origValues.si_id = doc.value("siId").toInt(); - ui->edFirstNameOrig->setText(doc.value("firstName").toString()); - ui->edLastNameOrig->setText(doc.value("lastName").toString()); - ui->edRegistrationOrig->setText(doc.value("registration").toString()); - ui->edSiCardOrig->setValue(doc.value("siId").toInt()); + ui->edFirstNameOrig->setText(m_origValues.first_name); + ui->edLastNameOrig->setText(m_origValues.last_name); + ui->edRegistrationOrig->setText(m_origValues.registration); + ui->edSiCardOrig->setValue(m_origValues.si_id); +} + +void RunChangeDialog::loadClassId() +{ + auto class_name = ui->edClassName->text(); + if (class_name.isEmpty()) { + return; + } + qf::core::sql::Query q; + q.execThrow(QStringLiteral("SELECT id" + " FROM classes" + " WHERE name='%1'") + .arg(class_name) + ); + if (q.next()) { + m_classId = q.value("id").toInt(); + } } void RunChangeDialog::lockChange() @@ -118,26 +149,26 @@ void RunChangeDialog::lockChange() auto url = svc->exchangeServerUrl(); // qfInfo() << "url " << url.toString(); url.setPath("/api/event/current/changes/lock-change"); + QUrlQuery query; query.addQueryItem("change_id", QString::number(m_changeId)); auto connection_id = QxClientService::currentConnectionId(); query.addQueryItem("lock_number", QString::number(connection_id)); url.setQuery(query); // qfInfo() << "GET " << url.toString(); + request.setUrl(url); request.setRawHeader(QxClientService::QX_API_TOKEN, svc->apiToken()); auto *reply = nm->get(request); connect(reply, &QNetworkReply::finished, this, [this, reply, connection_id]() { auto data = reply->readAll(); if (reply->error() == QNetworkReply::NetworkError::NoError) { - auto id = data.toInt(); - qfInfo() << "lock reply:" << QString::fromUtf8(data) << "id:" << id; - if (id == connection_id) { + m_lockNumber = data.toInt(); + ui->edLockNumber->setValue(m_lockNumber); + if (m_lockNumber == connection_id) { ui->btAccept->setDisabled(false); ui->btReject->setDisabled(false); - ui->edLockNumber->setValue(id); - qf::core::sql::Query q; q.execThrow(QStringLiteral("UPDATE qxchanges SET lock_number=%1, status='Locked' WHERE id=%2") .arg(connection_id) @@ -145,7 +176,7 @@ void RunChangeDialog::lockChange() ); } else { - setMessage(tr("Change is locked already by other client: %1, current client id:.%2").arg(id).arg(connection_id), false); + setMessage(tr("Change is locked already by other client: %1, current client id:.%2").arg(m_lockNumber).arg(connection_id), false); } } else { @@ -155,6 +186,89 @@ void RunChangeDialog::lockChange() }); } +void RunChangeDialog::resolveChanges(bool is_accepted) +{ + if (is_accepted) { + applyLocalChanges(is_accepted); + } + auto *svc = service(); + auto *nm = svc->networkManager(); + QNetworkRequest request; + auto url = svc->exchangeServerUrl(); + // qfInfo() << "url " << url.toString(); + url.setPath("/api/event/current/changes/resolve-change"); + + QUrlQuery query; + query.addQueryItem("change_id", QString::number(m_changeId)); + query.addQueryItem("lock_number", QString::number(m_lockNumber)); + query.addQueryItem("accepted", is_accepted? "true": "false"); + query.addQueryItem("status_message", ui->edStatusMessage->text()); + url.setQuery(query); + + request.setUrl(url); + request.setRawHeader(QxClientService::QX_API_TOKEN, svc->apiToken()); + auto *reply = nm->get(request); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + if (reply->error() == QNetworkReply::NetworkError::NoError) { + accept(); + } + else { + QMessageBox::warning(this, + QCoreApplication::applicationName(), + tr("Update change error: %1").arg(reply->errorString())); + } + reply->deleteLater(); + }); +} + +void RunChangeDialog::applyLocalChanges(bool is_accepted) +{ + bool is_insert = m_runId == 0; + Competitors::CompetitorDocument doc; + doc.load(m_competitorId, is_insert? DataDocument::ModeInsert: DataDocument::ModeEdit); + if (is_insert) { + doc.setValue("classId", m_classId); + } + if (ui->grpFirstName->isChecked()) { + doc.setValue("firstName", ui->edFirstName->text()); + } + if (ui->grpLastName->isChecked()) { + doc.setValue("lastName", ui->edLastName->text()); + } + if (ui->grpRegistration->isChecked()) { + doc.setValue("registration", ui->edRegistration->text()); + } + if (ui->grpSiCard->isChecked()) { + if (is_insert) { + doc.setValue("siId", ui->edSiCard->value()); + } + else { + qf::core::sql::Query q; + q.execThrow(QStringLiteral("UPDATE runs SET siId=%1 WHERE id=%2") + .arg(ui->edSiCard->value()) + .arg(m_runId) + ); + } + } + doc.save(); + { + qf::core::sql::Query q; + q.execThrow(QStringLiteral("UPDATE qxchanges SET status='%1' WHERE id=%2") + .arg(is_accepted? "Accepted": "Rejected") + .arg(m_changeId) + ); + } + { + auto dc_str = qf::core::Utils::qvariantToJson(m_origValues.toVariantMap()); + QString qs = "UPDATE qxchanges SET orig_data=:orig_data WHERE id=:id"; + qf::core::sql::Query q; + q.prepare(qs, qf::core::Exception::Throw); + q.bindValue(":orig_data", dc_str); + q.bindValue(":id", m_changeId); + q.exec(qf::core::Exception::Throw); + } +} + } // namespace Event::services::qx diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h index 3b6ac184b..5b2d5a7b9 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h @@ -1,6 +1,8 @@ #ifndef RUNCHANGEDIALOG_H #define RUNCHANGEDIALOG_H +#include "runchange.h" + #include namespace Event::services::qx { @@ -25,12 +27,20 @@ class RunChangeDialog : public QDialog void setMessage(const QString &msg = {}, bool error = false); void loadOrigValues(); + void loadClassId(); + void lockChange(); + void resolveChanges(bool is_accepted); + void applyLocalChanges(bool is_accepted); private: Ui::RunChangeDialog *ui; int m_changeId = 0; int m_runId = 0; + int m_competitorId = 0; + int m_classId = 0; + int m_lockNumber = 0; + OrigRunRecord m_origValues; }; diff --git a/quickevent/app/quickevent/src/appversion.h b/quickevent/app/quickevent/src/appversion.h index d4474fcb4..206208c9d 100644 --- a/quickevent/app/quickevent/src/appversion.h +++ b/quickevent/app/quickevent/src/appversion.h @@ -1,4 +1,4 @@ #pragma once -#define APP_VERSION "3.4.3" +#define APP_VERSION "3.4.5" From 99c159750db71c48581404d35eaa75a82680cd52 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Mon, 25 Aug 2025 17:18:56 +0200 Subject: [PATCH 18/20] Last qxhttpd changes --- .../Event/src/services/qx/qxclientservice.cpp | 36 +++++++++++-- .../Event/src/services/qx/qxclientservice.h | 6 ++- .../services/qx/qxlateregistrationswidget.cpp | 6 ++- .../plugins/Event/src/services/qx/runchange.h | 52 +++++++++++++++---- .../Event/src/services/qx/runchangedialog.cpp | 30 ++++++++++- .../Event/src/services/qx/runchangedialog.h | 6 ++- .../Event/src/services/qx/runchangedialog.ui | 39 +++++++++++++- 7 files changed, 153 insertions(+), 22 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp index 18189404c..e1fa29743 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -220,12 +220,42 @@ void QxClientService::postRuns(QObject *context, std::function c } } -QNetworkReply* QxClientService::loadQxChanges(int from_id) +void QxClientService::getHttpJson(const QString &path, const QUrlQuery &query, QObject *context, const std::function &call_back) +{ + auto url = exchangeServerUrl(); + url.setPath(path); + url.setQuery(query); + // qfInfo() << url.toString(); + QNetworkRequest request; + request.setUrl(url); + auto *reply = networkManager()->get(request); + // connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); + connect(reply, &QNetworkReply::finished, context, [call_back, reply]() { + if (reply->error() == QNetworkReply::NetworkError::NoError) { + QJsonParseError err; + auto data = reply->readAll(); + auto json = QJsonDocument::fromJson(data, &err).toVariant(); + if (err.error != QJsonParseError::NoError) { + call_back({}, err.errorString()); + } + else { + call_back(json, {}); + } + } + else { + call_back({}, reply->errorString()); + } + reply->deleteLater(); + }); +} + +QNetworkReply* QxClientService::getQxChangesReply(int from_id) { auto url = exchangeServerUrl(); url.setPath(QStringLiteral("/api/event/%1/changes").arg(eventId())); url.setQuery(QStringLiteral("from_id=%1").arg(from_id)); + qfInfo() << url.toString(); QNetworkRequest request; request.setUrl(url); return networkManager()->get(request); @@ -385,11 +415,11 @@ void QxClientService::pollQxChanges() try { int max_change_id = 0; qf::core::sql::Query q; - q.execThrow("SELECT MAX(change_id) FROM qxchanges"); + q.execThrow("SELECT MAX(change_id) FROM qxchanges WHERE stage_id=" + QString::number(event_plugin->currentStageId())); if (q.next()) { max_change_id = q.value(0).toInt(); } - auto *reply = loadQxChanges(max_change_id + 1); + auto *reply = getQxChangesReply(max_change_id + 1); connect(reply, &QNetworkReply::finished, this, [reply, stage_id]() { QString err; if(reply->error()) { diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h index 5205b339e..79007bec0 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.h @@ -4,6 +4,7 @@ class QNetworkAccessManager; class QNetworkReply; +class QUrlQuery; class QTimer; namespace Event::services::qx { @@ -58,15 +59,16 @@ class QxClientService : public Service void postStartListIofXml3(QObject *context, std::function call_back = nullptr); void postRuns(QObject *context, std::function call_back = nullptr); + void getHttpJson(const QString &path, const QUrlQuery &query, QObject *context, const std::function &call_back = nullptr); - QNetworkReply* loadQxChanges(int from_id); + QNetworkReply* getQxChangesReply(int from_id); QByteArray apiToken() const; static int currentConnectionId(); QUrl exchangeServerUrl() const; -private: int eventId() const; +private: void loadSettings() override; qf::qmlwidgets::framework::DialogWidget *createDetailWidget() override; void postFileCompressed(std::optional path, std::optional name, QByteArray data, QObject *context, std::function call_back = nullptr); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp index 0b5a4957c..1f79f9dc7 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp @@ -23,6 +23,7 @@ using qf::qmlwidgets::framework::getPlugin; namespace Event::services::qx { constexpr auto COL_ID = "id"; +constexpr auto COL_CHANGE_ID = "change_id"; constexpr auto COL_DATA_TYPE = "data_type"; constexpr auto COL_DATA_ID = "data_id"; constexpr auto COL_DATA = "data"; @@ -69,9 +70,10 @@ QxLateRegistrationsWidget::QxLateRegistrationsWidget(QWidget *parent) : m_model->addColumn(COL_USER_ID, tr("User")); m_model->addColumn(COL_STATUS_MESSAGE, tr("Status message")); m_model->addColumn(COL_CREATED, tr("Created")); + m_model->addColumn(COL_CHANGE_ID, tr("Change ID")); m_model->addColumn(COL_LOCK_NUMBER, tr("Lock")); m_model->addColumn(COL_DATA, tr("Data"));//.setToolTip(tr("Locked for drawing")); - m_model->addColumn(COL_ORIG_DATA, tr("Data"));//.setToolTip(tr("Locked for drawing")); + m_model->addColumn(COL_ORIG_DATA, tr("Orig data"));//.setToolTip(tr("Locked for drawing")); ui->tableView->setTableModel(m_model); showMessage({}); @@ -287,7 +289,7 @@ void QxLateRegistrationsWidget::onTableDoubleClicked(const QModelIndex &ix) if (source != SOURCE_WWW) { return; } - auto change_id = m_model->value(row, COL_ID).toInt(); + auto change_id = m_model->value(row, COL_CHANGE_ID).toInt(); auto status = m_model->value(row, COL_STATUS).toString(); auto lock_number = m_model->value(row, COL_LOCK_NUMBER).toInt(); auto data_id = m_model->value(row, COL_DATA_ID).toInt(); diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h index 22605a833..83ab57062 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchange.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace Event::services::qx { @@ -17,6 +19,7 @@ struct RunChange // static RunChange fromJsonString(const QString &json); static RunChange fromVariantMap(const QVariantMap &map); }; + struct OrigRunRecord { QString first_name; @@ -28,17 +31,46 @@ struct OrigRunRecord QVariantMap toVariantMap() const; }; -// struct ChangesRecord +enum class DataType { + OcChange, + RunUpdateRequest, + RunUpdated, + RadioPunch, + CardReadout, +}; +enum class ChangeStatus { + Pending, + Locked, + Accepted, + Rejected, +}; +struct EventChange +{ + int64_t id; + QString source; + DataType data_type; + int64_t data_id; + QVariant data; + QString user_id; + ChangeStatus status; + QString status_message; + QDateTime created; + int64_t lock_number; + + QVariantMap toVariantMap() const; +}; + +// struct ChangeRecord // { -// int64_t id; -// QString source; -// QString data_type, -// int64_t data_id; -// data: ChangeData, -// user_id: Option, -// status: Option, -// created: QxDateTime, -// lock_number: Option, +// int64_t id; +// QString source; +// QString data_type, +// int64_t data_id; +// data: ChangeData, +// user_id: Option, +// status: Option, +// created: QxDateTime, +// lock_number: Option, // }; } // namespace Event::services::qx diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp index 4a9904919..6a2042778 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp @@ -45,6 +45,7 @@ RunChangeDialog::RunChangeDialog(int change_id, int run_id, int lock_number, con ui->edNote->setText(run_change.note); ui->edRunId->setValue(run_id); + ui->edChangeId->setValue(change_id); ui->edLockNumber->setValue(lock_number); ui->grpFirstName->setChecked(run_change.first_name.has_value()); @@ -145,6 +146,20 @@ void RunChangeDialog::lockChange() { auto *svc = service(); auto *nm = svc->networkManager(); + + auto path = QStringLiteral("/api/event/%1/changes").arg(svc->eventId()); + QUrlQuery query; + query.addQueryItem("from_id", QString::number(m_changeId)); + query.addQueryItem("limit", QString::number(1)); + svc->getHttpJson(path, query, this, [this](auto data, auto error) { + if (!error.isEmpty()) { + setMessage(error, true); + return; + } + auto rec = data.toList().value(0).toMap(); + }); + + QNetworkRequest request; auto url = svc->exchangeServerUrl(); // qfInfo() << "url " << url.toString(); @@ -155,7 +170,7 @@ void RunChangeDialog::lockChange() auto connection_id = QxClientService::currentConnectionId(); query.addQueryItem("lock_number", QString::number(connection_id)); url.setQuery(query); - // qfInfo() << "GET " << url.toString(); + qfInfo() << "GET " << url.toString(); request.setUrl(url); request.setRawHeader(QxClientService::QX_API_TOKEN, svc->apiToken()); @@ -180,7 +195,7 @@ void RunChangeDialog::lockChange() } } else { - setMessage(tr("Lock change error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data))); + setMessage(tr("Lock change error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data)), true); } reply->deleteLater(); }); @@ -269,6 +284,17 @@ void RunChangeDialog::applyLocalChanges(bool is_accepted) } } +bool RunChangeDialog::checkHttpError(QNetworkReply *reply) +{ + if (reply->error() != QNetworkReply::NetworkError::NoError) { + setMessage(tr("Http error: %1\n%2") + .arg(reply->request().url().toString()) + .arg(reply->errorString()), true); + return false; + } + return true; +} + } // namespace Event::services::qx diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h index 5b2d5a7b9..d704a1d48 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.h @@ -5,6 +5,8 @@ #include +class QNetworkReply; + namespace Event::services::qx { namespace Ui { @@ -24,7 +26,7 @@ class RunChangeDialog : public QDialog private: QxClientService* service(); - void setMessage(const QString &msg = {}, bool error = false); + void setMessage(const QString &msg, bool error); void loadOrigValues(); void loadClassId(); @@ -33,6 +35,8 @@ class RunChangeDialog : public QDialog void resolveChanges(bool is_accepted); void applyLocalChanges(bool is_accepted); + + bool checkHttpError(QNetworkReply *reply); private: Ui::RunChangeDialog *ui; int m_changeId = 0; diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui index c63e442d3..c18f157b3 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.ui @@ -72,6 +72,35 @@ + + + + Change ID + + + + + + + + 0 + 0 + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + true + + + None + + + 999999999 + + + @@ -354,6 +383,10 @@ + edClassName + edRunId + edChangeId + edLockNumber grpFirstName edFirstNameOrig edFirstName @@ -368,10 +401,12 @@ edSiCardRentOrig edSiCard edSiCardRent + edNote edStatusMessage - btCancel - btReject + chkForce btAccept + btReject + btCancel From 2740e2e03ba5988418b40f01e4994e682877bd58 Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Mon, 25 Aug 2025 19:21:32 +0200 Subject: [PATCH 19/20] Clazy fixes --- .../Event/src/services/qx/runchangedialog.cpp | 111 +++++++++--------- .../Runs/src/eventstatisticswidget.cpp | 17 ++- .../plugins/Runs/src/eventstatisticswidget.h | 12 +- .../plugins/Runs/src/runsplugin.cpp | 21 ++-- 4 files changed, 84 insertions(+), 77 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp index 6a2042778..9b0988b3a 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/runchangedialog.cpp @@ -144,61 +144,62 @@ void RunChangeDialog::loadClassId() void RunChangeDialog::lockChange() { - auto *svc = service(); - auto *nm = svc->networkManager(); - - auto path = QStringLiteral("/api/event/%1/changes").arg(svc->eventId()); - QUrlQuery query; - query.addQueryItem("from_id", QString::number(m_changeId)); - query.addQueryItem("limit", QString::number(1)); - svc->getHttpJson(path, query, this, [this](auto data, auto error) { - if (!error.isEmpty()) { - setMessage(error, true); - return; - } - auto rec = data.toList().value(0).toMap(); - }); - - - QNetworkRequest request; - auto url = svc->exchangeServerUrl(); - // qfInfo() << "url " << url.toString(); - url.setPath("/api/event/current/changes/lock-change"); - - QUrlQuery query; - query.addQueryItem("change_id", QString::number(m_changeId)); - auto connection_id = QxClientService::currentConnectionId(); - query.addQueryItem("lock_number", QString::number(connection_id)); - url.setQuery(query); - qfInfo() << "GET " << url.toString(); - - request.setUrl(url); - request.setRawHeader(QxClientService::QX_API_TOKEN, svc->apiToken()); - auto *reply = nm->get(request); - connect(reply, &QNetworkReply::finished, this, [this, reply, connection_id]() { - auto data = reply->readAll(); - if (reply->error() == QNetworkReply::NetworkError::NoError) { - m_lockNumber = data.toInt(); - ui->edLockNumber->setValue(m_lockNumber); - if (m_lockNumber == connection_id) { - ui->btAccept->setDisabled(false); - ui->btReject->setDisabled(false); - - qf::core::sql::Query q; - q.execThrow(QStringLiteral("UPDATE qxchanges SET lock_number=%1, status='Locked' WHERE id=%2") - .arg(connection_id) - .arg(m_changeId) - ); - } - else { - setMessage(tr("Change is locked already by other client: %1, current client id:.%2").arg(m_lockNumber).arg(connection_id), false); - } - } - else { - setMessage(tr("Lock change error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data)), true); - } - reply->deleteLater(); - }); + // NIY + // auto *svc = service(); + // auto *nm = svc->networkManager(); + + // auto path = QStringLiteral("/api/event/%1/changes").arg(svc->eventId()); + // QUrlQuery query; + // query.addQueryItem("from_id", QString::number(m_changeId)); + // query.addQueryItem("limit", QString::number(1)); + // svc->getHttpJson(path, query, this, [this](auto data, auto error) { + // if (!error.isEmpty()) { + // setMessage(error, true); + // return; + // } + // auto rec = data.toList().value(0).toMap(); + // }); + + + // QNetworkRequest request; + // auto url = svc->exchangeServerUrl(); + // // qfInfo() << "url " << url.toString(); + // url.setPath("/api/event/current/changes/lock-change"); + + // QUrlQuery query; + // query.addQueryItem("change_id", QString::number(m_changeId)); + // auto connection_id = QxClientService::currentConnectionId(); + // query.addQueryItem("lock_number", QString::number(connection_id)); + // url.setQuery(query); + // qfInfo() << "GET " << url.toString(); + + // request.setUrl(url); + // request.setRawHeader(QxClientService::QX_API_TOKEN, svc->apiToken()); + // auto *reply = nm->get(request); + // connect(reply, &QNetworkReply::finished, this, [this, reply, connection_id]() { + // auto data = reply->readAll(); + // if (reply->error() == QNetworkReply::NetworkError::NoError) { + // m_lockNumber = data.toInt(); + // ui->edLockNumber->setValue(m_lockNumber); + // if (m_lockNumber == connection_id) { + // ui->btAccept->setDisabled(false); + // ui->btReject->setDisabled(false); + + // qf::core::sql::Query q; + // q.execThrow(QStringLiteral("UPDATE qxchanges SET lock_number=%1, status='Locked' WHERE id=%2") + // .arg(connection_id) + // .arg(m_changeId) + // ); + // } + // else { + // setMessage(tr("Change is locked already by other client: %1, current client id:.%2").arg(m_lockNumber).arg(connection_id), false); + // } + // } + // else { + // setMessage(tr("Lock change error: %1\n%2").arg(reply->errorString()).arg(QString::fromUtf8(data)), true); + // } + // reply->deleteLater(); + // }); } void RunChangeDialog::resolveChanges(bool is_accepted) diff --git a/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp b/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp index 3410511aa..1c51fdb03 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace qfs = qf::core::sql; namespace qfu = qf::core::utils; @@ -408,6 +409,12 @@ EventStatisticsWidget::EventStatisticsWidget(QWidget *parent) connect(getPlugin(), &Event::EventPlugin::dbEventNotify, this, &EventStatisticsWidget::onDbEventNotify, Qt::QueuedConnection); connect(getPlugin(), &Event::EventPlugin::currentStageIdChanged, this, &EventStatisticsWidget::reloadDeferred); + connect(ui->btReload, &QPushButton::clicked, this, &EventStatisticsWidget::onReloadClicked); + connect(ui->btOptions, &QPushButton::clicked, this, &EventStatisticsWidget::onOptionsClicked); + connect(ui->btPrintResultsSelected, &QPushButton::clicked, this, &EventStatisticsWidget::onPrintResultsSelectedClicked); + connect(ui->btPrintResultsNew, &QPushButton::clicked, this, &EventStatisticsWidget::onPrintResultsNewClicked); + connect(ui->btClearNewInSelectedRows, &QPushButton::clicked, this, &EventStatisticsWidget::onClearNewInSelectedRowsClicked); + initAutoRefreshTimer(); } @@ -518,13 +525,13 @@ void EventStatisticsWidget::initAutoRefreshTimer() } } -void EventStatisticsWidget::on_btReload_clicked() +void EventStatisticsWidget::onReloadClicked() { qfLogFuncFrame(); reload(); } -void EventStatisticsWidget::on_btPrintResultsSelected_clicked() +void EventStatisticsWidget::onPrintResultsSelectedClicked() { QList rows; for(int i : ui->tableView->selectedRowsIndexes()) @@ -580,7 +587,7 @@ void EventStatisticsWidget::printResultsForRows(const QList &rows) } } -void EventStatisticsWidget::on_btClearNewInSelectedRows_clicked() +void EventStatisticsWidget::onClearNewInSelectedRowsClicked() { qfLogFuncFrame(); QList classdefs_ids; @@ -621,7 +628,7 @@ QVariantMap EventStatisticsWidget::options() return m; } -void EventStatisticsWidget::on_btOptions_clicked() +void EventStatisticsWidget::onOptionsClicked() { EventStatisticsOptions dlg(this); if(dlg.exec()) { @@ -629,7 +636,7 @@ void EventStatisticsWidget::on_btOptions_clicked() } } -void EventStatisticsWidget::on_btPrintResultsNew_clicked() +void EventStatisticsWidget::onPrintResultsNewClicked() { qfLogFuncFrame(); reload(); diff --git a/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.h b/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.h index d83b3ca20..5d57d8c97 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.h +++ b/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.h @@ -27,12 +27,12 @@ class EventStatisticsWidget : public QWidget void loadPersistentSettings(); void savePersistentSettings(); -private slots: - void on_btReload_clicked(); - void on_btOptions_clicked(); - void on_btPrintResultsSelected_clicked(); - void on_btPrintResultsNew_clicked(); - void on_btClearNewInSelectedRows_clicked(); +private: + void onReloadClicked(); + void onOptionsClicked(); + void onPrintResultsSelectedClicked(); + void onPrintResultsNewClicked(); + void onClearNewInSelectedRowsClicked(); private: int currentStageId(); void initAutoRefreshTimer(); diff --git a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp index d260c19b4..0124b0635 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp @@ -868,15 +868,16 @@ qf::core::utils::TreeTable RunsPlugin::addLapsToStageResultsTable(int course_id, int stp = q.value(3).toInt(); if(stp <= 0) continue; - stp_times[pos][run_id] = RunStp{run_id, stp}; + stp_times[pos][run_id] = RunStp{.runId=run_id, .time=stp}; int lap = q.value(4).toInt(); if(lap <= 0) continue; - lap_times[pos][run_id] = RunStp{run_id, lap}; + lap_times[pos][run_id] = RunStp{.runId=run_id, .time=lap}; } } auto make_pos = [](QMap ×) { - for(int pos : times.keys()) { + const auto control_positions = times.keys(); + for(int pos : control_positions) { RunStpMap &map = times[pos]; QList lst = map.values(); std::sort(lst.begin(), lst.end(), [](const RunStp &a, const RunStp &b) { return a.time < b.time; }); @@ -896,10 +897,10 @@ qf::core::utils::TreeTable RunsPlugin::addLapsToStageResultsTable(int course_id, const RunStp &stprun = stps.value(run_id); const RunStpMap &laps = lap_times.value(j+1); const RunStp &laprun = laps.value(run_id); - tt_row.setValue(col_stp_time0_ix + 4*j + 0, stprun.time); - tt_row.setValue(col_stp_time0_ix + 4*j + 1, stprun.pos); - tt_row.setValue(col_stp_time0_ix + 4*j + 2, laprun.time); - tt_row.setValue(col_stp_time0_ix + 4*j + 3, laprun.pos); + tt_row.setValue(col_stp_time0_ix + (4 * j) + 0, stprun.time); + tt_row.setValue(col_stp_time0_ix + (4 * j) + 1, stprun.pos); + tt_row.setValue(col_stp_time0_ix + (4 * j) + 2, laprun.time); + tt_row.setValue(col_stp_time0_ix + (4 * j) + 3, laprun.pos); } tt.setRow(i, tt_row); } @@ -2211,7 +2212,6 @@ void RunsPlugin::export_startListClubsHtml() if(QDir().mkpath(file_name)) { QString default_file_name = "startlist-clubs.html"; file_name += "/" + default_file_name; - QVariantMap options; qf::core::utils::HtmlUtils::FromHtmlListOptions opts; opts.setDocumentTitle(tr("Start list by clubs")); QString str = qf::core::utils::HtmlUtils::fromHtmlList(body, opts); @@ -2324,7 +2324,6 @@ QString RunsPlugin::export_resultsHtmlStage(bool with_laps) fwk->hideProgress(); if(QDir().mkpath(file_dir)) { QString file_name = file_dir + "/results.html"; - QVariantMap options; qf::core::utils::HtmlUtils::FromHtmlListOptions opts; opts.setDocumentTitle(tr("Stage results")); QString str = qf::core::utils::HtmlUtils::fromHtmlList(body, opts); @@ -2516,7 +2515,7 @@ void RunsPlugin::exportResultsHtmlStageWithLaps(const QString &laps_file_name, c QVariantList{"th", QVariantMap{{QStringLiteral("class"), "brb"}}, tr("Loss")}, }; int i = 1; - for(QVariant v : course_codes) { + for(const auto &_ : course_codes) { append_list(trr, QVariantList{"th" , QVariantMap{{"class", "br"}, {"colspan", "2"}} , (i < course_codes.size())? QVariant(i++): tr("FIN")}); @@ -2531,7 +2530,7 @@ void RunsPlugin::exportResultsHtmlStageWithLaps(const QString &laps_file_name, c QVariantList{"th", QVariantMap{{QStringLiteral("class"), "brb bbb"}}, "\u00A0"}, }; int i = 1; - for(QVariant v : course_codes) { + for(const auto &v : course_codes) { append_list(trr, QVariantList{"th", QVariantMap{{QStringLiteral("class"), "br bbb"}, {"colspan", "2"}}, (i++ < course_codes.size())? QVariant(quickevent::core::CodeDef{v.toMap()}.code()): QVariant("\u00A0")}); From b2172f6c0a5f29912dc8d86d0d0895c79a7ea9fd Mon Sep 17 00:00:00 2001 From: Fanda Vacek Date: Mon, 25 Aug 2025 19:28:11 +0200 Subject: [PATCH 20/20] Fix clang tidy errors --- .../plugins/Runs/src/eventstatisticswidget.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp b/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp index 1c51fdb03..65db600d7 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/eventstatisticswidget.cpp @@ -25,8 +25,6 @@ #include #include -namespace qfs = qf::core::sql; -namespace qfu = qf::core::utils; using qf::qmlwidgets::framework::getPlugin; using Event::EventPlugin; using Runs::RunsPlugin; @@ -260,10 +258,10 @@ QVariant FooterModel::headerData(int section, Qt::Orientation orientation, int r if(role == Qt::DisplayRole) { return m_columnSums.value(section); } - else if(role == Qt::TextAlignmentRole) { + if(role == Qt::TextAlignmentRole) { return Qt::AlignRight; } - else if(role == Qt::BackgroundRole) { + if(role == Qt::BackgroundRole) { return QColor("khaki"); } } @@ -358,9 +356,7 @@ FooterView::FooterView(QTableView *table_view, QWidget *parent) } } -FooterView::~FooterView() -{ -} +FooterView::~FooterView() = default; void FooterView::resetFooterAttributes() {