diff --git a/libqf/libqfcore/src/sql/query.cpp b/libqf/libqfcore/src/sql/query.cpp index b35788c3e..6c03c5954 100644 --- a/libqf/libqfcore/src/sql/query.cpp +++ b/libqf/libqfcore/src/sql/query.cpp @@ -32,6 +32,13 @@ bool Query::prepare(const QString &query, bool throw_exc) return ret; } +Query Query::fromExec(const QString &query, bool throw_exc) +{ + Query q; + q.exec(query, throw_exc); + return q; +} + bool Query::exec(const QString &query, bool throw_exc) { qfLogFuncFrame() << query; diff --git a/libqf/libqfcore/src/sql/query.h b/libqf/libqfcore/src/sql/query.h index e65814134..3bdc92bfd 100644 --- a/libqf/libqfcore/src/sql/query.h +++ b/libqf/libqfcore/src/sql/query.h @@ -28,6 +28,7 @@ class QFCORE_DECL_EXPORT Query : public QSqlQuery //using Super::prepare; bool prepare(const QString& query, bool throw_exc = false); //using Super::exec; + static Query fromExec(const QString &query, bool throw_exc = true); bool exec(const QString &query, bool throw_exc = false); bool execThrow(const QString &query) {return exec(query, true);} bool exec(const QueryBuilder &query_builder, bool throw_exc = false); diff --git a/libquickevent/libquickeventcore/include/quickevent/core/utils.h b/libquickevent/libquickeventcore/include/quickevent/core/utils.h new file mode 100644 index 000000000..2b3dec6e6 --- /dev/null +++ b/libquickevent/libquickeventcore/include/quickevent/core/utils.h @@ -0,0 +1 @@ +#include "../../../src/utils.h" diff --git a/libquickevent/libquickeventcore/src/codedef.h b/libquickevent/libquickeventcore/src/codedef.h index 519cdf087..a0650bf97 100644 --- a/libquickevent/libquickeventcore/src/codedef.h +++ b/libquickevent/libquickeventcore/src/codedef.h @@ -22,7 +22,7 @@ class QUICKEVENTCORE_DECL_EXPORT CodeDef : public QVariantMap QF_VARIANTMAP_FIELD(int, d, setD, istance) public: - static constexpr int PUNCH_CODE_MIN = 100; + static constexpr int PUNCH_CODE_MIN = 10; static constexpr int PUNCH_CODE_MAX = 899; static const int START_PUNCH_CODE; static const int FINISH_PUNCH_CODE; diff --git a/libquickevent/libquickeventcore/src/src.pri b/libquickevent/libquickeventcore/src/src.pri index 32711fc20..75b99d24e 100644 --- a/libquickevent/libquickeventcore/src/src.pri +++ b/libquickevent/libquickeventcore/src/src.pri @@ -2,10 +2,12 @@ HEADERS += \ $$PWD/quickeventcoreglobal.h \ $$PWD/coursedef.h \ $$PWD/codedef.h \ + $$PWD/utils.h SOURCES += \ $$PWD/coursedef.cpp \ $$PWD/codedef.cpp \ + $$PWD/utils.cpp FORMS += \ diff --git a/libquickevent/libquickeventcore/src/utils.cpp b/libquickevent/libquickeventcore/src/utils.cpp new file mode 100644 index 000000000..97cef6244 --- /dev/null +++ b/libquickevent/libquickeventcore/src/utils.cpp @@ -0,0 +1,31 @@ +#include "utils.h" + +#include + +namespace quickevent { +namespace core { + + QString Utils::dateTimeToIsoStringWithUtcOffset(const QDateTime &dt) +{ + QString ret = dt.toString(Qt::ISODate); + if(dt.timeSpec() == Qt::LocalTime) { + int offset_min = dt.offsetFromUtc(); + if(offset_min == 0) { + ret += 'Z'; + } + else { + if(offset_min < 0) { + ret += '-'; + offset_min = -offset_min; + } + else { + ret += '+'; + } + ret += QStringLiteral("%1:%2").arg(offset_min / (60 * 60), 2, 10, QChar('0')).arg((offset_min / 60) % 60, 2, 10, QChar('0')); + } + } + return ret; +} + +} // namespace core +} // namespace quickevent diff --git a/libquickevent/libquickeventcore/src/utils.h b/libquickevent/libquickeventcore/src/utils.h new file mode 100644 index 000000000..2fb4ab5c1 --- /dev/null +++ b/libquickevent/libquickeventcore/src/utils.h @@ -0,0 +1,20 @@ +#ifndef QUICKEVENT_CORE_UTILS_H +#define QUICKEVENT_CORE_UTILS_H + +#include "quickeventcoreglobal.h" + +class QDateTime; + +namespace quickevent { +namespace core { + +class QUICKEVENTCORE_DECL_EXPORT Utils +{ +public: + static QString dateTimeToIsoStringWithUtcOffset(const QDateTime &dt); +}; + +} // namespace core +} // namespace quickevent + +#endif // QUICKEVENT_CORE_UTILS_H diff --git a/quickevent/app/plugins/CardReader/src/cardchecker.cpp b/quickevent/app/plugins/CardReader/src/cardchecker.cpp index 39f9a4ba7..4e0418a0d 100644 --- a/quickevent/app/plugins/CardReader/src/cardchecker.cpp +++ b/quickevent/app/plugins/CardReader/src/cardchecker.cpp @@ -103,7 +103,7 @@ int CardChecker::cardCheckCheckTimeSec() QVariantMap CardChecker::courseCodesForRunId(int run_id) { - return runsPlugin()->courseCodesForRunId(run_id, true); + return runsPlugin()->courseCodesForRunId(run_id); } } diff --git a/quickevent/app/plugins/Event/Event.pro b/quickevent/app/plugins/Event/Event.pro index 8e959a0f3..e613ffa4f 100644 --- a/quickevent/app/plugins/Event/Event.pro +++ b/quickevent/app/plugins/Event/Event.pro @@ -19,8 +19,6 @@ TRANSLATIONS += \ $${PLUGIN_NAME}.ru_RU.ts \ $${PLUGIN_NAME}.nl_BE.ts \ -lupdate_only { -SOURCES += \ +OTHER_FILES += \ $$PWD/qml/*.qml \ -} diff --git a/quickevent/app/plugins/Relays/src/relaysplugin.cpp b/quickevent/app/plugins/Relays/src/relaysplugin.cpp index 4039160c5..b59f37dc2 100644 --- a/quickevent/app/plugins/Relays/src/relaysplugin.cpp +++ b/quickevent/app/plugins/Relays/src/relaysplugin.cpp @@ -4,6 +4,7 @@ #include "relaywidget.h" #include +#include #include #include @@ -20,6 +21,7 @@ #include #include +#include #include namespace qfw = qf::qmlwidgets; @@ -36,7 +38,13 @@ static Event::EventPlugin* eventPlugin() qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork(); return fwk->plugin(); } - +/* +static Runs::RunsPlugin* runsPlugin() +{ + qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork(); + return fwk->plugin(); +} +*/ RelaysPlugin::RelaysPlugin(QObject *parent) : Super("Relays", parent) { @@ -103,8 +111,12 @@ namespace { struct Leg { - QString name, reg; + QString fullName; + QString firstName; + QString lastName; + QString reg; int runId = 0; + //int courseId = 0; int time = 0; int pos = 0; int stime = 0; @@ -112,12 +124,23 @@ struct Leg bool disq = false; bool nc = false; bool notfinish = true; + QString status() const + { + if (notfinish) + return QStringLiteral("DidNotFinish"); + if (nc) + return QStringLiteral("NotCompeting"); + if (disq) + return QStringLiteral("Disqualified"); + return QStringLiteral("OK"); + } }; struct Relay { QString name; QVector legs; + int relayNumber = 0; int relayId = 0; int loss = 0; @@ -130,12 +153,25 @@ struct Relay return qog::TimeMs::DISQ_TIME_MSEC; if(leg.nc) return qog::TimeMs::NOT_COMPETITING_TIME_MSEC; - if(leg.time == 0) + if(leg.notfinish) return qog::TimeMs::NOT_FINISH_TIME_MSEC; ret += leg.time; } return ret; } + QString status(int leg_cnt) const + { + for (int i = 0; i < qMin(legs.count(), leg_cnt); ++i) { + const Leg &leg = legs[i]; + if(leg.disq) + return QStringLiteral("Disqualified"); + if(leg.nc) + return QStringLiteral("NotCompeting"); + if(leg.notfinish) + return QStringLiteral("DidNotFinish"); + } + return QStringLiteral("OK"); + } #if 0 bool isDisq(int leg_cnt) const { @@ -176,6 +212,7 @@ struct Relay qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(const QString &where_option, int leg_count, int places, bool exclude_not_finish) { + qfLogFuncFrame() << "leg cnt:" << leg_count; qf::core::utils::TreeTable tt; tt.setValue("event", eventPlugin()->eventConfig()->value("event")); tt.setValue("stageStart", eventPlugin()->stageStartDateTime(1)); @@ -193,15 +230,22 @@ qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(const QString &where_ int ix = tt.appendRow(); qf::core::utils::TreeTableRow tt_row = tt.row(ix); tt_row.setValue("className", q.value("classes.name")); - qf::core::utils::TreeTable tt2 = nLegsResultsTable(q.value("classes.id").toInt(), leg_count, places, exclude_not_finish); + qf::core::utils::TreeTable tt2 = nLegsClassResultsTable(q.value("classes.id").toInt(), leg_count, places, exclude_not_finish); tt_row.appendTable(tt2); tt.setRow(ix, tt_row); //qfDebug().noquote() << tt2.toString(); } + auto wt = [tt]() { + QFile f("/home/fanda/t/relays.json"); + f.open(QFile::WriteOnly); + f.write(tt.toString().toUtf8()); + return f.fileName(); + }; + qfDebug() << "nLegsResultsTable table:" << wt(); return tt; } -qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(int class_id, int leg_count, int places, bool exclude_not_finish) +qf::core::utils::TreeTable RelaysPlugin::nLegsClassResultsTable(int class_id, int leg_count, int places, bool exclude_not_finish) { int max_leg = 0; qfs::Query q; @@ -234,6 +278,7 @@ qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(int class_id, int leg while(q.next()) { Relay r; r.relayId = q.value("relays.id").toInt(); + r.relayNumber = q.value("relays.number").toInt(); r.name = (q.value("relays.number").toString() + ' ' + q.value("relays.club").toString() + ' ' + q.value("relays.name").toString() @@ -249,6 +294,7 @@ qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(int class_id, int leg qfs::QueryBuilder qb; qb.select2("competitors", "id, registration") .select2("runs", "id, relayId, leg") + .select2("competitors", "firstName, lastName") .select("COALESCE(competitors.lastName, '') || ' ' || COALESCE(competitors.firstName, '') AS competitorName") .from("runs") .join("runs.competitorId", "competitors.id") @@ -264,9 +310,12 @@ qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(int class_id, int leg Relay &relay = relays[i]; int legno = q.value("runs.leg").toInt(); Leg &leg = relay.legs[legno - 1]; - leg.name = q.value("competitorName").toString(); + leg.fullName = q.value("competitorName").toString(); + leg.firstName = q.value("firstName").toString(); + leg.lastName = q.value("lastName").toString(); leg.runId = q.value("runs.id").toInt(); leg.reg = q.value("competitors.registration").toString(); + //leg.courseId = runsPlugin()->courseForRun(leg.runId); break; } } @@ -361,8 +410,11 @@ qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(int class_id, int leg qf::core::utils::TreeTable tt; tt.appendColumn("pos", QVariant::Int); tt.appendColumn("name", QVariant::String); + tt.appendColumn("relayNumber", QVariant::Int); + tt.appendColumn("id", QVariant::Int); tt.appendColumn("time", QVariant::Int); tt.appendColumn("loss", QVariant::Int); + tt.appendColumn("status", QVariant::String); for (int i = 0; i < relays.count(); ++i) { int ix = tt.appendRow(); qf::core::utils::TreeTableRow tt_row = tt.row(ix); @@ -373,33 +425,47 @@ qf::core::utils::TreeTable RelaysPlugin::nLegsResultsTable(int class_id, int leg int prev_time = (i > 0)? relays[i-1].time(leg_count): 0; tt_row.setValue("pos", (time <= qog::TimeMs::MAX_REAL_TIME_MSEC && time > prev_time)? i+1: 0); tt_row.setValue("name", relay.name); + tt_row.setValue("relayNumber", relay.relayNumber); + tt_row.setValue("id", relay.relayId); tt_row.setValue("time", time); tt_row.setValue("loss", (time <= qog::TimeMs::MAX_REAL_TIME_MSEC)?time - time0: 0); + tt_row.setValue("status", relay.status(relay.legs.count())); qfDebug() << tt.rowCount() << relay.name; qf::core::utils::TreeTable tt2; - tt2.appendColumn("name", QVariant::String); - tt2.appendColumn("reg", QVariant::String); + tt2.appendColumn("competitorName", QVariant::String); + tt2.appendColumn("firstName", QVariant::String); + tt2.appendColumn("lastName", QVariant::String); + tt2.appendColumn("registration", QVariant::String); tt2.appendColumn("time", QVariant::Int); tt2.appendColumn("pos", QVariant::Int); + tt2.appendColumn("status", QVariant::String); tt2.appendColumn("stime", QVariant::Int); tt2.appendColumn("spos", QVariant::Int); - //tt.appendColumn("disq", QVariant::Bool); + tt2.appendColumn("runId", QVariant::Int); + tt2.appendColumn("courseId", QVariant::Int); + tt2.appendColumn("sstatus", QVariant::String); for (int j = 0; j < qMin(relay.legs.count(), places); ++j) { const Leg &leg = relay.legs[j]; int ix2 = tt2.appendRow(); qf::core::utils::TreeTableRow tt2_row = tt2.row(ix2); - tt2_row.setValue("competitorName", leg.name); + tt2_row.setValue("competitorName", leg.fullName); + tt2_row.setValue("firstName", leg.firstName); + tt2_row.setValue("lastName", leg.lastName); tt2_row.setValue("registration", leg.reg); tt2_row.setValue("time", leg.disq? qog::TimeMs::DISQ_TIME_MSEC : (leg.time == 0)? qog::TimeMs::NOT_FINISH_TIME_MSEC : leg.time); tt2_row.setValue("pos", leg.pos); + tt2_row.setValue("status", leg.status()); tt2_row.setValue("stime", leg.stime); tt2_row.setValue("spos", leg.spos); + tt2_row.setValue("runId", leg.runId); + tt2_row.setValue("sstatus", relay.status(j+1)); + //tt2_row.setValue("courseId", leg.courseId); tt2.setRow(ix2, tt2_row); //rr2.setValue("disq", leg.disq); - qfDebug() << '\t' << leg.pos << leg.name; + qfDebug() << '\t' << leg.pos << leg.fullName; } tt_row.appendTable(tt2); tt.setRow(ix, tt_row); diff --git a/quickevent/app/plugins/Relays/src/relaysplugin.h b/quickevent/app/plugins/Relays/src/relaysplugin.h index a1096136b..a59bed56c 100644 --- a/quickevent/app/plugins/Relays/src/relaysplugin.h +++ b/quickevent/app/plugins/Relays/src/relaysplugin.h @@ -45,7 +45,7 @@ class RELAYSPLUGIN_DECL_EXPORT RelaysPlugin : public qf::qmlwidgets::framework:: Q_SIGNAL void nativeInstalled(); qf::core::utils::TreeTable nLegsResultsTable(const QString &where_option, int leg_count, int places, bool exclude_not_finish); - qf::core::utils::TreeTable nLegsResultsTable(int class_id, int leg_count, int places, bool exclude_not_finish); + qf::core::utils::TreeTable nLegsClassResultsTable(int class_id, int leg_count, int places, bool exclude_not_finish); private: Q_SLOT void onInstalled(); void onDbEventNotify(const QString &domain, int connection_id, const QVariant &data); diff --git a/quickevent/app/plugins/Relays/src/relayswidget.cpp b/quickevent/app/plugins/Relays/src/relayswidget.cpp index 8f7a45316..5d5bbbb94 100644 --- a/quickevent/app/plugins/Relays/src/relayswidget.cpp +++ b/quickevent/app/plugins/Relays/src/relayswidget.cpp @@ -7,14 +7,17 @@ #include "relaysplugin.h" #include "Event/eventplugin.h" +#include "Runs/runsplugin.h" #include #include +#include #include #include #include +#include #include #include #include @@ -29,14 +32,18 @@ #include #include #include +#include #include - #include #include #include +#include #include #include +//#define QF_TIMESCOPE_ENABLED +#include + namespace qfs = qf::core::sql; namespace qfw = qf::qmlwidgets; namespace qff = qf::qmlwidgets::framework; @@ -45,20 +52,28 @@ namespace qfc = qf::core; namespace qfm = qf::core::model; namespace { + +static QString datetime_to_string(const QDateTime &dt) +{ + return quickevent::core::Utils::dateTimeToIsoStringWithUtcOffset(dt); +} + Event::EventPlugin* eventPlugin() { qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork(); - auto *plugin = qobject_cast(fwk->plugin("Event")); - QF_ASSERT_EX(plugin != nullptr, "Bad Event plugin!"); - return plugin; + return fwk->plugin(); +} + +Runs::RunsPlugin* runsPlugin() +{ + qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork(); + return fwk->plugin(); } Relays::RelaysPlugin* thisPlugin() { qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork(); - auto *plugin = qobject_cast(fwk->plugin("Relays")); - QF_ASSERT_EX(plugin != nullptr, "Bad Relays plugin!"); - return plugin; + return fwk->plugin(); } enum Columns { @@ -174,6 +189,15 @@ void RelaysWidget::settleDownInPartWidget(ThisPartWidget *part_widget) a_print_results->addActionInto(a); connect(a, &qfw::Action::triggered, this, &RelaysWidget::print_results_overal); } + + qfw::Action *a_export = part_widget->menuBar()->actionForPath("export"); + a_export->setText(tr("E&xport")); + qfw::Action *a_export_results = a_export->addMenuInto("export", tr("&Results")); + { + qfw::Action *a = new qfw::Action("exportIofXml3", tr("IOF-XML 3.0")); + a_export_results->addActionInto(a); + connect(a, &qfw::Action::triggered, this, &RelaysWidget::export_results_iofxml3); + } } void RelaysWidget::lazyInit() @@ -524,14 +548,238 @@ void RelaysWidget::printResults(const QString &settings_id, const QVariantMap &d quickevent::gui::ReportOptionsDialog::Options opts = dlg.options(); qfDebug() << opts; qfDebug() << "opts.resultNumPlaces:" << opts.resultNumPlaces(); - QVariant td = thisPlugin()->nLegsResultsTable(dlg.sqlWhereExpression(), opts.legsCount(), opts.resultNumPlaces(), opts.isResultExcludeDisq()).toVariant(); + auto td = thisPlugin()->nLegsResultsTable(dlg.sqlWhereExpression(), opts.legsCount(), opts.resultNumPlaces(), opts.isResultExcludeDisq()); qf::qmlwidgets::reports::ReportViewWidget::showReport(this, thisPlugin()->manifest()->homeDir() + "/reports/results.qml" - , td + , td.toVariant() , tr("Start list by clubs") , "relaysResults" , props ); } +static void append_list(QVariantList &lst, const QVariantList &new_lst) +{ + lst.insert(lst.count(), new_lst); +} + +void RelaysWidget::export_results_iofxml3() +{ + qfLogFuncFrame(); + QString fn = "relays-results-iof-3.0.xml"; + QString ext = ".xml"; + fn = qfd::FileDialog::getSaveFileName(this, tr("Save as %1").arg(ext.mid(1).toUpper()), fn, '*' + ext); + if(!fn.isEmpty()) { + if(!fn.endsWith(ext, Qt::CaseInsensitive)) + fn += ext; + } + if(fn.isEmpty()) + return; + QFile out_file(fn); + if(!out_file.open(QIODevice::WriteOnly)) { + qfError() << "Cannot open file" << out_file.fileName() << "for writing."; + return; + } + + int progress_count = 0; + int progress_val = 0; + { + auto q = qfs::Query::fromExec("SELECT COUNT(*) FROM classes"); + if(q.next()) + progress_count = q.value(0).toInt() + 1; + } + + QProgressDialog progress(tr("Exporting result file..."), tr("Abort"), 0, progress_count + 1, this); + progress.setWindowModality(Qt::WindowModal); + + QDateTime start00 = eventPlugin()->stageStartDateTime(1); + qfDebug() << "creating table"; + //auto tt_classes = thisPlugin()->nLegsResultsTable("classes.name='D105'", 999, 999999, false); + auto tt_classes = thisPlugin()->nLegsResultsTable(QString(), 999, 999999, false); + progress.setValue(++progress_val); + QVariantList result_list{ + "ResultList", + QVariantMap{ + {"xmlns", "http://www.orienteering.org/datastandard/3.0"}, + {"status", "Complete"}, + {"iofVersion", "3.0"}, + {"creator", "QuickEvent"}, + {"createTime", datetime_to_string(QDateTime::currentDateTime())}, + } + }; + { + QVariantList event_lst{"Event"}; + QVariantMap event = tt_classes.value("event").toMap(); + event_lst.insert(event_lst.count(), QVariantList{"Id", QVariantMap{{"type", "ORIS"}}, event.value("importId")}); + event_lst.insert(event_lst.count(), QVariantList{"Name", event.value("name")}); + event_lst.insert(event_lst.count(), QVariantList{"StartTime", + QVariantList{"Date", event.value("date")}, + QVariantList{"Time", event.value("time")} + }); + event_lst.insert(event_lst.count(), + QVariantList{"Official", + QVariantMap{{"type", "director"}}, + QVariantList{"Person", + QVariantList{"Name", + QVariantList{"Family", event.value("director").toString().section(' ', 1, 1)}, + QVariantList{"Given", event.value("director").toString().section(' ', 0, 0)}, + } + }, + } + ); + event_lst.insert(event_lst.count(), + QVariantList{"Official", + QVariantMap{{"type", "mainReferee"}}, + QVariantList{"Person", + QVariantList{"Name", + QVariantList{"Family", event.value("mainReferee").toString().section(' ', 1, 1)}, + QVariantList{"Given", event.value("mainReferee").toString().section(' ', 0, 0)}, + } + }, + } + ); + result_list.insert(result_list.count(), event_lst); + } + for(int i=0; i 0) { + qfs::QueryBuilder qb; + qb.select2("runs", "startTimeMs, finishTimeMs, timeMs") + .from("runs").where("id=" + QString::number(run_id)); + qfs::Query q; + q.execThrow(qb.toString()); + if(q.next()) { + stime = q.value(0).toInt(); + ftime = q.value(1).toInt(); + time = q.value(2).toInt(); + } + else { + qfWarning() << "Cannot load run for id:" << run_id; + } + } + append_list(person_result, QVariantList{"StartTime", datetime_to_string(start00.addMSecs(stime))}); + append_list(person_result, QVariantList{"FinishTime", datetime_to_string(start00.addMSecs(ftime))}); + append_list(person_result, QVariantList{"Time", time}); + append_list(person_result, QVariantList{"Position", QVariantMap{{"type", "Leg"}}, tt_leg_row.value(QStringLiteral("pos"))}); + // MISSING position course append_list(person_result, QVariantList{"Position", QVariantMap{{"type", "course"}}, tt_laps_row.value(QStringLiteral("pos"))}); + append_list(person_result, QVariantList{"Status", tt_leg_row.value(QStringLiteral("status"))}); + QVariantList overall_result{"OverallResult"}; + { + append_list(overall_result, QVariantList{"Time", tt_leg_row.value(QStringLiteral("stime"))}); + append_list(overall_result, QVariantList{"Position", tt_leg_row.value(QStringLiteral("spos"))}); + append_list(overall_result, QVariantList{"Status", tt_leg_row.value(QStringLiteral("sstatus"))}); + // MISSING TimeBehind + } + append_list(person_result, overall_result); + int course_id = runsPlugin()->courseForRelay(relay_number, leg); + { + QF_TIME_SCOPE("exporting course: " + QString::number(course_id)); + QVariantList course{"Course"}; + append_list(course, QVariantList{"Id", course_id}); + { + qfs::QueryBuilder qb; + qb.select2("courses", "name, length, climb") + .from("courses").where("id=" + QString::number(course_id)); + qfs::Query q; + q.execThrow(qb.toString()); + if(q.next()) { + append_list(course, QVariantList{"Name", q.value(0)}); + append_list(course, QVariantList{"Length", q.value(1)}); + append_list(course, QVariantList{"Climb", q.value(2)}); + } + else { + qfWarning() << "Cannot load course for id:" << course_id; + } + } + append_list(person_result, course); + } + { + QF_TIME_SCOPE("exporting laps"); + qf::core::sql::QueryBuilder qb; + qb.select2("runlaps", "position, stpTimeMs") + .from("runlaps").where("runId=" + QString::number(run_id)) + .where("code >= " + QString::number(quickevent::core::CodeDef::PUNCH_CODE_MIN)) + .where("code <= " + QString::number(quickevent::core::CodeDef::PUNCH_CODE_MAX)) + .orderBy("position") ; + //qfInfo() << qb.toString(); + auto q = qf::core::sql::Query::fromExec(qb.toString()); + quickevent::core::CourseDef csd = runsPlugin()->courseForCourseId(course_id); + QVariantList codes = csd.codes(); + int sql_pos = -1; + for (int ix = 0; ix < codes.count(); ++ix) { + quickevent::core::CodeDef cd(codes[ix].toMap()); + int pos = ix + 1; + if(sql_pos < 0) { + if(q.next()) { + sql_pos = q.value(0).toInt(); + } + } + int time = 0; + if(pos == sql_pos) { + sql_pos = -1; + time = q.value(1).toInt(); + } + QVariantList split{QStringLiteral("SplitTime")}; + append_list(split, QVariantList{"ControlCode", cd.code()}); + append_list(split, QVariantList{"Time", time}); + append_list(person_result, split); + } + } + append_list(member_result, person_result); + append_list(team_result, member_result); + } + append_list(class_result, team_result); + } + progress.setValue(++progress_val); + if (progress.wasCanceled()) + break; + append_list(result_list, class_result); + } + qf::core::utils::HtmlUtils::FromXmlListOptions opts; + opts.setDocumentTitle(tr("Relays IOF-XML 3.0 results")); + QString str = qf::core::utils::HtmlUtils::fromXmlList(result_list, opts); + out_file.write(str.toUtf8()); + qfInfo() << "exported:" << out_file.fileName(); + progress.setValue(progress_count); +} + diff --git a/quickevent/app/plugins/Relays/src/relayswidget.h b/quickevent/app/plugins/Relays/src/relayswidget.h index 8937fa733..afa626ca8 100644 --- a/quickevent/app/plugins/Relays/src/relayswidget.h +++ b/quickevent/app/plugins/Relays/src/relayswidget.h @@ -26,7 +26,7 @@ class RelaysWidget : public QFrame private: typedef QFrame Super; public: - explicit RelaysWidget(QWidget *parent = 0); + explicit RelaysWidget(QWidget *parent = nullptr); ~RelaysWidget() Q_DECL_OVERRIDE; void settleDownInPartWidget(ThisPartWidget *part_widget); @@ -51,6 +51,8 @@ class RelaysWidget : public QFrame void print_results_nlegs(); void print_results_overal(); + void export_results_iofxml3(); + void printResults(const QString &settings_id, const QVariantMap &default_options); private: Ui::RelaysWidget *ui; diff --git a/quickevent/app/plugins/Runs/src/runsplugin.cpp b/quickevent/app/plugins/Runs/src/runsplugin.cpp index 8b2aaea25..75d8e48b4 100644 --- a/quickevent/app/plugins/Runs/src/runsplugin.cpp +++ b/quickevent/app/plugins/Runs/src/runsplugin.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -63,24 +64,7 @@ static qf::qmlwidgets::framework::Plugin* competitorsPlugin() static QString datetime_to_string(const QDateTime &dt) { - QString ret = dt.toString(Qt::ISODate); - if(dt.timeSpec() == Qt::LocalTime) { - int offset_min = dt.offsetFromUtc(); - if(offset_min == 0) { - ret += 'Z'; - } - else { - if(offset_min < 0) { - ret += '-'; - offset_min = -offset_min; - } - else { - ret += '+'; - } - ret += QStringLiteral("%1:%2").arg(offset_min / (60 * 60), 2, 10, QChar('0')).arg((offset_min / 60) % 60, 2, 10, QChar('0')); - } - } - return ret; + return quickevent::core::Utils::dateTimeToIsoStringWithUtcOffset(dt); } RunsPlugin::RunsPlugin(QObject *parent) @@ -201,7 +185,7 @@ static int latlng_distance(double lat1, double lng1, double lat2, double lng2) return static_cast(std::ceil(d)); } -QVariantMap RunsPlugin::courseCodesForRunId(int run_id, bool including_distance) +QVariantMap RunsPlugin::courseCodesForRunId(int run_id) { qfLogFuncFrame() << "run id:" << run_id; if(run_id <= 0) { @@ -209,10 +193,10 @@ QVariantMap RunsPlugin::courseCodesForRunId(int run_id, bool including_distance) return QVariantMap(); } int course_id = courseForRun(run_id); - return courseForCourseId(course_id, including_distance); + return courseForCourseId(course_id); } -quickevent::core::CourseDef RunsPlugin::courseForCourseId(int course_id, bool including_distance) +quickevent::core::CourseDef RunsPlugin::courseForCourseId(int course_id) { qfLogFuncFrame() << "course id:" << course_id; quickevent::core::CourseDef ret; @@ -265,7 +249,7 @@ quickevent::core::CourseDef RunsPlugin::courseForCourseId(int course_id, bool in // whatever code is imported, QE is using 999 everywhere finish_code.setCode(quickevent::core::CodeDef::FINISH_PUNCH_CODE); - if(including_distance) { + /*if(including_distance)*/ { int course_len = 0; quickevent::core::CodeDef prev_cd = start_code; for (int i = 0; i < codes.count(); ++i) { @@ -326,18 +310,9 @@ int RunsPlugin::courseForRun_Relays(int run_id) qfs::Query q; q.exec(qb.toString(), qf::core::Exception::Throw); if(q.next()) { - QString relay_num = q.value("number").toString(); - QString leg = q.value("leg").toString(); - q.exec("SELECT id FROM courses WHERE name='" + relay_num + '.' + leg + "'"); - int cnt = 0; - while (q.next()) { - if(cnt > 0) { - qfError() << "more courses found for run_id:" << run_id; - return 0; - } - ret = q.value(0).toInt(); - cnt++; - } + int relay_num = q.value("number").toInt(); + int leg = q.value("leg").toInt(); + ret = courseForRelay(relay_num, leg); } else { qfError() << "Cannot find relays record for run id:" << run_id; @@ -345,6 +320,24 @@ int RunsPlugin::courseForRun_Relays(int run_id) return ret; } +int RunsPlugin::courseForRelay(int relay_number, int leg) +{ + int ret = 0; + auto q = qfs::Query::fromExec(QStringLiteral("SELECT id FROM courses WHERE name='%1.%2'").arg(relay_number).arg(leg)); + int cnt = 0; + while (q.next()) { + if(cnt > 0) { + qfError() << "more courses found for relay:" << relay_number << "leg:" << leg; + return 0; + } + ret = q.value(0).toInt(); + cnt++; + } + if(!ret) + qfWarning() << "Cannot find course for relay:" << relay_number << "leg:" << leg; + return ret; +} + void RunsPlugin::showRunsTable(int stage_id, int class_id, bool show_offrace, const QString &sort_column, int select_competitor_id) { auto *w = new RunsTableDialogWidget(); @@ -674,7 +667,7 @@ qf::core::utils::TreeTable RunsPlugin::addLapsToStageResultsTable(int course_id, { qf::core::utils::TreeTable tt = class_results; int col_stp_time0_ix = tt.columnCount(); - quickevent::core::CourseDef course = courseForCourseId(course_id, false); + quickevent::core::CourseDef course = courseForCourseId(course_id); tt.setValue("course", course); QVariantList course_codes = course.codes(); quickevent::core::CodeDef finish_code = course.finishCode(); @@ -858,10 +851,10 @@ bool RunsPlugin::exportResultsIofXml30Stage(int stage_id, const QString &file_na QVariantList{"Climb", tt1_row.value(QStringLiteral("courses.climb")) }, } ); - qf::core::utils::TreeTable tt2 = tt1.row(i).table(); + qf::core::utils::TreeTable tt2 = tt1_row.table(); //int pos = 0; int course_id = tt1_row.value(QStringLiteral("courses.id")).toInt(); - quickevent::core::CourseDef course_def = courseForCourseId(course_id, false); + quickevent::core::CourseDef course_def = courseForCourseId(course_id); QVariantList codes = course_def.codes(); //codes << course_def.finishCode(); IOFXML does not require finish lap time, can be coputed from finish time int stpTime_0_ix = tt2.columnIndex(QStringLiteral("stpTime_0")); diff --git a/quickevent/app/plugins/Runs/src/runsplugin.h b/quickevent/app/plugins/Runs/src/runsplugin.h index 81dc5f4b4..1e312d733 100644 --- a/quickevent/app/plugins/Runs/src/runsplugin.h +++ b/quickevent/app/plugins/Runs/src/runsplugin.h @@ -54,8 +54,9 @@ class RUNSPLUGIN_DECL_EXPORT RunsPlugin : public qf::qmlwidgets::framework::Plug Q_SLOT void clearRunnersTableCache(); Q_INVOKABLE int courseForRun(int run_id); - Q_INVOKABLE QVariantMap courseCodesForRunId(int run_id, bool including_distance); - Q_INVOKABLE quickevent::core::CourseDef courseForCourseId(int course_id, bool including_distance); + int courseForRelay(int relay_number, int leg); + Q_INVOKABLE QVariantMap courseCodesForRunId(int run_id); + Q_INVOKABLE quickevent::core::CourseDef courseForCourseId(int course_id); Q_INVOKABLE int cardForRun(int run_id); qf::core::utils::TreeTable currentStageResultsTable(const QString &class_filter = QString(), int max_competitors_in_class = 0, bool exclude_disq = false); diff --git a/quickevent/app/quickevent/src/main.cpp b/quickevent/app/quickevent/src/main.cpp index 670c56cb0..4b60ec3a9 100644 --- a/quickevent/app/quickevent/src/main.cpp +++ b/quickevent/app/quickevent/src/main.cpp @@ -36,6 +36,7 @@ int main(int argc, char *argv[]) if(o_log_file.isEmpty()) o_log_file = QDir::tempPath() + "/quickevent.log"; + qf::core::LogDevice::setDefinedCategories(QStringList() << "TimeScope"); QStringList args = qf::core::LogDevice::setGlobalTresholds(argc, argv); QScopedPointer stderr_log_device(qf::core::FileLogDevice::install()); QScopedPointer file_log_device(qf::core::FileLogDevice::install());