diff --git a/3rdparty/necrolog b/3rdparty/necrolog index d38b4d56d..be88dcde5 160000 --- a/3rdparty/necrolog +++ b/3rdparty/necrolog @@ -1 +1 @@ -Subproject commit d38b4d56d2715e8b09f6ab27e05a89670a3adaf5 +Subproject commit be88dcde5ec0bb7dc224e285e63d1dd0e18834a7 diff --git a/libqf/libqfqmlwidgets/src/framework/plugin.cpp b/libqf/libqfqmlwidgets/src/framework/plugin.cpp index 0d6e1335f..06847a687 100644 --- a/libqf/libqfqmlwidgets/src/framework/plugin.cpp +++ b/libqf/libqfqmlwidgets/src/framework/plugin.cpp @@ -57,12 +57,12 @@ QString Plugin::findReportFile(const QString &report_file_path) const search_paths << effectiveReportsDir() + '/' + m_featureId + "/qml/reports"; //search_paths << qmlReportsDir(); for(const QString &dir : search_paths) { - //qfInfo() << "search_path:" << dir; + qfMessage() << "search_path:" << dir; auto fn = dir + '/' + report_file_path; - //qfInfo() << "trying:" << fn; + qfMessage() << "trying:" << fn; QFileInfo fi(fn); if(fi.isFile()) { - //qfInfo() << "HIT:" << fn; + qfMessage() << "HIT:" << fn; return fn; } } diff --git a/libqf/libqfqmlwidgets/src/tableview.cpp b/libqf/libqfqmlwidgets/src/tableview.cpp index a59b4156f..026df7c3e 100644 --- a/libqf/libqfqmlwidgets/src/tableview.cpp +++ b/libqf/libqfqmlwidgets/src/tableview.cpp @@ -57,12 +57,12 @@ TableView::TableView(QWidget *parent) : qfLogFuncFrame() << this; setItemDelegate(new TableItemDelegate(this)); { - HeaderView *h = new HeaderView(Qt::Horizontal, this); + auto *h = new HeaderView(Qt::Horizontal, this); setHorizontalHeader(h); connect(this, &TableView::seekStringChanged, h, &HeaderView::setSeekString); } { - HeaderView *h = new HeaderView(Qt::Vertical, this); + auto *h = new HeaderView(Qt::Vertical, this); setVerticalHeader(h); } setSortingEnabled(true); @@ -132,7 +132,7 @@ QAbstractProxyModel* TableView::lastProxyModel() const { QAbstractProxyModel *ret = nullptr; for(auto m=Super::model(); m; ) { - QAbstractProxyModel *pxm = qobject_cast(m); + auto *pxm = qobject_cast(m); if(pxm) { ret = pxm; m = pxm->sourceModel(); @@ -156,7 +156,7 @@ void TableView::setModel(QAbstractItemModel *model) qf::core::model::TableModel *TableView::tableModel() const { - qf::core::model::TableModel *ret = qobject_cast(lastProxyModel()->sourceModel()); + auto *ret = qobject_cast(lastProxyModel()->sourceModel()); return ret; } @@ -355,8 +355,9 @@ void TableView::setDirtyRowsMenuSectionEnabled(bool b) void TableView::setReadOnly(bool ro) { - if(ro == isReadOnly()) + if(ro == isReadOnly()) { return; + } m_isReadOnly = ro; setEditRowsMenuSectionEnabled(!ro); @@ -523,7 +524,7 @@ void TableView::paste() try { dialogs::Dialog dlg(this); dlg.setButtons(QDialogButtonBox::Ok); - internal::TableViewCopyToDialogWidget *w = new internal::TableViewCopyToDialogWidget(); + auto *w = new internal::TableViewCopyToDialogWidget(); dlg.setCentralWidget(w); int col_cnt = 0; { @@ -567,7 +568,7 @@ void TableView::paste() r.clearEditFlags(); } TableView *tv = w->tableView(); - qfm::TableModel *tm = new qfm::TableModel(tv); + auto *tm = new qfm::TableModel(tv); tm->setTable(t); tv->setTableModel(tm); tv->setContextMenuActions(tv->contextMenuActionsForGroups(AllActions)); @@ -698,7 +699,7 @@ void TableView::editCellContentInEditor() cell_text = QString::fromUtf8(cell_value.toByteArray()); else cell_text = cell_value.toString(); - TextEditWidget *w = new TextEditWidget(this); + auto *w = new TextEditWidget(this); w->setText(cell_text); w->setSuggestedFileName("new_file.txt"); /* @@ -708,7 +709,7 @@ void TableView::editCellContentInEditor() } */ dialogs::Dialog dlg(this); - DialogButtonBox *bb = new DialogButtonBox(QDialogButtonBox::Cancel, this); + auto *bb = new DialogButtonBox(QDialogButtonBox::Cancel, this); QAbstractButton *bt_save = bb->addButton(QDialogButtonBox::Save); connect(bt_save, &QAbstractButton::clicked, &dlg, &QDialog::accept); dlg.setButtonBox(bb); @@ -742,13 +743,13 @@ void TableView::exportCSV() if(!m) return; - qf::qmlwidgets::ExportCsvTableViewWidget *w = new qf::qmlwidgets::ExportCsvTableViewWidget(this, this); + auto *w = new qf::qmlwidgets::ExportCsvTableViewWidget(this, this); if(!persistentSettingsPath().isEmpty()) { w->setPersistentOptionsPath(persistentSettingsPath() + "/exportCSV"); w->loadPersistentOptions(); } dialogs::Dialog dlg(this); - DialogButtonBox *bb = new DialogButtonBox(QDialogButtonBox::Cancel, this); + auto *bb = new DialogButtonBox(QDialogButtonBox::Cancel, this); QAbstractButton *bt_apply = bb->addButton(QDialogButtonBox::Apply); connect(bt_apply, &QAbstractButton::clicked, w, &qf::qmlwidgets::ExportCsvTableViewWidget::applyOptions, Qt::QueuedConnection); dlg.setButtonBox(bb); @@ -762,13 +763,13 @@ void TableView::exportCSV() void TableView::exportReport() { qfLogFuncFrame(); - reports::PrintTableViewWidget *w = new reports::PrintTableViewWidget(this); + auto *w = new reports::PrintTableViewWidget(this); if(!persistentSettingsPath().isEmpty()) { w->setPersistentOptionsPath(persistentSettingsPath() + "/exportReport"); w->loadPersistentOptions(); } dialogs::Dialog dlg(this); - DialogButtonBox *bb = new DialogButtonBox(QDialogButtonBox::Cancel, this); + auto *bb = new DialogButtonBox(QDialogButtonBox::Cancel, this); QAbstractButton *bt_apply = bb->addButton(QDialogButtonBox::Apply); connect(bt_apply, &QAbstractButton::clicked, w, &reports::PrintTableViewWidget::applyOptions, Qt::QueuedConnection); dlg.setButtonBox(bb); @@ -931,7 +932,7 @@ void TableView::exportReport_helper(const QVariant& _options) //qfInfo() << ttable.toString(); - reports::ReportViewWidget *rw = new reports::ReportViewWidget(nullptr); + auto *rw = new reports::ReportViewWidget(nullptr); rw->setTableData(QString(), ttable); QString report_fn = opts.value("report").toMap().value("fileName").toString(); rw->setReport(report_fn); @@ -1341,7 +1342,7 @@ void TableView::savePersistentSettings() if(!path.isEmpty()) { QSettings settings; settings.beginGroup(path); - HeaderView *horiz_header = qobject_cast(horizontalHeader()); + auto *horiz_header = qobject_cast(horizontalHeader()); QByteArray header_state = horiz_header->saveState(); settings.setValue("horizontalheader", QString::fromLatin1(header_state.toBase64())); @@ -1357,7 +1358,7 @@ void TableView::onSqlException(const QString &what, const QString &where, const void TableView::keyPressEvent(QKeyEvent *e) { - qfLogFuncFrame() << "key:" << e->key() << "modifiers:" << e->modifiers(); + // qfLogFuncFrame() << "key:" << e->key() << "modifiers:" << e->modifiers(); if(!model()) { e->ignore(); return; @@ -1375,12 +1376,12 @@ void TableView::keyPressEvent(QKeyEvent *e) e->accept(); return; } - else if(e->key() == Qt::Key_V) { + if(e->key() == Qt::Key_V) { paste(); e->accept(); return; } - else if(e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { + if(e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { qfDebug() << "\tCTRL+ENTER"; postRow(); e->accept(); @@ -1458,9 +1459,7 @@ void TableView::keyPressEvent(QKeyEvent *e) e->accept(); return; } - else { - cancelSeek(); - } + cancelSeek(); //bool event_should_be_accepted = false; /// nejedna se o inkrementalni vyhledavani, zkusime editaci if(state() == EditingState) { @@ -1808,7 +1807,7 @@ void TableView::createActions() a->setOid("select"); m_actionGroups[SelectActions] << a->oid(); m_actions[a->oid()] = a; - QMenu *m = new QMenu(this); + auto *m = new QMenu(this); a->setMenu(m); { a = new Action(tr("Select current column"), this); @@ -1835,7 +1834,7 @@ void TableView::createActions() a->setOid("calculate"); m_actionGroups[CalculateActions] << a->oid(); m_actions[a->oid()] = a; - QMenu *m = new QMenu(this); + auto *m = new QMenu(this); a->setMenu(m); { a = new Action(tr("Sum column"), this); @@ -1855,7 +1854,7 @@ void TableView::createActions() a->setOid("export"); m_actionGroups[ExportActions] << a->oid(); m_actions[a->oid()] = a; - QMenu *m = new QMenu(this); + auto *m = new QMenu(this); a->setMenu(m); { a = new Action(tr("Report"), this); @@ -1900,7 +1899,7 @@ void TableView::createActions() a->setOid("import"); m_actionGroups[ImportActions] << a->oid(); m_actions[a->oid()] = a; - QMenu *m = new QMenu(this); + auto *m = new QMenu(this); a->setMenu(m); { a = new Action(tr("CSV"), this); @@ -2126,7 +2125,7 @@ void TableView::insertRowInline() if(tri < 0) { qfWarning() << "Valid proxy model index has invalid table model index!"; /// this can happen when one inserts to empty table ???? why ???? - tri = ri = 0; + tri = 0; } } tableModel()->insertRow(tri); @@ -2247,9 +2246,7 @@ bool TableView::edit(const QModelIndex& index, EditTrigger trigger, QEvent* even } else { if(trigger == QTableView::DoubleClicked || trigger == QTableView::EditKeyPressed) { - if(read_only) { - } - else { + if(!read_only) { emit editCellRequest(index); QVariant id = selectedRow().value(idColumnName()); if(id.isValid()) { diff --git a/libqf/libqfqmlwidgets/src/tableviewtoolbar.cpp b/libqf/libqfqmlwidgets/src/tableviewtoolbar.cpp index f07f21aff..3ea53a8ae 100644 --- a/libqf/libqfqmlwidgets/src/tableviewtoolbar.cpp +++ b/libqf/libqfqmlwidgets/src/tableviewtoolbar.cpp @@ -41,9 +41,7 @@ TableViewToolBar::TableViewToolBar(QWidget *parent) : #endif } -TableViewToolBar::~TableViewToolBar() -{ -} + void TableViewToolBar::setTableView(TableView *table_view) { @@ -66,7 +64,7 @@ void TableViewToolBar::addPendingActions() Q_FOREACH(auto a, m_pendingActions) lst << a; addActions(lst); - QLabel *lbl = new QLabel(tr("Filter")); + auto *lbl = new QLabel(tr("Filter")); auto *style = Style::instance(); QPixmap px = style->pixmap("find"); lbl->setPixmap(px); diff --git a/libqf/libqfqmlwidgets/src/tableviewtoolbar.h b/libqf/libqfqmlwidgets/src/tableviewtoolbar.h index b3d92afaf..8f181379f 100644 --- a/libqf/libqfqmlwidgets/src/tableviewtoolbar.h +++ b/libqf/libqfqmlwidgets/src/tableviewtoolbar.h @@ -29,12 +29,13 @@ class FilterCombo : public QComboBox class QFQMLWIDGETS_DECL_EXPORT TableViewToolBar : public QToolBar { Q_OBJECT + + using Super = QToolBar; + Q_PROPERTY(qf::qmlwidgets::TableView* tableView READ tableView WRITE setTableView FINAL) -private: - typedef QToolBar Super; public: explicit TableViewToolBar(QWidget *parent = nullptr); - ~TableViewToolBar() Q_DECL_OVERRIDE; + ~TableViewToolBar() Q_DECL_OVERRIDE = default; public: void setTableView(TableView *table_view); qf::qmlwidgets::TableView* tableView() const { return m_tableView; } diff --git a/quickevent/app/quickevent/CMakeLists.txt b/quickevent/app/quickevent/CMakeLists.txt index 769c3450a..6cff0992d 100644 --- a/quickevent/app/quickevent/CMakeLists.txt +++ b/quickevent/app/quickevent/CMakeLists.txt @@ -45,18 +45,8 @@ add_executable(quickevent plugins/Classes/src/importcoursedef.cpp plugins/Competitors/src/competitordocument.cpp - plugins/Competitors/src/competitorsplugin.cpp - plugins/Competitors/src/competitorswidget.cpp - plugins/Competitors/src/competitorswidget.ui plugins/Competitors/src/competitorwidget.cpp plugins/Competitors/src/competitorwidget.ui - plugins/Competitors/src/findregistrationedit.cpp - plugins/Competitors/src/lentcardssettingspage.cpp - plugins/Competitors/src/lentcardssettingspage.ui - plugins/Competitors/src/registrationswidget.cpp - plugins/Competitors/src/registrationswidget.ui - plugins/Competitors/src/stationsbackupmemorywidget.cpp - plugins/Competitors/src/stationsbackupmemorywidget.ui plugins/Core/src/coreplugin.cpp plugins/Core/src/reportssettings.cpp @@ -80,7 +70,11 @@ add_executable(quickevent plugins/Event/src/stagedocument.cpp plugins/Event/src/stagewidget.cpp plugins/Event/src/stagewidget.ui - + plugins/Event/src/findregistrationedit.cpp + plugins/Event/src/lentcardssettingspage.cpp + plugins/Event/src/lentcardssettingspage.ui + plugins/Event/src/registrationswidget.cpp + plugins/Event/src/registrationswidget.ui plugins/Event/src/services/emmaclient.cpp plugins/Event/src/services/emmaclientwidget.cpp plugins/Event/src/services/emmaclientwidget.ui @@ -187,7 +181,7 @@ add_executable(quickevent quickevent.rc plugins/Classes/qml/reports/table.qml - plugins/Competitors/qml/reports/competitorsStatistics.qml + plugins/Event/qml/DbSchema.qml plugins/Event/qml/sql/def/Boolean.qml plugins/Event/qml/sql/def/Date.qml @@ -205,16 +199,19 @@ add_executable(quickevent plugins/Event/qml/sql/def/Table.qml plugins/Event/qml/sql/def/Time.qml plugins/Event/qml/sql/def/private/FieldType.qml + plugins/Receipts/qml/reports/error.qml plugins/Receipts/qml/reports/sicard.qml plugins/Receipts/qml/reports/receipts/Classic.qml plugins/Receipts/qml/reports/receipts/ClassicLottery.qml plugins/Receipts/qml/reports/receipts/Default.qml plugins/Receipts/qml/reports/receipts/private/LotteryTicket.qml + plugins/Relays/qml/reports/results.qml plugins/Relays/qml/reports/results_condensed.qml plugins/Relays/qml/reports/startList_classes.qml plugins/Relays/qml/reports/startList_clubs.qml + plugins/Runs/qml/reports/competitorsWithCardRent.qml plugins/Runs/qml/reports/results_nstages.qml plugins/Runs/qml/reports/results_nstagesSpeaker.qml @@ -231,6 +228,8 @@ add_executable(quickevent plugins/Runs/qml/reports/awards/results_stage_awards-hsh-2023.qml plugins/Runs/qml/reports/awards/results_stage_awards-hsh.qml plugins/Runs/qml/reports/awards/results_stage_awards.qml + plugins/Runs/qml/reports/competitorsStatistics.qml + plugins/shared/qml/reports/Cell.qml plugins/shared/qml/reports/HeaderCell.qml plugins/shared/qml/reports/QfObject.qml @@ -269,6 +268,6 @@ install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_BINDIR}/translations) #install(DIRECTORY plugins/Runs/qml/reports DESTINATION ${CMAKE_INSTALL_BINDIR}/reports/Runs/qml) #install(DIRECTORY plugins/Receipts/qml/reports DESTINATION ${CMAKE_INSTALL_BINDIR}/reports/Receipts/qml) -foreach(plugin IN ITEMS Classes Competitors Runs Relays Receipts shared) +foreach(plugin IN ITEMS Classes Runs Relays Receipts shared) install(DIRECTORY plugins/${plugin}/qml/reports DESTINATION ${CMAKE_INSTALL_BINDIR}/reports/${plugin}/qml) endforeach() diff --git a/quickevent/app/quickevent/plugins/CardReader/src/cardreaderwidget.cpp b/quickevent/app/quickevent/plugins/CardReader/src/cardreaderwidget.cpp index ef95a4ce2..104876407 100644 --- a/quickevent/app/quickevent/plugins/CardReader/src/cardreaderwidget.cpp +++ b/quickevent/app/quickevent/plugins/CardReader/src/cardreaderwidget.cpp @@ -2,7 +2,6 @@ #include "ui_cardreaderwidget.h" #include "cardreadersettings.h" -#include "cardreaderplugin.h" #include "cardreaderplugin.h" #include @@ -48,8 +47,9 @@ #include #include #include -#include +// #include #include +#include #include #include @@ -72,7 +72,8 @@ using qf::qmlwidgets::framework::getPlugin; using Event::EventPlugin; using CardReader::CardReaderPlugin; using Receipts::ReceiptsPlugin; -using Competitors::CompetitorsPlugin; +// using Competitors::CompetitorsPlugin; +using Runs::RunsPlugin; namespace { class Model : public quickevent::core::og::SqlTableModel @@ -196,10 +197,10 @@ QVariant Model::value(int row_ix, int column_ix) const sl << tr("OT", "OverTime"); if(is_disqualified && !mis_punch && !bad_check && !not_start && !not_finish && !is_disqualified_by_organizer && !over_time) sl << tr("DSQ", "Disqualified"); - if(sl.isEmpty()) + if(sl.isEmpty()) { return QStringLiteral(""); - else - return sl.join(','); + } + return sl.join(','); } return Super::value(row_ix, column_ix); } @@ -379,11 +380,11 @@ void CardReaderWidget::settleDownInPartWidget(::PartWidget *part_widget) //a_station->addActionInto(m_actSettings); //a_station->addActionInto(m_actCommOpen); { - QAction *a = new QAction(tr("Station info")); + auto *a = new QAction(tr("Station info")); a->setEnabled(false); connect(commPort(), &siut::CommPort::openChanged, a, &QAction::setEnabled); connect(a, &QAction::triggered, [this]() { - siut::SiTaskStationConfig *cmd = new siut::SiTaskStationConfig(); + auto *cmd = new siut::SiTaskStationConfig(); connect(cmd, &siut::SiTaskStationConfig::finished, this, [this](bool ok, QVariant result) { if(ok) { siut::SiStationConfig cfg(result.toMap()); @@ -396,7 +397,7 @@ void CardReaderWidget::settleDownInPartWidget(::PartWidget *part_widget) a_station->addActionInto(a); } { - QAction *a = new QAction(tr("Read station memory")); + auto *a = new QAction(tr("Read station memory")); a->setEnabled(false); connect(commPort(), &siut::CommPort::openChanged, a, &QAction::setEnabled); connect(a, &QAction::triggered, this, &CardReaderWidget::readStationBackupMemory); @@ -427,18 +428,19 @@ void CardReaderWidget::settleDownInPartWidget(::PartWidget *part_widget) { auto *m_import_cards = a_tools->addMenuInto("importCards", tr("Import cards")); { - qfw::Action *a = new qfw::Action(tr("Laps only CSV")); + auto *a = new qfw::Action(tr("Laps only CSV")); connect(a, &qf::qmlwidgets::Action::triggered, this, &CardReaderWidget::importCards_lapsOnlyCsv); m_import_cards->addActionInto(a); } { - qfw::Action *a = new qfw::Action(tr("SI reader backup memory CSV")); + auto *a = new qfw::Action(tr("SI reader backup memory CSV")); connect(a, &qf::qmlwidgets::Action::triggered, this, &CardReaderWidget::importCards_SIReaderBackupMemoryCsv); m_import_cards->addActionInto(a); + m_import_cards->addActionInto(a); } } { - qfw::Action *a = new qfw::Action(tr("Test audio")); + auto *a = new qfw::Action(tr("Test audio")); connect(a, &qf::qmlwidgets::Action::triggered, this, &CardReaderWidget::operatorAudioNotify); a_tools->addActionInto(a); } @@ -535,7 +537,7 @@ siut::CommPort *CardReaderWidget::commPort() void CardReaderWidget::onComOpenChanged(bool comm_is_open) { if(comm_is_open) { - siut::SiTaskSetDirectRemoteMode *cmd = new siut::SiTaskSetDirectRemoteMode(siut::SiTaskSetDirectRemoteMode::Mode::Direct); + auto *cmd = new siut::SiTaskSetDirectRemoteMode(siut::SiTaskSetDirectRemoteMode::Mode::Direct); connect(cmd, &siut::SiTaskSetDirectRemoteMode::finished, this, [this](bool ok) { if(ok) { ui->lblConnectionInfo->setText(tr("Connected to %1 in direct mode.").arg(this->commPort()->portName())); @@ -585,7 +587,7 @@ void CardReaderWidget::appendLog(NecroLog::Level level, const QString& msg) void CardReaderWidget::onSiTaskFinished(int task_type, QVariant result) { qfLogFuncFrame(); - siut::SiTask::Type tt = static_cast(task_type); + auto tt = static_cast(task_type); if(tt == siut::SiTask::Type::CardRead) { siut::SICard card(result.toMap()); if(card.isEmpty()) @@ -631,7 +633,7 @@ void CardReaderWidget::processSICard(const siut::SICard &card) appendLog(NecroLog::Level::Info, tr("card: %1").arg(card.cardNumber())); if(currentReaderMode() == CardReaderSettings::ReaderMode::EditOnPunch) { - getPlugin()->editCompetitorOnPunch(card.cardNumber()); + getPlugin()->editCompetitorOnPunch(card.cardNumber()); return; } @@ -1149,9 +1151,9 @@ void CardReaderWidget::importCards_SIReaderBackupMemoryCsv() void CardReaderWidget::readStationBackupMemory() { - siut::SiTaskReadStationBackupMemory *si_task = new siut::SiTaskReadStationBackupMemory(); + auto *si_task = new siut::SiTaskReadStationBackupMemory(); connect(si_task, &siut::SiTaskStationConfig::progress, this, [this, si_task](int phase, int count) { - QProgressDialog *progress_dlg = this->findChild(QString(), Qt::FindDirectChildrenOnly); + auto *progress_dlg = this->findChild(QString(), Qt::FindDirectChildrenOnly); if(progress_dlg == nullptr) { progress_dlg = new QProgressDialog(tr("Downloading station backup ..."), "Abort", 0, count, this); connect(progress_dlg, &QProgressDialog::canceled, si_task, [si_task]() { @@ -1221,8 +1223,8 @@ void CardReaderWidget::readStationBackupMemory() q2.prepare("UPDATE runs SET checkTimeMs=:checkTimeMs" " WHERE siId=:siId AND checkTimeMs IS NULL AND stageId=" QF_IARG(stage_id) , qfc::Exception::Throw); - for (int i = 0; i < punches.size(); i++) { - QVariantList punch = punches[i].toList(); + for (const auto & plist : punches) { + QVariantList punch = plist.toList(); q1.bindValue(QStringLiteral(":stageId"), stage_id); q1.bindValue(QStringLiteral(":stationNumber"), station_number); int si = punch.value(0).toInt(); diff --git a/quickevent/app/quickevent/plugins/Classes/src/classesplugin.cpp b/quickevent/app/quickevent/plugins/Classes/src/classesplugin.cpp index 70faa6d1a..679b2470b 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/classesplugin.cpp +++ b/quickevent/app/quickevent/plugins/Classes/src/classesplugin.cpp @@ -32,6 +32,29 @@ using Event::EventPlugin; namespace Classes { +void ClassDef::load(int class_id, int stage_id, bool is_relays) +{ + qf::core::sql::QueryBuilder qb; + qb.select2("classdefs", "startTimeMin, lastStartTimeMin, startIntervalMin, vacantsBefore, vacantEvery, vacantsAfter") + .from("classdefs") + .where("classId=" QF_IARG(class_id)); + if(!is_relays) + qb.where("stageId=" QF_IARG(stage_id)); + qfs::Query q(qfs::Connection::forName()); + //qfInfo() << qb.toString(); + q.exec(qb.toString(), qf::core::Exception::Throw); + if(q.next()) { + classInterval = q.value("startIntervalMin").toInt() * 60 * 1000; + classStartFirst = q.value("startTimeMin").toInt() * 60 * 1000; + classStartLast = q.value("lastStartTimeMin").toInt() * 60 * 1000; + } + else { + classInterval = 0; + classStartFirst = 0; + classStartLast = 0; + } +} + ClassesPlugin::ClassesPlugin(QObject *parent) : Super("Classes", parent) { diff --git a/quickevent/app/quickevent/plugins/Classes/src/classesplugin.h b/quickevent/app/quickevent/plugins/Classes/src/classesplugin.h index 6e4142e79..6f7619dfd 100644 --- a/quickevent/app/quickevent/plugins/Classes/src/classesplugin.h +++ b/quickevent/app/quickevent/plugins/Classes/src/classesplugin.h @@ -22,6 +22,15 @@ namespace quickevent { namespace core { class CodeDef; }} namespace Classes { +struct ClassDef +{ + int classStartFirst = 0; + int classStartLast = 0; + int classInterval = 0; + + void load(int class_id, int stage_id, bool is_relays); +}; + class ClassesPlugin : public qf::qmlwidgets::framework::Plugin { Q_OBJECT diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorsplugin.cpp b/quickevent/app/quickevent/plugins/Competitors/src/competitorsplugin.cpp deleted file mode 100644 index e7577fa19..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorsplugin.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "competitorsplugin.h" -#include "competitordocument.h" -#include "plugins/Core/src/widgets/settingsdialog.h" -#include "registrationswidget.h" -#include "competitorwidget.h" -#include "competitorswidget.h" -#include "lentcardssettingspage.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace qfw = qf::qmlwidgets; -namespace qff = qf::qmlwidgets::framework; -namespace qfd = qf::qmlwidgets::dialogs; -namespace qfm = qf::core::model; -namespace qfs = qf::core::sql; -using ::PartWidget; -using qff::getPlugin; -using Event::EventPlugin; - -namespace Competitors { - -CompetitorsPlugin::CompetitorsPlugin(QObject *parent) - : Super("Competitors", parent) -{ - connect(this, &CompetitorsPlugin::installed, this, &CompetitorsPlugin::onInstalled); -} - -CompetitorsPlugin::~CompetitorsPlugin() -{ - //if(m_registrationsDockWidget) - // m_registrationsDockWidget->savePersistentSettingsRecursively(); -} - -QObject *CompetitorsPlugin::createCompetitorDocument(QObject *parent) -{ - CompetitorDocument *ret = new CompetitorDocument(parent); - return ret; -} - -int CompetitorsPlugin::editCompetitor(int id, int mode) -{ - qfLogFuncFrame() << "id:" << id; - auto *w = new CompetitorWidget(); - w->setWindowTitle(tr("Edit Competitor")); - qfd::Dialog dlg(QDialogButtonBox::Save | QDialogButtonBox::Cancel, m_partWidget); - dlg.setDefaultButton(QDialogButtonBox::Save); - dlg.setCentralWidget(w); - w->load(id, (qfm::DataDocument::RecordEditMode)mode); - return dlg.exec(); -} - -void CompetitorsPlugin::onInstalled() -{ - qff::MainWindow *fwk = qff::MainWindow::frameWork(); - m_partWidget = qff::initPluginWidget(tr("&Competitors"), featureId()); - { - m_registrationsDockWidget = new qff::DockWidget(nullptr); - m_registrationsDockWidget->setObjectName("registrationsDockWidget"); - m_registrationsDockWidget->setWindowTitle(tr("Registrations")); - fwk->addDockWidget(Qt::RightDockWidgetArea, m_registrationsDockWidget); - m_registrationsDockWidget->hide(); - connect(m_registrationsDockWidget, &qff::DockWidget::visibilityChanged, this, &CompetitorsPlugin::onRegistrationsDockVisibleChanged); - - auto *a = m_registrationsDockWidget->toggleViewAction(); - //a->setCheckable(true); - a->setShortcut(QKeySequence("ctrl+shift+R")); - fwk->menuBar()->actionForPath("view")->addActionInto(a); - } - - connect(getPlugin(), &Event::EventPlugin::eventOpenChanged, this, &CompetitorsPlugin::reloadRegistrationsModel); - connect(getPlugin(), &Event::EventPlugin::dbEventNotify, this, &CompetitorsPlugin::onDbEventNotify); - - - auto core_plugin = qf::qmlwidgets::framework::getPlugin(); - core_plugin->settingsDialog()->addPage(new LentCardsSettingsPage()); - - emit nativeInstalled(); -} - -void CompetitorsPlugin::onRegistrationsDockVisibleChanged(bool on) -{ - if(on && !m_registrationsDockWidget->widget()) { - auto *rw = new RegistrationsWidget(); - m_registrationsDockWidget->setWidget(rw); - rw->checkModel(); - } -} - -void CompetitorsPlugin::onDbEventNotify(const QString &domain, int connection_id, const QVariant &data) -{ - Q_UNUSED(connection_id) - qfLogFuncFrame() << "domain:" << domain << "payload:" << data; - if(domain == QLatin1String(Event::EventPlugin::DBEVENT_REGISTRATIONS_IMPORTED)) - reloadRegistrationsModel(); - emit dbEventNotify(domain, connection_id, data); -} - -void CompetitorsPlugin::reloadRegistrationsModel() -{ - qfLogFuncFrame() << "isEventOpen():" << getPlugin()->isEventOpen(); - if(getPlugin()->isEventOpen()) - registrationsModel()->reload(); - else - registrationsModel()->clearRows(); - // clear registration table to be regenerated when registrationsTable() will be called - m_registrationsTable = qf::core::utils::Table(); -} - -qf::core::model::SqlTableModel* CompetitorsPlugin::registrationsModel() -{ - if(!m_registrationsModel) { - m_registrationsModel = new qf::core::model::SqlTableModel(this); - m_registrationsModel->addColumn("competitorName", tr("Name")); - m_registrationsModel->addColumn("registration", tr("Reg")); - m_registrationsModel->addColumn("licence", tr("Lic")); - m_registrationsModel->addColumn("siId", tr("SI")); - //m_registrationsModel->addColumn("fistName"); - //m_registrationsModel->addColumn("lastName"); - qfs::QueryBuilder qb; - qb.select2("registrations", "firstName, lastName, licence, registration, siId") - .select("COALESCE(lastName, '') || ' ' || COALESCE(firstName, '') AS competitorName") - .from("registrations") - .orderBy("lastName, firstName"); - m_registrationsModel->setQueryBuilder(qb, false); - } - return m_registrationsModel; -} - -const qf::core::utils::Table &CompetitorsPlugin::registrationsTable() -{ - qf::core::model::SqlTableModel *m = registrationsModel(); - if(m_registrationsTable.isNull() && !m->table().isNull()) { - m_registrationsTable = m->table(); - auto c_nsk = QStringLiteral("competitorNameAscii7"); - m_registrationsTable.appendColumn(c_nsk, QMetaType::QString); - int ix_nsk = m_registrationsTable.fields().fieldIndex(c_nsk); - int ix_cname = m_registrationsTable.fields().fieldIndex(QStringLiteral("competitorName")); - for (int i = 0; i < m_registrationsTable.rowCount(); ++i) { - qf::core::utils::TableRow &row_ref = m_registrationsTable.rowRef(i); - QString nsk = row_ref.value(ix_cname).toString(); - nsk = QString::fromLatin1(qf::core::Collator::toAscii7(QLocale::Czech, nsk, true)); - row_ref.setValue(ix_nsk, nsk); - } - } - return m_registrationsTable; -} - -} diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorsplugin.h b/quickevent/app/quickevent/plugins/Competitors/src/competitorsplugin.h deleted file mode 100644 index 16a8773df..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorsplugin.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef COMPETITORS_COMPETITORSPLUGIN_H -#define COMPETITORS_COMPETITORSPLUGIN_H - -#include - -#include -#include - -namespace qf { - -namespace core { - -namespace model { -class SqlTableModel; -}} - -namespace qmlwidgets { -class Action; -namespace framework { -class PartWidget; -class DockWidget; -}} - -} - -namespace Competitors { - -class CompetitorsPlugin : public qf::qmlwidgets::framework::Plugin -{ - Q_OBJECT - Q_PROPERTY(qf::qmlwidgets::framework::PartWidget* partWidget READ partWidget FINAL) -private: - typedef qf::qmlwidgets::framework::Plugin Super; -public: - CompetitorsPlugin(QObject *parent = nullptr); - ~CompetitorsPlugin() Q_DECL_OVERRIDE; - - qf::qmlwidgets::framework::PartWidget *partWidget() {return m_partWidget;} - - Q_INVOKABLE QObject* createCompetitorDocument(QObject *parent); - Q_INVOKABLE int editCompetitor(int id, int mode); - - Q_SIGNAL int editCompetitorOnPunch(int siid); - Q_SIGNAL void dbEventNotify(const QString &domain, int connection_id, const QVariant &payload); - Q_SIGNAL void competitorEdited(); // used to clear caches with competitors - - Q_SIGNAL void nativeInstalled(); - - Q_SLOT void reloadRegistrationsModel(); - qf::core::model::SqlTableModel* registrationsModel(); - const qf::core::utils::Table& registrationsTable(); - - Q_SLOT void onInstalled(); -private: - void onRegistrationsDockVisibleChanged(bool on = true); - void onDbEventNotify(const QString &domain, int connection_id, const QVariant &data); -private: - qf::qmlwidgets::framework::PartWidget *m_partWidget = nullptr; - qf::qmlwidgets::framework::DockWidget *m_registrationsDockWidget = nullptr; - qf::core::model::SqlTableModel *m_registrationsModel = nullptr; - qf::core::utils::Table m_registrationsTable; -}; - -} - -#endif // COMPETITORS_COMPETITORSPLUGIN_H diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.cpp b/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.cpp deleted file mode 100644 index a032e8353..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.cpp +++ /dev/null @@ -1,431 +0,0 @@ -#include "competitorswidget.h" -#include "ui_competitorswidget.h" -#include "competitorwidget.h" -#include "stationsbackupmemorywidget.h" - -#include "competitordocument.h" -#include "competitorsplugin.h" - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace qfs = qf::core::sql; -namespace qfw = qf::qmlwidgets; -namespace qff = qf::qmlwidgets::framework; -namespace qfd = qf::qmlwidgets::dialogs; -namespace qfc = qf::core; -namespace qfm = qf::core::model; -namespace qfu = qf::core::utils; - - -using qf::qmlwidgets::framework::getPlugin; -using Event::EventPlugin; -using Competitors::CompetitorsPlugin; - -class CompetitorsModel : public qfm::SqlTableModel -{ - using Super = qfm::SqlTableModel; -public: - CompetitorsModel(QObject *parent ) : Super(parent) {} - - bool postRow(int row_no, bool throw_exc) override - { - qfu::TableRow &row_ref = m_table.rowRef(row_no); - auto competitor_id = row_ref.value("competitors.id").toInt(); - QVariantMap dirty_rec; - if (!row_ref.isInsert()) { - Q_ASSERT(competitor_id > 0); - dirty_rec = row_ref.dirtyValuesMap(); - } - auto ret = Super::postRow(row_no, throw_exc); - if (!dirty_rec.isEmpty()) { - qf::core::sql::Query q; - q.exec(QStringLiteral("SELECT id FROM runs WHERE competitorId = %1").arg(competitor_id)); - while(q.next()) { - int run_id = q.value(0).toInt(); - getPlugin()->emitDbEvent(Event::EventPlugin::DBEVENT_RUN_CHANGED, QVariantList {run_id, dirty_rec}); - } - } - return ret; - } -}; - -CompetitorsWidget::CompetitorsWidget(QWidget *parent) : - Super(parent), - ui(new Ui::CompetitorsWidget) -{ - ui->setupUi(this); - - ui->tblCompetitorsToolBar->setTableView(ui->tblCompetitors); - - ui->tblCompetitors->setCloneRowEnabled(false); - ui->tblCompetitors->setDirtyRowsMenuSectionEnabled(false); - ui->tblCompetitors->setPersistentSettingsId("tblCompetitors"); - ui->tblCompetitors->setRowEditorMode(qfw::TableView::EditRowsMixed); - ui->tblCompetitors->setInlineEditSaveStrategy(qfw::TableView::OnEditedValueCommit); - qfm::SqlTableModel *m = new CompetitorsModel(this); - m->addColumn("id").setReadOnly(true); - m->addColumn("classes.name", tr("Class")); - m->addColumn("competitors.startNumber", tr("SN", "start number")).setToolTip(tr("Start number")); - m->addColumn("competitorName", tr("Name")); - m->addColumn("registration", tr("Reg")).setToolTip(tr("Czech registration number"));; - m->addColumn("iofId", tr("IOF ID")).setToolTip(tr("IOF ID number"));; - m->addColumn("siId", tr("SI")).setReadOnly(true).setCastType(qMetaTypeId()); - m->addColumn("ranking", tr("Ranking pos")).setToolTip(tr("Runner's position in CZ ranking.")); - m->addColumn("note", tr("Note")); - ui->tblCompetitors->setTableModel(m); - m_competitorsModel = m; - - connect(ui->tblCompetitors, &qfw::TableView::editRowInExternalEditor, this, &CompetitorsWidget::editCompetitor, Qt::QueuedConnection); - connect(ui->tblCompetitors, &qfw::TableView::editSelectedRowsInExternalEditor, this, &CompetitorsWidget::editCompetitors, Qt::QueuedConnection); - - ui->tblCompetitors->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->tblCompetitors, &qfw::TableView::customContextMenuRequested, this, &CompetitorsWidget::onCustomContextMenuRequest); - connect(getPlugin(), &Competitors::CompetitorsPlugin::editCompetitorOnPunch, this, &CompetitorsWidget::editCompetitorOnPunch); - - QMetaObject::invokeMethod(this, "lazyInit", Qt::QueuedConnection); -} - -CompetitorsWidget::~CompetitorsWidget() -{ - delete ui; -} - -void CompetitorsWidget::settleDownInPartWidget(::PartWidget *part_widget) -{ - connect(part_widget, &::PartWidget::resetPartRequest, this, &CompetitorsWidget::reset); - connect(part_widget, &::PartWidget::reloadPartRequest, this, &CompetitorsWidget::reload); - - qfw::ToolBar *main_tb = part_widget->toolBar("main", true); - { - QLabel *lbl; - { - lbl = new QLabel(tr("&Class ")); - main_tb->addWidget(lbl); - } - { - m_cbxClasses = new qfw::ForeignKeyComboBox(); -#if QT_VERSION_MAJOR >= 6 - m_cbxClasses->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); -#else - m_cbxClasses->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); -#endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) - m_cbxClasses->setMinimumWidth(fontMetrics().horizontalAdvance('X') * 15); -#else - m_cbxClasses->setMinimumWidth(fontMetrics().width('X') * 15); -#endif - m_cbxClasses->setMaxVisibleItems(100); - m_cbxClasses->setReferencedTable("classes"); - m_cbxClasses->setReferencedField("id"); - m_cbxClasses->setReferencedCaptionField("name"); - main_tb->addWidget(m_cbxClasses); - } - lbl->setBuddy(m_cbxClasses); - } - - qf::qmlwidgets::Action *act_print = part_widget->menuBar()->actionForPath("print"); - act_print->setText(tr("&Print")); - { - auto *a = new qfw::Action(tr("Competitors statistics")); - //a->setShortcut("ctrl+L"); - connect(a, &qfw::Action::triggered, this, &CompetitorsWidget::report_competitorsStatistics); - act_print->addActionInto(a); - } - - qf::qmlwidgets::Action *act_stations = part_widget->menuBar()->actionForPath("stations"); - act_stations->setText(tr("&Stations")); - { - qf::qmlwidgets::Action *a = new qf::qmlwidgets::Action("backupMemory", tr("Backup memory")); - act_stations->addActionInto(a); - connect(a, &qf::qmlwidgets::Action::triggered, [this]() { - qf::qmlwidgets::dialogs::Dialog dlg(this); - auto *w = new StationsBackupMemoryWidget(); - dlg.setCentralWidget(w); - dlg.exec(); - }); - } -} - -void CompetitorsWidget::lazyInit() -{ -} - -void CompetitorsWidget::reset() -{ - if(!getPlugin()->isEventOpen()) { - m_competitorsModel->clearRows(); - return; - } - { - m_cbxClasses->blockSignals(true); - m_cbxClasses->loadItems(true); - m_cbxClasses->insertItem(0, tr("--- all ---"), 0); - m_cbxClasses->setCurrentIndex(0); - connect(m_cbxClasses, &qf::qmlwidgets::ForeignKeyComboBox::currentDataChanged, this, &CompetitorsWidget::reload, Qt::UniqueConnection); - m_cbxClasses->blockSignals(false); - } - reload(); -} - -void CompetitorsWidget::reload() -{ - bool is_relays = getPlugin()->eventConfig()->isRelays(); - qfs::QueryBuilder qb; - qb.select2("competitors", "*") - .select2("classes", "name") - .select("COALESCE(lastName, '') || ' ' || COALESCE(firstName, '') AS competitorName") - .from("competitors") - .orderBy("competitors.id");//.limit(10); - int class_id = m_cbxClasses->currentData().toInt(); - if(class_id > 0) { - qb.where("classes.id=" + QString::number(class_id)); - } - QString join_kind = (class_id > 0)? qfs::QueryBuilder::INNER_JOIN: qfs::QueryBuilder::LEFT_JOIN; - if(is_relays) { - qb.join("competitors.id", "runs.competitorId", join_kind); - qb.join("runs.relayId", "relays.id", join_kind); - qb.join("relays.classId", "classes.id", join_kind); - } - else { - qb.join("competitors.classId", "classes.id", join_kind); - } - m_competitorsModel->setQueryBuilder(qb, false); - m_competitorsModel->reload(); -} - -void CompetitorsWidget::editCompetitor_helper(const QVariant &id, int mode, int siid) -{ - qfLogFuncFrame() << "id:" << id << "mode:" << mode; - //qf::core::sql::Transaction transaction; - if (m_editCompetitorLock) { - qfDebug() << "Another competitor dialog is opened, ignoring editOnPunch.."; - return; - } - - class EditGuard - { - public: - EditGuard(bool &lock) : m_lock(lock) { m_lock = true; } - ~EditGuard() { m_lock = false; } - private: - bool &m_lock; - }; - bool ok = false; - bool save_and_next = false; - - { - EditGuard guard(m_editCompetitorLock); - auto *w = new CompetitorWidget(); - w->setWindowTitle(tr("Edit Competitor")); - qfd::Dialog dlg(QDialogButtonBox::Save | QDialogButtonBox::Cancel, this); - dlg.setDefaultButton(QDialogButtonBox::Save); - if(mode == qf::core::model::DataDocument::ModeInsert || mode == qf::core::model::DataDocument::ModeEdit) { - QPushButton *bt_save_and_next = dlg.buttonBox()->addButton(tr("Save and &next"), QDialogButtonBox::AcceptRole); - connect(dlg.buttonBox(), &qf::qmlwidgets::DialogButtonBox::clicked, [&save_and_next, bt_save_and_next](QAbstractButton *button) { - save_and_next = (button == bt_save_and_next); - }); - } - dlg.setCentralWidget(w); - w->load(id, mode); - auto *doc = qobject_cast(w->dataController()->document()); - QF_ASSERT(doc != nullptr, "Document is null!", return); - if(mode == qfm::DataDocument::ModeInsert) { - if(siid == 0) { - bool is_relays = getPlugin()->eventConfig()->isRelays(); - if(!is_relays) { - int class_id = m_cbxClasses->currentData().toInt(); - if(class_id > 0) - doc->setValue("competitors.classId", class_id); - else - doc->setValue("competitors.classId", QVariant()); - } - } - else { - w->loadFromRegistrations(siid); - } - } - connect(doc, &Competitors::CompetitorDocument::saved, ui->tblCompetitors, &qf::qmlwidgets::TableView::rowExternallySaved, Qt::QueuedConnection); - connect(doc, &Competitors::CompetitorDocument::saved, getPlugin(), &Competitors::CompetitorsPlugin::competitorEdited, Qt::QueuedConnection); - ok = dlg.exec(); - //if(ok) - // transaction.commit(); - //else - // transaction.rollback(); - - } - if(ok && save_and_next) { - QTimer::singleShot(0, [this]() { - this->editCompetitor(QVariant(), qf::core::model::DataDocument::ModeInsert); - }); - } -} - -void CompetitorsWidget::editCompetitors(int mode) -{ - if(mode == qfm::DataDocument::ModeDelete) { - QList sel_rows = ui->tblCompetitors->selectedRowsIndexes(); - if(sel_rows.count() <= 1) - return; - if(qfd::MessageBox::askYesNo(this, tr("Really delete all the selected competitors? This action cannot be reverted."), false)) { - qfs::Transaction transaction; - int n = 0; - for(int ix : sel_rows) { - int id = ui->tblCompetitors->tableRow(ix).value(ui->tblCompetitors->idColumnName()).toInt(); - if(id > 0) { - Competitors::CompetitorDocument doc; - doc.load(id, qfm::DataDocument::ModeDelete); - doc.drop(); - n++; - } - } - if(n > 0) { - if(qfd::MessageBox::askYesNo(this, tr("Confirm deletion of %1 competitors.").arg(n), false)) { - transaction.commit(); - ui->tblCompetitors->reload(); - } - else { - transaction.rollback(); - } - } - } - } -} - -void CompetitorsWidget::editCompetitorOnPunch(int siid) -{ - qfs::Query q; - q.exec("SELECT id FROM competitors WHERE siId=" + QString::number(siid), qfc::Exception::Throw); - if(q.next()) { - int competitor_id = q.value(0).toInt(); - if(competitor_id > 0) { - editCompetitor_helper(competitor_id, qfm::DataDocument::ModeEdit, 0); - } - } - else { - editCompetitor_helper(QVariant(), qfm::DataDocument::ModeInsert, siid); - } -} - -void CompetitorsWidget::onCustomContextMenuRequest(const QPoint &pos) -{ - qfLogFuncFrame(); - QAction a_change_class(tr("Set class in selected rows"), nullptr); - QList lst; - lst << &a_change_class; - QAction *a = QMenu::exec(lst, ui->tblCompetitors->viewport()->mapToGlobal(pos)); - if(a == &a_change_class) { - qfw::dialogs::GetItemInputDialog dlg(this); - QComboBox *box = dlg.comboBox(); - qfs::Query q; - q.exec("SELECT id, name FROM classes ORDER BY name"); - while (q.next()) { - box->addItem(q.value(1).toString(), q.value(0)); - } - dlg.setWindowTitle(tr("Dialog")); - dlg.setLabelText(tr("Select class")); - dlg.setCurrentItemIndex(-1); - if(dlg.exec()) { - int class_id = dlg.currentData().toInt(); - if(class_id > 0) { - qfs::Transaction transaction; - try { - QList rows = ui->tblCompetitors->selectedRowsIndexes(); - for(int i : rows) { - qf::core::utils::TableRow row = ui->tblCompetitors->tableRowRef(i); - int competitor_id = row.value("competitors.id").toInt(); - q.exec(QString("UPDATE competitors SET classId=%1 WHERE id=%2").arg(class_id).arg(competitor_id), qfc::Exception::Throw); - } - transaction.commit(); - } - catch (std::exception &e) { - qfError() << e.what(); - } - } - ui->tblCompetitors->reload(true); - } - } -} - -void CompetitorsWidget::report_competitorsStatistics() -{ - qfLogFuncFrame(); - int stage_cnt = getPlugin()->stageCount(); - - qfs::QueryBuilder qb; - qb.select2("classes", "id, name").from("classes").orderBy("classes.name"); - qf::core::model::SqlTableModel m; - m.setQueryBuilder(qb); - m.reload(); - qfu::TreeTable tt = m.toTreeTable(); - tt.setValue("event", getPlugin()->eventConfig()->value("event")); - for (int stage_id = 1; stage_id <= stage_cnt; ++stage_id) { - QString prefix = "e" + QString::number(stage_id) + "_"; - QString col_runs_count = prefix + "runCount"; - QString col_map_count = prefix + "mapCount"; - tt.appendColumn(col_runs_count, QMetaType(QMetaType::Int)); - tt.appendColumn(col_map_count, QMetaType(QMetaType::Int)); - { - 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 - .from("classes") - .joinRestricted("classes.id", "classdefs.classid", "classdefs.stageId={{stage_id}}") - .join("classes.id", "competitors.classId") - .joinRestricted("competitors.id", "runs.competitorId", "runs.isRunning AND runs.stageId={{stage_id}}") - .groupBy("classes.name") - .orderBy("classes.name"); - QVariantMap qpm; - qpm["stage_id"] = stage_id; - qfs::Query q; - q.execThrow(qb.toString(qpm)); - int i = 0; - while(q.next()) { - qf::core::utils::TreeTableRow tt_row = tt.row(i); - tt_row.setValue(col_runs_count, q.value("runCount")); - tt_row.setValue(col_map_count, q.value("mapCount")); - tt.setRow(i, tt_row); - i++; - } - } - } - qfDebug() << tt.toString(); - QVariantMap props; - //props["isBreakAfterEachClass"] = (opts.breakType() != (int)quickevent::gui::ReportOptionsDialog::BreakType::None); - //props["isColumnBreak"] = (opts.breakType() == (int)quickevent::gui::ReportOptionsDialog::BreakType::Column); - props["stageCount"] = stage_cnt; - QString rep_fn = getPlugin()->findReportFile("competitorsStatistics.qml"); - qf::qmlwidgets::reports::ReportViewWidget::showReport(this - , rep_fn - , tt.toVariant() - , tr("Competitors statistics") - , "competitorsStatistics" - , props - ); -} diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.h b/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.h deleted file mode 100644 index 5892a5e74..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef COMPETITORSWIDGET_H -#define COMPETITORSWIDGET_H - -#include - -#include "partwidget.h" - -class QCheckBox; - -namespace Ui { -class CompetitorsWidget; -} -namespace qf { -namespace core { -namespace model { -class SqlTableModel; -} -} -namespace qmlwidgets { -class ForeignKeyComboBox; -} -} - -namespace Competitors { class ThisPartWidget; } - -class CompetitorsWidget : public QFrame -{ - Q_OBJECT -private: - typedef QFrame Super; -public: - explicit CompetitorsWidget(QWidget *parent = nullptr); - ~CompetitorsWidget() Q_DECL_OVERRIDE; - - void settleDownInPartWidget(::PartWidget *part_widget); -private: - Q_SLOT void lazyInit(); - Q_SLOT void reset(); - Q_SLOT void reload(); - - void editCompetitor(const QVariant &id, int mode) {editCompetitor_helper(id, mode, 0);} - void editCompetitors(int mode); - - void editCompetitor_helper(const QVariant &id, int mode, int siid); - Q_SLOT void editCompetitorOnPunch(int siid); - - void onCustomContextMenuRequest(const QPoint &pos); - - void report_competitorsStatistics(); -private: - bool m_editCompetitorLock = false; - Ui::CompetitorsWidget *ui; - qf::core::model::SqlTableModel *m_competitorsModel; - qf::qmlwidgets::ForeignKeyComboBox *m_cbxClasses = nullptr; -}; - -#endif // COMPETITORSWIDGET_H diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.ui b/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.ui deleted file mode 100644 index 21de44f93..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorswidget.ui +++ /dev/null @@ -1,61 +0,0 @@ - - - CompetitorsWidget - - - - 0 - 0 - 400 - 300 - - - - - 0 - 0 - - - - Form - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - - - - - - - qf::qmlwidgets::TableView - QTableView -
qf/qmlwidgets/tableview.h
-
- - qf::qmlwidgets::TableViewToolBar - QWidget -
qf/qmlwidgets/tableviewtoolbar.h
- 1 -
-
- - -
diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.cpp b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.cpp index 47a114fd8..2e9436133 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.cpp +++ b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.cpp @@ -3,6 +3,7 @@ #include "competitordocument.h" +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include namespace qfd = qf::qmlwidgets::dialogs; namespace qfw = qf::qmlwidgets; @@ -186,20 +188,8 @@ CompetitorWidget::CompetitorWidget(QWidget *parent) : ui->tblRuns->horizontalHeader()->setSectionHidden(CompetitorRunsModel::col_relays_name, !is_relays); ui->tblRuns->horizontalHeader()->setSectionHidden(CompetitorRunsModel::col_runs_leg, !is_relays); ui->tblRuns->horizontalHeader()->setSectionHidden(CompetitorRunsModel::col_classes_name, !is_relays); - //ui->tblRuns->setContextMenuPolicy(Qt::CustomContextMenu); - //connect(ui->tblRuns, &qfw::TableView::customContextMenuRequested, this, &CompetitorWidget::onRunsTableCustomContextMenuRequest); - - { - int stage_cnt = getPlugin()->stageCount(); - auto *ly = new QHBoxLayout(ui->grpStartTimes); - for (int i = 1; i <= stage_cnt; ++i) { - QPushButton *bt = new QPushButton(tr("E&%1").arg(i)); - ly->addWidget(bt); - connect(bt, &QPushButton::clicked, this, [this, i]() { - this->showRunsTable(i); - }); - } - } + ui->tblRuns->setContextMenuPolicy(Qt::CustomContextMenu); + // connect(ui->tblRuns, &qfw::TableView::customContextMenuRequested, this, &CompetitorWidget::onRunsTableCustomContextMenuRequest); // if there is only one run propagate widget SI card change from competitors to runs connect(ui->edSiId, qOverload(&QSpinBox::valueChanged), this, [this](int new_si_number) // widget SIcard edit box @@ -212,13 +202,44 @@ CompetitorWidget::CompetitorWidget(QWidget *parent) : connect(ui->tblRuns, &qfw::TableView::editCellRequest, this, [this](QModelIndex index) { auto col = index.column(); - if(col == CompetitorRunsModel::col_runs_runFlags) { + if(col == CompetitorRunsModel::col_runs_startTimeMs) { + bool is_relays = getPlugin()->eventConfig()->isRelays(); + if (!is_relays) { + saveData(); + auto row = ui->tblRuns->tableRow(); + auto run_id = row.value("id").toInt(); + Q_ASSERT(run_id > 0); + // auto competitor_id = row.value("competitorId").toInt(); + auto club_abbr = dataDocument()->value("registration").toString().mid(0, 3).toUpper(); + + auto st_times = possibleStartTimesMs(run_id); + if (st_times.isEmpty()) { + return; + } + QStringList items; + for (auto t : st_times) { + items << quickevent::core::og::TimeMs(t).toString(); + } + bool ok; + auto item = QInputDialog::getItem(this, tr("Quick Event get start time"), + tr("New start time:"), items, 0, false, &ok); + if (ok && !item.isEmpty()) { + auto ix = items.indexOf(item); + Q_ASSERT(ix >= 0); + auto stime = quickevent::core::og::TimeMs(st_times[ix]); + ui->tblRuns->model()->setData(index, QVariant::fromValue(stime)); + m_runsModel->postAll(qf::core::Exception::Throw); + } + } + } + else if(col == CompetitorRunsModel::col_runs_runFlags) { Runs::RunFlagsDialog dlg(this); dlg.load(m_runsModel, ui->tblRuns->toTableModelRowNo(ui->tblRuns->currentIndex().row())); if(dlg.exec()) { dlg.save(); } - } else if(col == CompetitorRunsModel::col_runs_cardFlags) { + } + else if(col == CompetitorRunsModel::col_runs_cardFlags) { Runs::CardFlagsDialog dlg(this); dlg.load(m_runsModel, ui->tblRuns->toTableModelRowNo(ui->tblRuns->currentIndex().row())); if(dlg.exec()) { @@ -280,17 +301,12 @@ bool CompetitorWidget::saveRunsTable() void CompetitorWidget::onRunsTableCustomContextMenuRequest(const QPoint &pos) { qfLogFuncFrame(); - QAction a_show_in_runs(tr("Show in runs table"), nullptr); + QAction a_set_start_time(tr("Set start time"), nullptr); QList lst; - lst << &a_show_in_runs; + lst << &a_set_start_time; QAction *a = QMenu::exec(lst, ui->tblRuns->viewport()->mapToGlobal(pos)); - if(a == &a_show_in_runs) { - auto row = ui->tblRuns->tableRow(); - int stage_no = row.value("stageId").toInt(); - int class_id = row.value("classId").toInt(); - int competitor_id = row.value("competitorId").toInt(); - //QMetaObject::invokeMethod(this, "accept", Qt::QueuedConnection); - emit editStartListRequest(stage_no, class_id, competitor_id); + if(a == &a_set_start_time) { + } } */ @@ -354,19 +370,114 @@ QString CompetitorWidget::guessClassFromRegistration(const QString ®istration return candidate ? gender + QString::number(candidate) : QString(); } -void CompetitorWidget::showRunsTable(int stage_id) +QList CompetitorWidget::possibleStartTimesMs(int run_id) { - if(!saveData()) - return; - - qf::core::model::DataDocument*doc = dataController()->document(); - int competitor_id = doc->value("competitors.id").toInt(); - int class_id = ui->cbxClass->currentData().toInt(); - QString sort_col = QStringLiteral("runs.startTimeMs"); - getPlugin()->showRunsTable(stage_id, class_id, false, sort_col, competitor_id); - loadRunsTable(); + int class_id; + int stage_id; + int start_time; + QString club_abbr; + { + qf::core::sql::QueryBuilder qb; + qb.select2("runs", "competitorId, stageId, startTimeMs") + .select2("competitors", "classId, registration") + .from("runs") + .joinRestricted("runs.competitorId", "competitors.id", "runs.id=" QF_IARG(run_id), qf::core::sql::QueryBuilder::INNER_JOIN) + .where("runs.isRunning"); + qfs::Query q; + q.exec(qb.toString(), qf::core::Exception::Throw); + if (q.next()) { + class_id = q.value("classId").toInt(); + stage_id = q.value("stageId").toInt(); + start_time = q.value("startTimeMs").toInt(); + club_abbr = q.value("registration").toString().mid(0, 3).toUpper(); + } + else { + return {}; + } + } + + Classes::ClassDef class_def; + class_def.load(class_id, stage_id, false); + + if (class_def.classInterval == 0) { + return {}; + } + + struct S { + QString club; + int start_time = 0; + + bool operator<(const S &o) const { return start_time < o.start_time; } + }; + QList runs; + { + qf::core::sql::QueryBuilder qb; + qb.select2("runs", "startTimeMs") + .select2("competitors", "registration") + .from("runs") + .joinRestricted("runs.competitorId", "competitors.id", "competitors.classId=" QF_IARG(class_id), qf::core::sql::QueryBuilder::INNER_JOIN) + .where("runs.stageId=" QF_IARG(stage_id)) + .where("runs.isRunning") + .orderBy("runs.startTimeMs"); + //qfInfo() << qb.toString(); + qfs::Query q; + q.exec(qb.toString(), qf::core::Exception::Throw); + while (q.next()) { + //auto start_time = q.value("startTimeMs").toInt(); + //if (start_time < class_def.classStartFirst) { + // continue; + //} + runs << S { + .club = q.value("registration").toString().mid(0, 3).toUpper(), + .start_time = q.value("startTimeMs").toInt() + }; + } + std::sort(runs.begin(), runs.end()); + } + QList ret; + auto start_interval_ms = class_def.classInterval * 60 * 1000; + for (auto stime = class_def.classStartFirst; stime <= class_def.classStartLast; stime += class_def.classInterval) { + S r { .club = {}, .start_time = stime }; + auto [lo, up] = std::equal_range(runs.begin(), runs.end(), r); + if (lo == up || stime == start_time) { + // stime does not exist or it is my current start_time, should be inserted before up + // check next club + qfInfo() << "free stime:" << quickevent::core::og::TimeMs(stime).toString(); + if (up != runs.end()) { + qfInfo() << "next club:" << up->club; + if (up->club == club_abbr && (up->start_time - stime) <= start_interval_ms) { + qfInfo() << "same next club:" << club_abbr; + continue; + } + } + // check prev club + if (up != runs.begin()) { + up--; + qfInfo() << "prev club:" << up->club; + if (up->club == club_abbr && (stime - up->start_time) <= start_interval_ms) { + qfInfo() << "same prev club:" << club_abbr; + continue; + } + } + ret << stime; + } + } + return ret; } +// void CompetitorWidget::showRunsTable(int stage_id) +// { +// if(!saveData()) +// return; + +// qf::core::model::DataDocument*doc = dataController()->document(); +// int competitor_id = doc->value("competitors.id").toInt(); +// int class_id = ui->cbxClass->currentData().toInt(); +// QString sort_col = QStringLiteral("runs.startTimeMs"); +// getPlugin()->showRunsTable(stage_id, class_id, false, sort_col, competitor_id); +// loadRunsTable(); +// } + void CompetitorWidget::onRegistrationSelected(const QVariantMap &values) { qfLogFuncFrame(); @@ -406,11 +517,17 @@ void CompetitorWidget::loadFromRegistrations(int siid) } } +void CompetitorWidget::save() +{ + saveData(); + loadRunsTable(); +} + bool CompetitorWidget::saveData() { try { bool is_relays = getPlugin()->eventConfig()->isRelays(); - Competitors::CompetitorDocument*doc = qobject_cast(dataController()->document()); + auto *doc = qobject_cast(dataController()->document()); if(!is_relays && doc->value(QStringLiteral("classId")).toInt() == 0) { qf::qmlwidgets::dialogs::MessageBox::showWarning(this, tr("Class should be entered.")); return false; diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.h b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.h index dcb0a7b21..9d06b6482 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.h +++ b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.h @@ -1,5 +1,4 @@ -#ifndef COMPETITORWIDGET_H -#define COMPETITORWIDGET_H +#pragma once #include @@ -18,27 +17,25 @@ class CompetitorWidget : public qf::qmlwidgets::framework::DataDialogWidget typedef qf::qmlwidgets::framework::DataDialogWidget Super; public: explicit CompetitorWidget(QWidget *parent = nullptr); - ~CompetitorWidget() Q_DECL_OVERRIDE; + ~CompetitorWidget() override; - bool load(const QVariant &id = QVariant(), int mode = qf::core::model::DataDocument::ModeEdit) Q_DECL_OVERRIDE; + bool load(const QVariant &id = QVariant(), int mode = qf::core::model::DataDocument::ModeEdit) override; void loadFromRegistrations(int siid); - //Q_SIGNAL void editStartListRequest(int stage_id, int class_id, int competitor_id); - + void save(); private slots: void onRegistrationSelected(const QVariantMap &values); void onSwitchNames(); private: - Q_SLOT bool loadRunsTable(); - Q_SLOT bool saveRunsTable(); - //void onRunsTableCustomContextMenuRequest(const QPoint &pos); - bool saveData() Q_DECL_OVERRIDE; + bool loadRunsTable(); + bool saveRunsTable(); + // void onRunsTableCustomContextMenuRequest(const QPoint &pos); + bool saveData() override; QString guessClassFromRegistration(const QString ®istration); - void showRunsTable(int stage_id); + static QList possibleStartTimesMs(int run_id); private: Ui::CompetitorWidget *ui; quickevent::core::og::SqlTableModel *m_runsModel; }; -#endif // COMPETITORWIDGET_H diff --git a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui index 6df5be0a5..7f356a3be 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui +++ b/quickevent/app/quickevent/plugins/Competitors/src/competitorwidget.ui @@ -105,7 +105,7 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter competitors.siId @@ -196,7 +196,7 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter 999999999 @@ -249,7 +249,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -264,13 +264,6 @@ - - - - Start times - - - @@ -330,7 +323,7 @@ FindRegistrationEdit QLineEdit -
plugins/Competitors/src/findregistrationedit.h
+
plugins/Event/src/findregistrationedit.h
qf::qmlwidgets::SpinBox diff --git a/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.cpp b/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.cpp deleted file mode 100644 index 3bbbd1390..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "stationsbackupmemorywidget.h" -#include "ui_stationsbackupmemorywidget.h" - -#include - -namespace qfc = qf::core; -namespace qfw = qf::qmlwidgets; -namespace qfd = qf::qmlwidgets::dialogs; -namespace qfm = qf::core::model; -namespace qfs = qf::core::sql; - -StationsBackupMemoryWidget::StationsBackupMemoryWidget(QWidget *parent) - : Super(parent) - , ui(new Ui::StationsBackupMemoryWidget) -{ - setTitle(tr("Stations backup memory")); - setPersistentSettingsId("StationsBackupMemoryWidget"); - ui->setupUi(this); - ui->tblCardsTB->setTableView(ui->tblCards); - { - ui->tblCards->setPersistentSettingsId("tableView"); - qfm::SqlTableModel *m = new qfm::SqlTableModel(this); - m->addColumn("stageId", tr("Stage")); - m->addColumn("stationNumber", tr("Station number")); - m->addColumn("siId", tr("SI")); - m->addColumn("punchDateTime", tr("Punch time")); - m->addColumn("cardErr", tr("Card error")); - ui->tblCards->setTableModel(m); - m_tableModel = m; - } - { - qfs::QueryBuilder qb; - qb.select2("stationsbackup", "*") - .from("stationsbackup") - .orderBy("stageId, punchDateTime");//.limit(10); - m_tableModel->setQueryBuilder(qb, false); - m_tableModel->reload(); - } -} - -StationsBackupMemoryWidget::~StationsBackupMemoryWidget() -{ - delete ui; -} diff --git a/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.h b/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.h deleted file mode 100644 index 65273419b..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -namespace Ui { -class StationsBackupMemoryWidget; -} - -namespace qf { -namespace core { namespace model { class SqlTableModel; } } -} - -class StationsBackupMemoryWidget : public qf::qmlwidgets::framework::DialogWidget -{ - Q_OBJECT -private: - using Super = qf::qmlwidgets::framework::DialogWidget; - -public: - explicit StationsBackupMemoryWidget(QWidget *parent = nullptr); - ~StationsBackupMemoryWidget() Q_DECL_OVERRIDE; - -private: - Ui::StationsBackupMemoryWidget *ui; - qf::core::model::SqlTableModel *m_tableModel; -}; - diff --git a/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.ui b/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.ui deleted file mode 100644 index bbc51f557..000000000 --- a/quickevent/app/quickevent/plugins/Competitors/src/stationsbackupmemorywidget.ui +++ /dev/null @@ -1,55 +0,0 @@ - - - StationsBackupMemoryWidget - - - - 0 - 0 - 400 - 300 - - - - Form - - - - 1 - - - 1 - - - 1 - - - 1 - - - 1 - - - - - - - - - - - - qf::qmlwidgets::TableView - QTableView -
qf/qmlwidgets/tableview.h
-
- - qf::qmlwidgets::TableViewToolBar - QWidget -
qf/qmlwidgets/tableviewtoolbar.h
- 1 -
-
- - -
diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp index 972065783..9b092989b 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.cpp @@ -3,6 +3,8 @@ #include "connectionsettings.h" #include "eventdialogwidget.h" #include "dbschema.h" +#include "registrationswidget.h" +#include "lentcardssettingspage.h" #include "stagewidget.h" #include "stagedocument.h" #include "../../Core/src/widgets/appstatusbar.h" @@ -11,6 +13,10 @@ #include "services/emmaclient.h" #include "services/qx/qxclientservice.h" +#include +#include +#include + #include #include @@ -32,7 +38,6 @@ #include #include #include -#include #include #include @@ -60,6 +65,8 @@ namespace qff = qf::qmlwidgets::framework; namespace qfd = qf::qmlwidgets::dialogs; namespace qfs = qf::core::sql; +using qff::getPlugin; + namespace Event { class DbEventPayload : public QVariantMap @@ -380,6 +387,25 @@ void EventPlugin::onInstalled() //a->setShortcut(QKeySequence("ctrl+shift+R")); fwk->menuBar()->actionForPath("view")->addActionInto(a); } + { + connect(this, &Event::EventPlugin::eventOpenChanged, this, &EventPlugin::reloadRegistrationsModel); + + { + m_registrationsDockWidget = new qff::DockWidget(nullptr); + m_registrationsDockWidget->setObjectName("registrationsDockWidget"); + m_registrationsDockWidget->setWindowTitle(tr("Registrations")); + fwk->addDockWidget(Qt::RightDockWidgetArea, m_registrationsDockWidget); + m_registrationsDockWidget->hide(); + connect(m_registrationsDockWidget, &qff::DockWidget::visibilityChanged, this, &EventPlugin::onRegistrationsDockVisibleChanged); + + auto *a = m_registrationsDockWidget->toggleViewAction(); + //a->setCheckable(true); + a->setShortcut(QKeySequence("ctrl+shift+R")); + fwk->menuBar()->actionForPath("view")->addActionInto(a); + } + auto core_plugin = qf::qmlwidgets::framework::getPlugin(); + core_plugin->settingsDialog()->addPage(new LentCardsSettingsPage()); + } } void EventPlugin::updateWindowTitle() @@ -558,16 +584,14 @@ void EventPlugin::onDbEvent(const QString &name, QSqlDriver::NotificationSource } } -void EventPlugin::onDbEventNotify(const QString &domain, int connection_id, const QVariant &data) +void EventPlugin::onRegistrationsDockVisibleChanged(bool on) { - Q_UNUSED(connection_id) - Q_UNUSED(data) - if(domain == QLatin1String(Event::EventPlugin::DBEVENT_STAGE_START_CHANGED)) { - //int stage_id = data.toInt(); - clearStageDataCache(); + if(on && !m_registrationsDockWidget->widget()) { + auto *rw = new RegistrationsWidget(); + m_registrationsDockWidget->setWidget(rw); + rw->checkModel(); } } - void EventPlugin::repairStageStarts(const qf::core::sql::Connection &from_conn, const qf::core::sql::Connection &to_conn) { qfs::Query to_q(to_conn); @@ -1307,5 +1331,69 @@ void EventPlugin::onServiceDockVisibleChanged(bool on) } } +void EventPlugin::onDbEventNotify(const QString &domain, int connection_id, const QVariant &data) +{ + Q_UNUSED(connection_id) + qfLogFuncFrame() << "domain:" << domain << "payload:" << data; + if(domain == QLatin1String(Event::EventPlugin::DBEVENT_STAGE_START_CHANGED)) { + //int stage_id = data.toInt(); + clearStageDataCache(); + } + else if(domain == QLatin1String(Event::EventPlugin::DBEVENT_REGISTRATIONS_IMPORTED)) { + reloadRegistrationsModel(); + } + emit dbEventNotify(domain, connection_id, data); +} + +void EventPlugin::reloadRegistrationsModel() +{ + qfLogFuncFrame() << "isEventOpen():" << getPlugin()->isEventOpen(); + if(getPlugin()->isEventOpen()) + registrationsModel()->reload(); + else + registrationsModel()->clearRows(); + // clear registration table to be regenerated when registrationsTable() will be called + m_registrationsTable = qf::core::utils::Table(); +} + +qf::core::model::SqlTableModel* EventPlugin::registrationsModel() +{ + if(!m_registrationsModel) { + m_registrationsModel = new qf::core::model::SqlTableModel(this); + m_registrationsModel->addColumn("competitorName", tr("Name")); + m_registrationsModel->addColumn("registration", tr("Reg")); + m_registrationsModel->addColumn("licence", tr("Lic")); + m_registrationsModel->addColumn("siId", tr("SI")); + //m_registrationsModel->addColumn("fistName"); + //m_registrationsModel->addColumn("lastName"); + qfs::QueryBuilder qb; + qb.select2("registrations", "firstName, lastName, licence, registration, siId") + .select("COALESCE(lastName, '') || ' ' || COALESCE(firstName, '') AS competitorName") + .from("registrations") + .orderBy("lastName, firstName"); + m_registrationsModel->setQueryBuilder(qb, false); + } + return m_registrationsModel; +} + +const qf::core::utils::Table &EventPlugin::registrationsTable() +{ + qf::core::model::SqlTableModel *m = registrationsModel(); + if(m_registrationsTable.isNull() && !m->table().isNull()) { + m_registrationsTable = m->table(); + auto c_nsk = QStringLiteral("competitorNameAscii7"); + m_registrationsTable.appendColumn(c_nsk, QMetaType::QString); + int ix_nsk = m_registrationsTable.fields().fieldIndex(c_nsk); + int ix_cname = m_registrationsTable.fields().fieldIndex(QStringLiteral("competitorName")); + for (int i = 0; i < m_registrationsTable.rowCount(); ++i) { + qf::core::utils::TableRow &row_ref = m_registrationsTable.rowRef(i); + QString nsk = row_ref.value(ix_cname).toString(); + nsk = QString::fromLatin1(qf::core::Collator::toAscii7(QLocale::Czech, nsk, true)); + row_ref.setValue(ix_nsk, nsk); + } + } + return m_registrationsTable; +} + } diff --git a/quickevent/app/quickevent/plugins/Event/src/eventplugin.h b/quickevent/app/quickevent/plugins/Event/src/eventplugin.h index cd4cdc5d5..f543e1f4e 100644 --- a/quickevent/app/quickevent/plugins/Event/src/eventplugin.h +++ b/quickevent/app/quickevent/plugins/Event/src/eventplugin.h @@ -4,15 +4,18 @@ #include "eventconfig.h" #include "stage.h" -#include #include +#include +#include + #include #include -namespace qf { namespace core { namespace sql { class Query; class Connection; }}} -namespace qf { namespace qmlwidgets { class Action; } } -namespace qf { namespace qmlwidgets { namespace framework { class DockWidget; }}} +namespace qf::core::sql { class Query; class Connection; } +namespace qf::qmlwidgets { class Action; } +namespace qf::qmlwidgets::framework { class DockWidget; } +namespace qf::core::model { class SqlTableModel; } class QComboBox; class DbSchema; @@ -108,6 +111,10 @@ class EventPlugin : public qf::qmlwidgets::framework::Plugin static int dbVersion(); Q_SLOT void onInstalled(); + + qf::core::model::SqlTableModel* registrationsModel(); + const qf::core::utils::Table& registrationsTable(); + public: ConnectionType connectionType() const; bool isSingleUser() const; @@ -127,7 +134,10 @@ class EventPlugin : public qf::qmlwidgets::framework::Plugin void onDbEventNotify(const QString &domain, int connection_id, const QVariant &data); + void onRegistrationsDockVisibleChanged(bool on = true); + void updateWindowTitle(); + void reloadRegistrationsModel(); //bool runSqlScript(qf::core::sql::Query &q, const QStringList &sql_lines); void repairStageStarts(const qf::core::sql::Connection &from_conn, const qf::core::sql::Connection &to_conn); @@ -151,6 +161,10 @@ class EventPlugin : public qf::qmlwidgets::framework::Plugin QMap m_classNameCache; qf::qmlwidgets::framework::DockWidget *m_servicesDockWidget = nullptr; + qf::qmlwidgets::framework::DockWidget *m_registrationsDockWidget = nullptr; + + qf::core::model::SqlTableModel *m_registrationsModel = nullptr; + qf::core::utils::Table m_registrationsTable; }; } diff --git a/quickevent/app/quickevent/plugins/Competitors/src/findregistrationedit.cpp b/quickevent/app/quickevent/plugins/Event/src/findregistrationedit.cpp similarity index 90% rename from quickevent/app/quickevent/plugins/Competitors/src/findregistrationedit.cpp rename to quickevent/app/quickevent/plugins/Event/src/findregistrationedit.cpp index c759dc1ac..bda32a23e 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/findregistrationedit.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/findregistrationedit.cpp @@ -1,6 +1,6 @@ #include "findregistrationedit.h" -#include "competitorsplugin.h" +#include "eventplugin.h" #include #include @@ -14,7 +14,7 @@ #include using qf::qmlwidgets::framework::getPlugin; -using Competitors::CompetitorsPlugin; +using Event::EventPlugin; class FindRegistrationsModel : public QAbstractTableModel { @@ -34,7 +34,7 @@ class FindRegistrationsModel : public QAbstractTableModel FindRegistrationsModel::FindRegistrationsModel(QObject *parent) : Super(parent) { - m_registrationsTable = getPlugin()->registrationsTable(); + m_registrationsTable = getPlugin()->registrationsTable(); } const qf::core::utils::Table &FindRegistrationsModel::registrationsTable() const @@ -66,7 +66,7 @@ QVariant FindRegistrationsModel::data(const QModelIndex &index, int role) const + table_row.value(col_registration).toString() + ' ' + SI + table_row.value(col_siid).toString(); } - else if(role == FindRegistrationEdit::CompletionRole) { + if(role == FindRegistrationEdit::CompletionRole) { return table_row.value(col_searchkey).toString() + ' ' + table_row.value(col_registration).toString() + ' ' + SI + table_row.value(col_siid).toString() + ' ' @@ -80,7 +80,7 @@ FindRegistrationEdit::FindRegistrationEdit(QWidget *parent) : Super(parent) { m_findRegistrationsModel = new FindRegistrationsModel(this); - QCompleter *cmpl = new QCompleter(m_findRegistrationsModel, this); + auto *cmpl = new QCompleter(m_findRegistrationsModel, this); cmpl->setCompletionRole(CompletionRole); cmpl->setCaseSensitivity(Qt::CaseInsensitive); cmpl->setFilterMode(Qt::MatchContains); @@ -100,9 +100,9 @@ void FindRegistrationEdit::focusInEvent(QFocusEvent *event) void FindRegistrationEdit::onCompleterActivated(const QModelIndex &index) { - qfLogFuncFrame() << index << index.data(); + // qfLogFuncFrame() << index << index.data(); auto *proxy_model = qobject_cast(completer()->completionModel()); - QF_ASSERT(proxy_model != nullptr, "Bad proxy model!", return); + Q_ASSERT(proxy_model != nullptr); QModelIndex ix = proxy_model->mapToSource(index); const qf::core::utils::Table &table = m_findRegistrationsModel->registrationsTable(); int row_no = ix.row(); diff --git a/quickevent/app/quickevent/plugins/Competitors/src/findregistrationedit.h b/quickevent/app/quickevent/plugins/Event/src/findregistrationedit.h similarity index 100% rename from quickevent/app/quickevent/plugins/Competitors/src/findregistrationedit.h rename to quickevent/app/quickevent/plugins/Event/src/findregistrationedit.h diff --git a/quickevent/app/quickevent/plugins/Competitors/src/lentcardssettingspage.cpp b/quickevent/app/quickevent/plugins/Event/src/lentcardssettingspage.cpp similarity index 90% rename from quickevent/app/quickevent/plugins/Competitors/src/lentcardssettingspage.cpp rename to quickevent/app/quickevent/plugins/Event/src/lentcardssettingspage.cpp index 747fec020..22b30f75a 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/lentcardssettingspage.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/lentcardssettingspage.cpp @@ -3,9 +3,6 @@ #include -namespace qfc = qf::core; -namespace qfw = qf::qmlwidgets; -namespace qfd = qf::qmlwidgets::dialogs; namespace qfm = qf::core::model; namespace qfs = qf::core::sql; diff --git a/quickevent/app/quickevent/plugins/Competitors/src/lentcardssettingspage.h b/quickevent/app/quickevent/plugins/Event/src/lentcardssettingspage.h similarity index 100% rename from quickevent/app/quickevent/plugins/Competitors/src/lentcardssettingspage.h rename to quickevent/app/quickevent/plugins/Event/src/lentcardssettingspage.h diff --git a/quickevent/app/quickevent/plugins/Competitors/src/lentcardssettingspage.ui b/quickevent/app/quickevent/plugins/Event/src/lentcardssettingspage.ui similarity index 100% rename from quickevent/app/quickevent/plugins/Competitors/src/lentcardssettingspage.ui rename to quickevent/app/quickevent/plugins/Event/src/lentcardssettingspage.ui diff --git a/quickevent/app/quickevent/plugins/Competitors/src/registrationswidget.cpp b/quickevent/app/quickevent/plugins/Event/src/registrationswidget.cpp similarity index 83% rename from quickevent/app/quickevent/plugins/Competitors/src/registrationswidget.cpp rename to quickevent/app/quickevent/plugins/Event/src/registrationswidget.cpp index 80c0af0b3..b9b5b8264 100644 --- a/quickevent/app/quickevent/plugins/Competitors/src/registrationswidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/registrationswidget.cpp @@ -1,6 +1,6 @@ #include "registrationswidget.h" #include "ui_registrationswidget.h" -#include "competitorsplugin.h" +#include "eventplugin.h" #include @@ -9,10 +9,8 @@ #include #include -namespace qfm = qf::core::model; -namespace qfs = qf::core::sql; using qf::qmlwidgets::framework::getPlugin; -using Competitors::CompetitorsPlugin; +using Event::EventPlugin; RegistrationsWidget::RegistrationsWidget(QWidget *parent) : QWidget(parent), @@ -39,9 +37,9 @@ void RegistrationsWidget::checkModel() return; if(!ui->tblRegistrations->tableModel()) { - qf::core::model::SqlTableModel *reg_model = getPlugin()->registrationsModel(); + qf::core::model::SqlTableModel *reg_model = getPlugin()->registrationsModel(); ui->tblRegistrations->setTableModel(reg_model); - connect(reg_model, &qf::core::model::SqlTableModel::reloaded, [this]() { + connect(reg_model, &qf::core::model::SqlTableModel::reloaded, this, [this]() { ui->tblRegistrations->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); }); } diff --git a/quickevent/app/quickevent/plugins/Competitors/src/registrationswidget.h b/quickevent/app/quickevent/plugins/Event/src/registrationswidget.h similarity index 100% rename from quickevent/app/quickevent/plugins/Competitors/src/registrationswidget.h rename to quickevent/app/quickevent/plugins/Event/src/registrationswidget.h diff --git a/quickevent/app/quickevent/plugins/Competitors/src/registrationswidget.ui b/quickevent/app/quickevent/plugins/Event/src/registrationswidget.ui similarity index 100% rename from quickevent/app/quickevent/plugins/Competitors/src/registrationswidget.ui rename to quickevent/app/quickevent/plugins/Event/src/registrationswidget.ui 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 fb575be52..ddad83208 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxclientservice.cpp @@ -284,6 +284,7 @@ QByteArray QxClientService::zlibCompress(QByteArray data) void QxClientService::connectToSSE(int event_id) { + Q_UNUSED(event_id); // auto url = exchangeServerUrl(); // url.setPath(QStringLiteral("/api/event/%1/run/changes/sse").arg(event_id)); // QNetworkRequest request(url); 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 449e2c5f1..a22449a4e 100644 --- a/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp +++ b/quickevent/app/quickevent/plugins/Event/src/services/qx/qxlateregistrationswidget.cpp @@ -4,7 +4,6 @@ #include "qxclientservice.h" #include -#include #include #include diff --git a/quickevent/app/quickevent/plugins/Oris/src/orisimporter.cpp b/quickevent/app/quickevent/plugins/Oris/src/orisimporter.cpp index f41e0a3a1..0090b3784 100644 --- a/quickevent/app/quickevent/plugins/Oris/src/orisimporter.cpp +++ b/quickevent/app/quickevent/plugins/Oris/src/orisimporter.cpp @@ -611,8 +611,8 @@ void OrisImporter::syncEventEntries(int event_id, std::function success doc->setProperty(KEY_ORIG_RUNS, orig_runs); items_processed++; } - for(int id : imported_competitors.values()) { - Competitors::CompetitorDocument *doc = new Competitors::CompetitorDocument(); + for(const auto &[import_id, id] : imported_competitors.asKeyValueRange()) { + auto *doc = new Competitors::CompetitorDocument(); doc_lst << doc; doc->load(id, doc->ModeDelete); } diff --git a/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.cpp b/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.cpp index 0a6bf3634..5ccf0a8b7 100644 --- a/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.cpp +++ b/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.cpp @@ -9,13 +9,15 @@ #include #include #include -#include +#include +// #include #include #include using qf::qmlwidgets::framework::getPlugin; -using Competitors::CompetitorsPlugin; +// using Competitors::CompetitorsPlugin; +using Event::EventPlugin; AddLegDialogWidget::AddLegDialogWidget(QWidget *parent) : Super(parent) @@ -29,7 +31,7 @@ AddLegDialogWidget::AddLegDialogWidget(QWidget *parent) m_defaultStatusText = ui->lblStatus->text(); - qf::core::model::SqlTableModel *competitors_model = new qf::core::model::SqlTableModel(this); + auto *competitors_model = new qf::core::model::SqlTableModel(this); //competitors_model->addColumn("relays.club", tr("Club")); competitors_model->addColumn("relayName", tr("Name")); competitors_model->addColumn("runs.leg", tr("Leg")); @@ -55,7 +57,7 @@ AddLegDialogWidget::AddLegDialogWidget(QWidget *parent) ui->tblCompetitors->setReadOnly(true); competitors_model->reload(); - auto *reg_model = getPlugin()->registrationsModel(); + auto *reg_model = getPlugin()->registrationsModel(); ui->tblRegistrations->setTableModel(reg_model); ui->tblRegistrations->setReadOnly(true); connect(reg_model, &qf::core::model::SqlTableModel::reloaded, this, [this]() { @@ -165,7 +167,7 @@ void AddLegDialogWidget::updateLegAddedStatus(const QString &msg) m_updateStatusTimer->start(); } -int AddLegDialogWidget::findFreeLeg() +int AddLegDialogWidget::findFreeLeg() const { qf::core::sql::Query q; q.exec("SELECT leg FROM runs WHERE leg IS NOT NULL AND relayId=" + QString::number(relayId()) + " ORDER BY leg", qf::core::Exception::Throw); diff --git a/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.h b/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.h index c1562bc8f..b96249f89 100644 --- a/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.h +++ b/quickevent/app/quickevent/plugins/Relays/src/addlegdialogwidget.h @@ -30,7 +30,7 @@ class AddLegDialogWidget : public qf::qmlwidgets::framework::DialogWidget void updateLegAddedStatus(const QString &msg); - int findFreeLeg(); + int findFreeLeg() const; private: Ui::AddLegDialogWidget *ui; QTimer *m_updateStatusTimer = nullptr; diff --git a/quickevent/app/quickevent/plugins/Competitors/qml/reports/competitorsStatistics.qml b/quickevent/app/quickevent/plugins/Runs/qml/reports/competitorsStatistics.qml similarity index 100% rename from quickevent/app/quickevent/plugins/Competitors/qml/reports/competitorsStatistics.qml rename to quickevent/app/quickevent/plugins/Runs/qml/reports/competitorsStatistics.qml diff --git a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp index 5d0607a6a..01fa69ed8 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.cpp @@ -8,8 +8,8 @@ #include "printawardsoptionsdialogwidget.h" #include "services/resultsexporter.h" #include "partwidget.h" +// #include "../../Competitors/src/competitorwidget.h" #include "../../CardReader/src/cardreaderplugin.h" -#include "../../Competitors/src/competitorsplugin.h" #include "../../Event/src/eventplugin.h" #include "../../Event/src/services/qx/qxlateregistrationswidget.h" @@ -43,14 +43,13 @@ #include #include -namespace qfw = qf::qmlwidgets; namespace qff = qf::qmlwidgets::framework; namespace qfu = qf::core::utils; namespace qfs = qf::core::sql; using ::PartWidget; using qff::getPlugin; using Event::EventPlugin; -using Competitors::CompetitorsPlugin; +// using Competitors::CompetitorsPlugin; using CardReader::CardReaderPlugin; namespace Runs { @@ -66,9 +65,7 @@ RunsPlugin::RunsPlugin(QObject *parent) connect(this, &RunsPlugin::installed, this, &RunsPlugin::onInstalled); } -RunsPlugin::~RunsPlugin() -{ -} +RunsPlugin::~RunsPlugin() = default; const qf::core::utils::Table &RunsPlugin::runnersTable(int stage_id) { @@ -160,7 +157,7 @@ void RunsPlugin::onInstalled() m_qxLateRegistrationsDockWidget = dw; } - services::ResultsExporter *results_exporter = new services::ResultsExporter(this); + auto *results_exporter = new services::ResultsExporter(this); Event::services::Service::addService(results_exporter); } @@ -362,7 +359,7 @@ QWidget* RunsPlugin::createReportOptionsDialog(QWidget *parent) qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork(); parent = fwk; } - quickevent::gui::ReportOptionsDialog *ret = new quickevent::gui::ReportOptionsDialog(parent); + auto *ret = new quickevent::gui::ReportOptionsDialog(parent); ret->loadPersistentSettings(); return ret; } @@ -866,7 +863,7 @@ QVariantMap RunsPlugin::printAwardsOptionsWithDialog(const QVariantMap &opts) { qfInfo() << Q_FUNC_INFO; QVariantMap ret; - PrintAwardsOptionsDialogWidget *w = new PrintAwardsOptionsDialogWidget(); + auto *w = new PrintAwardsOptionsDialogWidget(); w->setPrintOptions(opts); qf::qmlwidgets::dialogs::Dialog dlg(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, m_partWidget); dlg.setCentralWidget(w); @@ -1219,7 +1216,7 @@ qf::core::sql::QueryBuilder RunsPlugin::runsQuery(int stage_id, int class_id, bo qfs::QueryBuilder qb; qb.select2("runs", "*") .select2("classes", "name") - .select2("competitors", "id, registration, licence, ranking, siId, note") + .select2("competitors", "id, iofId, registration, licence, ranking, siId, note") .select("COALESCE(lastName, '') || ' ' || COALESCE(firstName, '') AS competitorName") .select("lentcards.siid IS NOT NULL AS cardInLentTable") @@ -1668,6 +1665,18 @@ qf::core::utils::TreeTable RunsPlugin::startListClubsNStagesTable(const int stag return tt; } +// int RunsPlugin::editCompetitor(int id, int mode) +// { +// qfLogFuncFrame() << "id:" << id; +// auto *w = new CompetitorWidget(); +// w->setWindowTitle(tr("Edit Competitor")); +// qfd::Dialog dlg(QDialogButtonBox::Save | QDialogButtonBox::Cancel, m_partWidget); +// dlg.setDefaultButton(QDialogButtonBox::Save); +// dlg.setCentralWidget(w); +// w->load(id, (qfm::DataDocument::RecordEditMode)mode); +// return dlg.exec(); +// } + void RunsPlugin::report_startListClasses() { qff::MainWindow *fwk = qff::MainWindow::frameWork(); diff --git a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h index 0da81b4b0..6948886e3 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h +++ b/quickevent/app/quickevent/plugins/Runs/src/runsplugin.h @@ -78,7 +78,7 @@ 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); + 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); @@ -97,6 +97,9 @@ class RunsPlugin : public qf::qmlwidgets::framework::Plugin qf::core::utils::TreeTable startListStartersTable(const QString &where_expr); qf::core::utils::TreeTable startListClassesNStagesTable(const int stages_count, const QString &where_expr, const quickevent::gui::ReportOptionsDialog::StartTimeFormat start_time_format); qf::core::utils::TreeTable startListClubsNStagesTable(const int stages_count, const quickevent::gui::ReportOptionsDialog::StartTimeFormat start_time_format); + + void editCompetitorOnPunch(int siid) { emit editCompetitorOnPunchRequest(siid); } + Q_SIGNAL void editCompetitorOnPunchRequest(int siid); public: void report_startListClasses(); void report_startListClubs(); diff --git a/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.cpp b/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.cpp index 1ec7047b1..c685aae2c 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.cpp @@ -34,26 +34,8 @@ void RunsTableItemDelegate::setHighlightedClassId(int class_id, int stage_id) void RunsTableItemDelegate::reloadHighlightedClassId() { - qf::core::sql::QueryBuilder qb; - qb.select2("classdefs", "startTimeMin, lastStartTimeMin, startIntervalMin, vacantsBefore, vacantEvery, vacantsAfter") - .from("classdefs") - .where("classId=" QF_IARG(m_highlightedClassId)); bool is_relays = getPlugin()->eventConfig()->isRelays(); - if(!is_relays) - qb.where("stageId=" QF_IARG(m_stageId)); - qfs::Query q(qfs::Connection::forName()); - //qfInfo() << qb.toString(); - q.exec(qb.toString(), qf::core::Exception::Throw); - if(q.next()) { - m_classInterval = q.value("startIntervalMin").toInt() * 60 * 1000; - m_classStartFirst = q.value("startTimeMin").toInt() * 60 * 1000; - m_classStartLast = q.value("lastStartTimeMin").toInt() * 60 * 1000; - } - else { - m_classInterval = 0; - m_classStartFirst = 0; - m_classStartLast = 0; - } + m_classDef.load(m_highlightedClassId, m_stageId, is_relays); //qfInfo() << "interval:" << m_classInterval << "first:" << m_classStartFirst << "last:" << m_classStartLast; } @@ -80,7 +62,7 @@ void RunsTableItemDelegate::paintBackground(QPainter *painter, const QStyleOptio if(!stime.isValid()) return; int leg = m->data(index.sibling(index.row(), RunsTableModel::Columns::col_runs_leg), Qt::EditRole).toInt(); - if(leg == 1 && m_classStartFirst != start_ms) { + if(leg == 1 && m_classDef.classStartFirst != start_ms) { //qfInfo() << m_highlightedClassId << m_classStartFirst << start_ms; QColor c = Qt::red; c.setAlphaF(0.5); @@ -88,15 +70,15 @@ void RunsTableItemDelegate::paintBackground(QPainter *painter, const QStyleOptio } } } - else if(isStartTimeHighlightVisible() && m_classInterval > 0) { + else if(isStartTimeHighlightVisible() && m_classDef.classInterval > 0) { //qfInfo() << "col:" << index.column() << m_highlightedClassId << "interval:" << m_classInterval << isStartTimeHighlightVisible(); QVariant stime_v = m->data(index.sibling(index.row(), RunsTableModel::Columns::col_runs_startTimeMs), Qt::EditRole); quickevent::core::og::TimeMs stime = stime_v.value(); int start_ms = stime.msec(); if(!stime.isValid()) return; - int prev_start_ms = m_classStartFirst; - int next_start_ms = m_classStartLast; + int prev_start_ms = m_classDef.classStartFirst; + int next_start_ms = m_classDef.classStartLast; //int table_row = v->toTableModelRowNo(index.row()); QString club = m->data(index.sibling(index.row(), RunsTableModel::Columns::col_registration), Qt::EditRole).toString().mid(0, 3).trimmed(); QString prev_club; @@ -108,11 +90,11 @@ void RunsTableItemDelegate::paintBackground(QPainter *painter, const QStyleOptio next_start_ms = m->data(index.sibling(index.row() + 1, RunsTableModel::Columns::col_runs_startTimeMs), Qt::EditRole).value().msec(); } - bool bad_start_time = (start_ms > m_classStartFirst && start_ms == prev_start_ms) || ((start_ms - prev_start_ms) % m_classInterval) != 0; - bool vacant_before = !bad_start_time && (((start_ms - prev_start_ms) > m_classInterval) || ((index.row() == 0) && (start_ms > m_classStartFirst))); + bool bad_start_time = (start_ms > m_classDef.classStartFirst && start_ms == prev_start_ms) || ((start_ms - prev_start_ms) % m_classDef.classInterval) != 0; + bool vacant_before = !bad_start_time && (((start_ms - prev_start_ms) > m_classDef.classInterval) || ((index.row() == 0) && (start_ms > m_classDef.classStartFirst))); bool vacant_after = !bad_start_time - && (((next_start_ms - start_ms) > m_classInterval) - || ((index.row() == tm->rowCount() - 1) && (start_ms < m_classStartLast))); + && (((next_start_ms - start_ms) > m_classDef.classInterval) + || ((index.row() == tm->rowCount() - 1) && (start_ms < m_classDef.classStartLast))); bool bad_club = !vacant_before && (club == prev_club); //auto cd = tm->columnDefinition(index.column()); if(index.column() == RunsTableModel::Columns::col_runs_startTimeMs) { diff --git a/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.h b/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.h index e20c75fd2..8a30bc7b0 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.h +++ b/quickevent/app/quickevent/plugins/Runs/src/runstableitemdelegate.h @@ -1,6 +1,7 @@ #ifndef RUNSTABLEITEMDELEGATE_H #define RUNSTABLEITEMDELEGATE_H +#include "../../Classes/src/classesplugin.h" #include #include @@ -22,9 +23,7 @@ class RunsTableItemDelegate : public quickevent::gui::og::ItemDelegate private: int m_stageId = 0; int m_highlightedClassId = 0; - int m_classStartFirst = 0; - int m_classStartLast = 0; - int m_classInterval = 0; + Classes::ClassDef m_classDef; }; #endif // RUNSTABLEITEMDELEGATE_H diff --git a/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp b/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp index 3dbf37265..768764f91 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.cpp @@ -31,7 +31,6 @@ RunsTableModel::RunsTableModel(QObject *parent) setColumn(col_competitorName, ColumnDefinition("competitorName", tr("Name"))); setColumn(col_registration, ColumnDefinition("registration", tr("Reg"))); setColumn(col_runs_license, ColumnDefinition("licence", tr("Lic")).setToolTip(tr("License"))); - setColumn(col_runs_ranking, ColumnDefinition("ranking", tr("Rank")).setToolTip(tr("Ranking"))); setColumn(col_runs_siId, ColumnDefinition("runs.siId", tr("SI")).setToolTip(tr("Actual SI")).setCastType(qMetaTypeId())); setColumn(col_runs_corridorTime, ColumnDefinition("runs.corridorTime", tr("Corridor")).setToolTip(tr("Time when the competitor entered start corridor"))); setColumn(col_runs_checkTimeMs, ColumnDefinition("runs.checkTimeMs", tr("Check")).setCastType(qMetaTypeId())); @@ -41,11 +40,8 @@ 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_notCompeting, ColumnDefinition("runs.notCompeting", tr("NC")).setToolTip(tr("Not competing"))); - //setColumn(col_runs_cardRentRequested, ColumnDefinition("runs.cardLent", tr("RR")).setToolTip(tr("Card rent requested"))); - //setColumn(col_cardInLentTable, ColumnDefinition("cardInLentTable", tr("RT", "cardInLentTable")).setToolTip(tr("Card in rent table"))); - //setColumn(col_runs_cardReturned, ColumnDefinition("runs.cardReturned", tr("R")).setToolTip(tr("Card returned"))); - //setColumn(col_runs_disqualified, ColumnDefinition("runs.disqualified", tr("DISQ")).setToolTip(tr("Disqualified"))); + setColumn(col_runs_rankingPos, ColumnDefinition("ranking", tr("Ranking pos")).setToolTip(tr("Runner's position in CZ ranking.")).setReadOnly(true)); + setColumn(col_iofId, ColumnDefinition("iofId", tr("IOF ID")).setReadOnly(true)); setColumn(col_competitors_note, ColumnDefinition("competitors.note", tr("Note"))); connect(this, &RunsTableModel::dataChanged, this, &RunsTableModel::onDataChanged, Qt::QueuedConnection); @@ -248,7 +244,7 @@ bool RunsTableModel::canDropMimeData(const QMimeData *data, Qt::DropAction actio */ bool RunsTableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { - qfLogFuncFrame() << "row:" << row << "col:" << column << "parent:" << parent; + // qfLogFuncFrame() << "row:" << row << "col:" << column << "parent:" << parent; if (!canDropMimeData(data, action, row, column, parent)) return false; diff --git a/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.h b/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.h index 78587c2c0..a60fab59a 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.h +++ b/quickevent/app/quickevent/plugins/Runs/src/runstablemodel.h @@ -20,7 +20,6 @@ class RunsTableModel : public quickevent::core::og::SqlTableModel col_competitorName, col_registration, col_runs_license, - col_runs_ranking, col_runs_siId, col_runs_corridorTime, col_runs_checkTimeMs, @@ -30,6 +29,8 @@ class RunsTableModel : public quickevent::core::og::SqlTableModel col_runs_timeMs, col_runFlags, col_cardFlags, + col_runs_rankingPos, + col_iofId, col_competitors_note, col_COUNT, }; diff --git a/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.cpp b/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.cpp index 11fc925bf..8818f4037 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.cpp @@ -3,12 +3,14 @@ #include "runstablemodel.h" #include "runstableitemdelegate.h" #include "runsplugin.h" +#include "runswidget.h" #include "runflagsdialog.h" #include "cardflagsdialog.h" #include #include +#include #include #include #include @@ -18,7 +20,6 @@ #include #include #include -#include #include #include @@ -27,16 +28,12 @@ #include #include +namespace qfc = qf::core; namespace qfs = qf::core::sql; namespace qfw = qf::qmlwidgets; -namespace qff = qf::qmlwidgets::framework; -namespace qfd = qf::qmlwidgets::dialogs; -namespace qfm = qf::core::model; using qf::qmlwidgets::framework::getPlugin; using Event::EventPlugin; -using Competitors::CompetitorsPlugin; using Runs::RunsPlugin; -using CardReader::CardReaderPlugin; using Receipts::ReceiptsPlugin; RunsTableWidget::RunsTableWidget(QWidget *parent) : @@ -49,7 +46,8 @@ RunsTableWidget::RunsTableWidget(QWidget *parent) : ui->tblRuns->setShowExceptionDialog(false); connect(ui->tblRuns, &qf::qmlwidgets::TableView::sqlException, this, &RunsTableWidget::onTableViewSqlException, Qt::QueuedConnection); - ui->tblRuns->setEditRowsMenuSectionEnabled(false); + // ui->tblRuns->setEditRowsMenuSectionEnabled(false); + ui->tblRuns->setCloneRowEnabled(false); ui->tblRuns->setDirtyRowsMenuSectionEnabled(false); ui->tblRuns->setPersistentSettingsId("tblRuns"); ui->tblRuns->setRowEditorMode(qfw::TableView::EditRowsMixed); @@ -73,7 +71,7 @@ RunsTableWidget::RunsTableWidget(QWidget *parent) : // this ensures that table is sorted every time when start time is edited ui->tblRuns->sortFilterProxyModel()->setDynamicSortFilter(true); - connect(m_runsModel, &RunsTableModel::startTimesSwitched, [this](int id1, int id2, const QString &err_msg) + connect(m_runsModel, &RunsTableModel::startTimesSwitched, this, [this](int id1, int id2, const QString &err_msg) { Q_UNUSED(id1) Q_UNUSED(id2) @@ -86,6 +84,16 @@ RunsTableWidget::RunsTableWidget(QWidget *parent) : connect(ui->tblRuns->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &RunsTableWidget::updateStartTimeHighlight); + connect(ui->tblRuns, &qfw::TableView::editRowInExternalEditor, this, [this](const QVariant &, int mode) { + auto *tv = ui->tblRuns; + auto ix = tv->currentIndex(); + auto col = ix.column(); + if(col == RunsTableModel::col_runFlags || col == RunsTableModel::col_cardFlags) { + return; + } + auto competitor_id = tv->selectedRow().value("competitorId"); + emit editCompetitorRequest(competitor_id.toInt(), mode); + }); connect(ui->tblRuns, &qfw::TableView::editCellRequest, this, [this](QModelIndex index) { auto col = index.column(); if(col == RunsTableModel::col_runFlags) { @@ -102,11 +110,6 @@ RunsTableWidget::RunsTableWidget(QWidget *parent) : dlg.save(); } } - else { - auto *tv = ui->tblRuns; - QVariant id = tv->selectedRow().value(tv->idColumnName()); - editCompetitor(id, qfw::TableView::ModeEdit); - } }, Qt::QueuedConnection); } @@ -204,14 +207,9 @@ void RunsTableWidget::reload() updateStartTimeHighlight(); } -void RunsTableWidget::editCompetitor(const QVariant &id, int mode) +qf::qmlwidgets::TableView *RunsTableWidget::tableView() { - Q_UNUSED(id) - int competitor_id = ui->tblRuns->tableRow().value("competitorId").toInt(); - int result = getPlugin()->editCompetitor(competitor_id, mode); - if(result == QDialog::Accepted) { - ui->tblRuns->reload(); - } + return ui->tblRuns; } void RunsTableWidget::onCustomContextMenuRequest(const QPoint &pos) @@ -223,11 +221,13 @@ void RunsTableWidget::onCustomContextMenuRequest(const QPoint &pos) QAction a_sep1(nullptr); a_sep1.setSeparator(true); QAction a_shift_start_times(tr("Shift start times in selected rows"), nullptr); QAction a_clear_start_times(tr("Clear start times in selected rows"), nullptr); + QAction a_change_class(tr("Set class in selected rows"), nullptr); QList lst; lst << &a_show_receipt << &a_load_card << &a_print_card << &a_sep1 << &a_shift_start_times - << &a_clear_start_times; + << &a_clear_start_times + << &a_change_class; QAction *a = QMenu::exec(lst, ui->tblRuns->viewport()->mapToGlobal(pos)); if(a == &a_load_card) { qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork(); @@ -320,6 +320,37 @@ void RunsTableWidget::onCustomContextMenuRequest(const QPoint &pos) qf::qmlwidgets::dialogs::MessageBox::showException(this, e); } } + else if(a == &a_change_class) { + qfw::dialogs::GetItemInputDialog dlg(this); + QComboBox *box = dlg.comboBox(); + qfs::Query q; + q.exec("SELECT id, name FROM classes ORDER BY name"); + while (q.next()) { + box->addItem(q.value(1).toString(), q.value(0)); + } + dlg.setWindowTitle(tr("Dialog")); + dlg.setLabelText(tr("Select class")); + dlg.setCurrentItemIndex(-1); + if(dlg.exec()) { + int class_id = dlg.currentData().toInt(); + if(class_id > 0) { + qfs::Transaction transaction; + try { + QList rows = ui->tblRuns->selectedRowsIndexes(); + for(int i : rows) { + qf::core::utils::TableRow row = ui->tblRuns->tableRowRef(i); + int competitor_id = row.value("competitors.id").toInt(); + q.exec(QString("UPDATE competitors SET classId=%1 WHERE id=%2").arg(class_id).arg(competitor_id), qfc::Exception::Throw); + } + transaction.commit(); + } + catch (std::exception &e) { + qfError() << e.what(); + } + } + ui->tblRuns->reload(true); + } + } } void RunsTableWidget::onTableViewSqlException(const QString &what, const QString &where, const QString &stack_trace) diff --git a/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.h b/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.h index 15a2c8529..d28cca818 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.h +++ b/quickevent/app/quickevent/plugins/Runs/src/runstablewidget.h @@ -4,6 +4,7 @@ class RunsTableModel; class RunsTableItemDelegate; +namespace qf::qmlwidgets { class TableView; } namespace Ui { class RunsTableWidget; @@ -23,9 +24,11 @@ class RunsTableWidget : public QWidget void reload(); RunsTableModel* runsModel() {return m_runsModel;} + qf::qmlwidgets::TableView* tableView(); + + Q_SIGNAL void editCompetitorRequest(int competitor_id, int mode); private: void updateStartTimeHighlight() const; - void editCompetitor(const QVariant &id, int mode); void onCustomContextMenuRequest(const QPoint &pos); void onTableViewSqlException(const QString &what, const QString &where, const QString &stack_trace); void onBadTableDataInput(const QString &message); diff --git a/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp b/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp index fdd09fe58..bcb2dff86 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp +++ b/quickevent/app/quickevent/plugins/Runs/src/runswidget.cpp @@ -3,6 +3,10 @@ #include "runstablemodel.h" #include "runsplugin.h" +#include +#include +#include + #include #include #include @@ -16,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -24,7 +30,6 @@ #include #include #include -#include #include #include @@ -41,6 +46,8 @@ static const auto SkipEmptyParts = QString::SkipEmptyParts; static const auto SkipEmptyParts = Qt::SkipEmptyParts; #endif +namespace qfc = qf::core; +namespace qfu = qf::core::utils; namespace qfs = qf::core::sql; namespace qfw = qf::qmlwidgets; namespace qff = qf::qmlwidgets::framework; @@ -81,7 +88,11 @@ RunsWidget::RunsWidget(QWidget *parent) : ui->cbxDrawMethod->addItem(tr("Grouped by ranking (PSOB DH21L)"), static_cast(DrawMethod::GroupedRanking)); } }); - QMetaObject::invokeMethod(this, &RunsWidget::lazyInit, Qt::QueuedConnection); + // connect(ui->wRunsTableWidget->tableView(), &qfw::TableView::editRowInExternalEditor, this, &RunsWidget::editCompetitor, Qt::QueuedConnection); + connect(ui->wRunsTableWidget, &RunsTableWidget::editCompetitorRequest, this, &RunsWidget::editCompetitor, Qt::QueuedConnection); + connect(ui->wRunsTableWidget->tableView(), &qfw::TableView::editSelectedRowsInExternalEditor, this, &RunsWidget::editCompetitors, Qt::QueuedConnection); + connect(ui->btDraw, &QAbstractButton::clicked, this, &RunsWidget::onDrawClicked); + connect(ui->btDrawRemove, &QAbstractButton::clicked, this, &RunsWidget::onDrawRemoveClicked); } RunsWidget::~RunsWidget() @@ -89,10 +100,6 @@ RunsWidget::~RunsWidget() delete ui; } -void RunsWidget::lazyInit() -{ -} - void RunsWidget::reset(int class_id) { qfLogFuncFrame(); @@ -141,8 +148,10 @@ void RunsWidget::reload() void RunsWidget::settleDownInPartWidget(::PartWidget *part_widget) { qfLogFuncFrame(); - connect(part_widget, SIGNAL(resetPartRequest()), this, SLOT(reset())); - connect(part_widget, SIGNAL(reloadPartRequest()), this, SLOT(reload())); + connect(part_widget, &::PartWidget::resetPartRequest, this, [this]() { reset(); }); + connect(part_widget, &::PartWidget::reloadPartRequest, this, &RunsWidget::reload); + + connect(getPlugin(), &Runs::RunsPlugin::editCompetitorOnPunchRequest, this, &RunsWidget::editCompetitorOnPunch); auto *a_print = part_widget->menuBar()->actionForPath("print", true); a_print->setText(tr("&Print")); @@ -216,6 +225,12 @@ void RunsWidget::settleDownInPartWidget(::PartWidget *part_widget) } } a_print->addSeparatorInto(); + { + auto *a = new qfw::Action(tr("Competitors statistics")); + //a->setShortcut("ctrl+L"); + connect(a, &qfw::Action::triggered, this, &RunsWidget::report_competitorsStatistics); + a_print->addActionInto(a); + } { auto *a = new qfw::Action(tr("&Competitors with rented cards")); connect(a, &qfw::Action::triggered, [this]() { @@ -250,7 +265,7 @@ void RunsWidget::settleDownInPartWidget(::PartWidget *part_widget) a_import->setText(tr("&Import")); qfw::Action *a_import_start_times = a_import->addMenuInto("startTimes", tr("Start times")); - qfw::Action *a_import_start_times_ob2000 = new qfw::Action("ob2000", tr("OB 2000")); + auto *a_import_start_times_ob2000 = new qfw::Action("ob2000", tr("OB 2000")); a_import_start_times->addActionInto(a_import_start_times_ob2000); connect(a_import_start_times_ob2000, &qfw::Action::triggered, this, &RunsWidget::import_start_times_ob2000); @@ -332,7 +347,7 @@ void RunsWidget::settleDownInPartWidget(::PartWidget *part_widget) qfw::ToolBar *main_tb = part_widget->toolBar("main", true); //main_tb->addAction(m_actCommOpen); { - QLabel *lbl = new QLabel(tr("Stage ")); + auto *lbl = new QLabel(tr("Stage ")); main_tb->addWidget(lbl); } { @@ -357,7 +372,7 @@ void RunsWidget::settleDownInPartWidget(::PartWidget *part_widget) } lbl_classes->setBuddy(m_cbxClasses); { - QLabel *lbl_leg = new QLabel(tr("&Leg ")); + auto *lbl_leg = new QLabel(tr("&Leg ")); m_toolbarActionLabelLeg = main_tb->addWidget(lbl_leg); m_cbxLeg = new QComboBox(); { @@ -645,7 +660,7 @@ void RunsWidget::saveLockedForDrawing(int class_id, int stage_id, bool is_locked q.exec(qf::core::Exception::Throw); } -void RunsWidget::on_btDraw_clicked() +void RunsWidget::onDrawClicked() { qfLogFuncFrame(); bool is_relays = getPlugin()->eventConfig()->isRelays(); @@ -933,7 +948,7 @@ void RunsWidget::on_btDraw_clicked() ui->wRunsTableWidget->reload(); } -void RunsWidget::on_btDrawRemove_clicked() +void RunsWidget::onDrawRemoveClicked() { int class_id = m_cbxClasses->currentData().toInt(); if(class_id == 0) @@ -1022,3 +1037,161 @@ void RunsWidget::export_results_stage_csv() exp.setWithDidNotStart(true); exp.generateCsvSingle(); } + +void RunsWidget::editCompetitors(int mode) +{ + if(mode == qfm::DataDocument::ModeDelete) { + auto *tv = ui->wRunsTableWidget->tableView(); + QList sel_rows = tv->selectedRowsIndexes(); + if(sel_rows.count() <= 1) + return; + if(qfd::MessageBox::askYesNo(this, tr("Really delete all the selected competitors? This action cannot be reverted."), false)) { + qfs::Transaction transaction; + int n = 0; + for(int ix : sel_rows) { + int id = tv->tableRow(ix).value(tv->idColumnName()).toInt(); + if(id > 0) { + Competitors::CompetitorDocument doc; + doc.load(id, qfm::DataDocument::ModeDelete); + doc.drop(); + n++; + } + } + if(n > 0) { + if(qfd::MessageBox::askYesNo(this, tr("Confirm deletion of %1 competitors.").arg(n), false)) { + transaction.commit(); + tv->reload(); + } + else { + transaction.rollback(); + } + } + } + } +} + +void RunsWidget::editCompetitorOnPunch(int siid) +{ + qfs::Query q; + q.exec("SELECT id FROM competitors WHERE siId=" + QString::number(siid), qfc::Exception::Throw); + if(q.next()) { + int competitor_id = q.value(0).toInt(); + if(competitor_id > 0) { + editCompetitor_helper(competitor_id, qfm::DataDocument::ModeEdit, 0); + } + } + else { + editCompetitor_helper(QVariant(), qfm::DataDocument::ModeInsert, siid); + } +} + +void RunsWidget::report_competitorsStatistics() +{ + qfLogFuncFrame(); + int stage_cnt = getPlugin()->stageCount(); + + qfs::QueryBuilder qb; + qb.select2("classes", "id, name").from("classes").orderBy("classes.name"); + qf::core::model::SqlTableModel m; + m.setQueryBuilder(qb); + m.reload(); + qfu::TreeTable tt = m.toTreeTable(); + tt.setValue("event", getPlugin()->eventConfig()->value("event")); + for (int stage_id = 1; stage_id <= stage_cnt; ++stage_id) { + QString prefix = "e" + QString::number(stage_id) + "_"; + QString col_runs_count = prefix + "runCount"; + QString col_map_count = prefix + "mapCount"; + tt.appendColumn(col_runs_count, QMetaType(QMetaType::Int)); + tt.appendColumn(col_map_count, QMetaType(QMetaType::Int)); + { + 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 + .from("classes") + .joinRestricted("classes.id", "classdefs.classid", "classdefs.stageId={{stage_id}}") + .join("classes.id", "competitors.classId") + .joinRestricted("competitors.id", "runs.competitorId", "runs.isRunning AND runs.stageId={{stage_id}}") + .groupBy("classes.name") + .orderBy("classes.name"); + QVariantMap qpm; + qpm["stage_id"] = stage_id; + qfs::Query q; + q.execThrow(qb.toString(qpm)); + int i = 0; + while(q.next()) { + qf::core::utils::TreeTableRow tt_row = tt.row(i); + tt_row.setValue(col_runs_count, q.value("runCount")); + tt_row.setValue(col_map_count, q.value("mapCount")); + tt.setRow(i, tt_row); + i++; + } + } + } + qfDebug() << tt.toString(); + QVariantMap props; + //props["isBreakAfterEachClass"] = (opts.breakType() != (int)quickevent::gui::ReportOptionsDialog::BreakType::None); + //props["isColumnBreak"] = (opts.breakType() == (int)quickevent::gui::ReportOptionsDialog::BreakType::Column); + props["stageCount"] = stage_cnt; + QString rep_fn = getPlugin()->findReportFile("competitorsStatistics.qml"); + qf::qmlwidgets::reports::ReportViewWidget::showReport(this + , rep_fn + , tt.toVariant() + , tr("Competitors statistics") + , "competitorsStatistics" + , props + ); +} + +void RunsWidget::editCompetitor_helper(const QVariant &id, int mode, int siid) +{ + qfLogFuncFrame() << "id:" << id << "mode:" << mode; + bool ok = false; + bool save_and_next = false; + + { + auto *w = new CompetitorWidget(); + w->setWindowTitle(tr("Edit Competitor")); + qfd::Dialog dlg(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + dlg.setDefaultButton(QDialogButtonBox::Ok); + if(mode == qf::core::model::DataDocument::ModeInsert || mode == qf::core::model::DataDocument::ModeEdit) { + QPushButton *bt_save = dlg.buttonBox()->addButton(tr("Save"), QDialogButtonBox::ApplyRole); + connect(dlg.buttonBox(), &qf::qmlwidgets::DialogButtonBox::clicked, [w, bt_save](QAbstractButton *button) { + if (button == bt_save) { + w->save(); + } + }); + QPushButton *bt_save_and_next = dlg.buttonBox()->addButton(tr("Ok and &next"), QDialogButtonBox::AcceptRole); + connect(dlg.buttonBox(), &qf::qmlwidgets::DialogButtonBox::clicked, [&save_and_next, bt_save_and_next](QAbstractButton *button) { + save_and_next = (button == bt_save_and_next); + }); + } + dlg.setCentralWidget(w); + w->load(id, mode); + auto *doc = qobject_cast(w->dataController()->document()); + QF_ASSERT(doc != nullptr, "Document is null!", return); + if(mode == qfm::DataDocument::ModeInsert) { + if(siid == 0) { + bool is_relays = getPlugin()->eventConfig()->isRelays(); + if(!is_relays) { + int class_id = m_cbxClasses->currentData().toInt(); + if(class_id > 0) + doc->setValue("competitors.classId", class_id); + else + doc->setValue("competitors.classId", QVariant()); + } + } + else { + w->loadFromRegistrations(siid); + } + } + connect(doc, &Competitors::CompetitorDocument::saved, ui->wRunsTableWidget->tableView(), &qf::qmlwidgets::TableView::rowExternallySaved, Qt::QueuedConnection); + ok = dlg.exec(); + + } + if(ok && save_and_next) { + QTimer::singleShot(0, [this]() { + this->editCompetitor(QVariant(), qf::core::model::DataDocument::ModeInsert); + }); + } +} diff --git a/quickevent/app/quickevent/plugins/Runs/src/runswidget.h b/quickevent/app/quickevent/plugins/Runs/src/runswidget.h index 58ee7cdc6..16165646e 100644 --- a/quickevent/app/quickevent/plugins/Runs/src/runswidget.h +++ b/quickevent/app/quickevent/plugins/Runs/src/runswidget.h @@ -56,13 +56,19 @@ class RunsWidget : public QFrame void export_results_overall_csos(); void export_results_stage_csv(); -private slots: - void on_btDraw_clicked(); - void on_btDrawRemove_clicked(); - void onCbxStageCurrentIndexChanged(); +public: + void editCompetitor(const QVariant &id, int mode) {editCompetitor_helper(id, mode, 0);} + void editCompetitors(int mode); + void editCompetitorOnPunch(int siid); private: + void report_competitorsStatistics(); + + void editCompetitor_helper(const QVariant &id, int mode, int siid); - Q_SLOT void lazyInit(); + void onDrawClicked(); + void onDrawRemoveClicked(); + void onCbxStageCurrentIndexChanged(); +private: /** * @brief runnersInClubsHistogram diff --git a/quickevent/app/quickevent/src/mainwindow.cpp b/quickevent/app/quickevent/src/mainwindow.cpp index 503ea64af..6a80c2ab1 100644 --- a/quickevent/app/quickevent/src/mainwindow.cpp +++ b/quickevent/app/quickevent/src/mainwindow.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -72,7 +71,7 @@ void MainWindow::onPluginsLoaded() auto *w = qobject_cast(centralWidget()); menuBar()->actionForPath("view/toolbar")->addActionInto(w->partSwitch()->toggleViewAction()); - centralWidget()->setActivePart("Competitors", true); + centralWidget()->setActivePart("Runs", true); setPersistentSettingsId("MainWindow"); loadPersistentSettings(); } @@ -87,16 +86,16 @@ void MainWindow::loadPlugins() auto *plugin = new Event::EventPlugin(this); registerPlugin(plugin); } + // { + // auto *plugin = new Competitors::CompetitorsPlugin(this); + // registerPlugin(plugin); + // } { - auto *plugin = new Classes::ClassesPlugin(this); - registerPlugin(plugin); - } - { - auto *plugin = new Competitors::CompetitorsPlugin(this); + auto *plugin = new Runs::RunsPlugin(this); registerPlugin(plugin); } { - auto *plugin = new Runs::RunsPlugin(this); + auto *plugin = new Classes::ClassesPlugin(this); registerPlugin(plugin); } {