QVariantMap ReceiptsPlugin::readCardTablesData(int card_id) { qfLogFuncFrame() << card_id; QVariantMap ret; CardReader::ReadCard read_card = cardReaderPlugin()->readCard(card_id); { qfu::TreeTable tt; tt.appendColumn("position", QVariant::Int); tt.appendColumn("code", QVariant::Int); tt.appendColumn("punchTimeMs", QVariant::Int); tt.appendColumn("stpTimeMs", QVariant::Int); tt.appendColumn("lapTimeMs", QVariant::Int); QMapIterator<QString, QVariant> it(read_card); while(it.hasNext()) { it.next(); if(it.key() != QLatin1String("punches")) tt.setValue(it.key(), it.value()); } int position = 0; int start_time_ms = read_card.startTime(); if(start_time_ms == 0xeeee) start_time_ms = read_card.checkTime(); start_time_ms *= 1000; int prev_stp_time_ms = 0; for(auto v : read_card.punches()) { CardReader::ReadPunch punch(v.toMap()); int punch_time_ms = punch.time() * 1000 + punch.msec(); int stp_time_ms = quickevent::og::TimeMs::msecIntervalAM(start_time_ms, punch_time_ms); qfu::TreeTableRow ttr = tt.appendRow(); ++position; int code = punch.code(); ttr.setValue("position", position); ttr.setValue("code", code); ttr.setValue("punchTimeMs", punch_time_ms); ttr.setValue("stpTimeMs", stp_time_ms); ttr.setValue("lapTimeMs", stp_time_ms - prev_stp_time_ms); prev_stp_time_ms = stp_time_ms; } { qf::core::sql::QueryBuilder qb; qb.select2("config", "ckey, cvalue, ctype") .from("config") .where("ckey LIKE 'event.%'"); qf::core::sql::Query q; q.exec(qb.toString()); while(q.next()) { QVariant v = qf::core::Utils::retypeStringValue(q.value("cvalue").toString(), q.value("ctype").toString()); tt.setValue(q.value("ckey").toString(), v); } } tt.setValue("stageCount", eventPlugin()->stageCount()); tt.setValue("currentStageId", eventPlugin()->currentStageId()); qfDebug() << "card:\n" << tt.toString(); ret["card"] = tt.toVariant(); } return ret; }
bool ResultsExporter::exportResults() { ResultsExporterSettings ss = settings(); if(!QDir().mkpath(ss.exportDir())) { qfError() << "Cannot create export dir:" << ss.exportDir(); return false; } qfInfo() << "ResultsExporter export dir:" << ss.exportDir(); if(ss.outputFormat() == static_cast<int>(ResultsExporterSettings::OutputFormat::CSOS)) { int current_stage = eventPlugin()->currentStageId(); QString fn = ss.exportDir() + "/results-csos.txt"; runsPlugin()->exportResultsCsosStage(current_stage, fn); return true; } if(ss.outputFormat() == static_cast<int>(ResultsExporterSettings::OutputFormat::IofXml3)) { int current_stage = eventPlugin()->currentStageId(); QString fn = ss.exportDir() + "/results-csos.txt"; runsPlugin()->exportResultsIofXml30Stage(current_stage, fn); return true; } else if(ss.outputFormat() == static_cast<int>(ResultsExporterSettings::OutputFormat::HtmlMulti)) { quickevent::core::exporters::StageResultsHtmlExporter exp; exp.setOutDir(ss.exportDir()); exp.generateHtml(); QString cmd = ss.whenFinishedRunCmd(); if(!cmd.isEmpty()) { qfInfo() << "Starting process:" << cmd; QProcess *proc = new QProcess(); connect(proc, &QProcess::readyReadStandardOutput, [proc]() { QByteArray ba = proc->readAllStandardOutput(); qfInfo().noquote() << "PROC stdout:" << ba; }); connect(proc, &QProcess::readyReadStandardError, [proc]() { QByteArray ba = proc->readAllStandardError(); qfWarning().noquote() << "PROC stderr:" << ba; }); connect(proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [proc](int exit_code, QProcess::ExitStatus exit_status) { if(exit_status == QProcess::ExitStatus::CrashExit) qfError() << "PROC crashed"; else qfInfo() << "PROC finished with exit code:" << exit_code; proc->deleteLater(); }); proc->start(cmd); } return true; } qfError() << "Unsupported output format:" << ss.outputFormat(); return false; }
void ReceiptsWidget::settleDownInPartWidget(ReceiptsPartWidget *part_widget) { connect(part_widget, SIGNAL(resetPartRequest()), this, SLOT(reset())); connect(part_widget, SIGNAL(reloadPartRequest()), this, SLOT(reset())); connect(eventPlugin(), &Event::EventPlugin::dbEventNotify, this, &ReceiptsWidget::onDbEventNotify, Qt::QueuedConnection); }
bool ClassDocument::saveData() { RecordEditMode old_mode = mode(); bool ret = Super::saveData(); //Log.info("CompetitorDocument", saveData_qml, "ret:", ret, "old_mode", old_mode, "vs", DataDocument.ModeInsert, old_mode == DataDocument.ModeInsert); if(ret) { if(old_mode == DataDocument::ModeInsert) { // insert classdefs int class_id = dataId().toInt(); //int si_id = value("competitors.siId").toInt(); auto *event_plugin = eventPlugin(); QF_ASSERT(event_plugin != nullptr, "invalid Event plugin type", return false); int stage_count = event_plugin->stageCount(); qf::core::sql::Query q(model()->connectionName()); q.prepare("INSERT INTO classdefs (classId, stageId) VALUES (:classId, :stageId)"); for(int i=0; i<stage_count; i++) { q.bindValue(":classId", class_id); q.bindValue(":stageId", i + 1); if(!q.exec()) { qfError() << q.lastError().text(); break; } } } }
void ReceiptsWidget::settleDownInPartWidget(ReceiptsPartWidget *part_widget) { connect(part_widget, SIGNAL(resetPartRequest()), this, SLOT(reset())); connect(part_widget, SIGNAL(reloadPartRequest()), this, SLOT(reset())); connect(eventPlugin(), SIGNAL(dbEventNotify(QString,QVariant)), this, SLOT(onDbEventNotify(QString,QVariant)), Qt::QueuedConnection); }
void ReceiptsWidget::reset() { if(!eventPlugin()->isEventOpen()) { m_cardsModel->clearRows(); return; } reload(); }
void ReceiptsWidget::reset() { if(eventPlugin()->eventName().isEmpty()) { m_cardsModel->clearRows(); return; } reload(); }
void RelaysPlugin::onInstalled() { qff::MainWindow *fwk = qff::MainWindow::frameWork(); m_partWidget = new ThisPartWidget(); fwk->addPartWidget(m_partWidget, manifest()->featureId()); connect(eventPlugin(), &Event::EventPlugin::dbEventNotify, this, &RelaysPlugin::onDbEventNotify); emit nativeInstalled(); }
qf::core::utils::TreeTable RelaysPlugin::nlegsResultsTable(int leg_count, int places, bool exclude_not_finish) { qf::core::utils::TreeTable tt; tt.setValue("event", eventPlugin()->eventConfig()->value("event")); tt.setValue("stageStart", eventPlugin()->stageStartDateTime(1)); tt.appendColumn("className", QVariant::String); qfs::QueryBuilder qb; qb.select2("classes", "id, name") .from("classes") .orderBy("classes.name"); qfs::Query q; q.execThrow(qb.toString()); while(q.next()) { qf::core::utils::TreeTableRow rr = tt.appendRow(); rr.setValue("className", q.value("classes.name")); qf::core::utils::TreeTable tt2 = nlegsResultsTable(q.value("classes.id").toInt(), leg_count, places, exclude_not_finish); rr.appendTable(tt2); } return tt; }
void ClassesWidget::reset() { { m_cbxStage->blockSignals(true); m_cbxStage->clear(); for(int i=0; i<eventPlugin()->stageCount(); i++) m_cbxStage->addItem(tr("E%1").arg(i+1), i+1); m_cbxStage->blockSignals(false); connect(m_cbxStage, SIGNAL(currentIndexChanged(int)), this, SLOT(reload()), Qt::UniqueConnection); } reload(); }
void ClassesWidget::reload() { if(eventPlugin()->eventName().isEmpty()) { m_classesModel->clearRows(); m_courseCodesModel->clearRows(); return; } int stage_id = selectedStageId(); { qf::core::sql::QueryBuilder qb1; qb1.select("COUNT(*)") .from("runs") .join("runs.competitorId", "competitors.id") .where("competitors.classId=classdefs.classId") .where("NOT runs.offRace") .where("runs.stageId=" QF_IARG(stage_id)); qfs::QueryBuilder qb; qb.select2("classes", "*") .select2("classdefs", "*") .select2("courses", "id, name, length, climb") .select("(" + qb1.toString() + ") AS runsCount") .from("classes") .joinRestricted("classes.id", "classdefs.classId", "classdefs.stageId=" QF_IARG(stage_id)) .join("classdefs.courseId", "courses.id") .orderBy("classes.name");//.limit(10); /* int class_id = m_cbxClasses->currentData().toInt(); if(class_id > 0) { qb.where("competitors.classId=" + QString::number(class_id)); } */ m_classesModel->setQueryBuilder(qb); m_classesModel->reload(); } { qf::core::sql::Query q(m_classesModel->sqlConnection()); q.exec("SELECT COUNT(*) FROM classdefs WHERE stageId=" QF_IARG(stage_id)); bool ro = true; if(q.next()) ro = (q.value(0).toInt() == 0); if(ro) { qfWarning() << tr("Courses are not imported, class table is read only."); } ui->tblClasses->setReadOnly(ro); } reloadCourseCodes(); }
void RunsTableDialogWidget::reload(int stage_id, int class_id, const QString &sort_column, int select_competitor_id) { QString class_name = eventPlugin()->classNameById(class_id); setTitle(tr("Stage %1 Class %2").arg(stage_id).arg(class_name)); runsTableWidget()->reload(stage_id, class_id, sort_column, select_competitor_id); }
int CardChecker::cardCheckCheckTimeSec() { return eventPlugin()->eventConfig()->cardCheckCheckTimeSec(); }
int CardChecker::stageIdForRun(int run_id) { return eventPlugin()->stageIdForRun(run_id); }
QVariantMap ReceiptsPlugin::receiptTablesData(int card_id) { qfLogFuncFrame() << card_id; QF_TIME_SCOPE("receiptTablesData()"); QVariantMap ret; CardReader::ReadCard read_card = cardReaderPlugin()->readCard(card_id); CardReader::CheckedCard checked_card = cardReaderPlugin()->checkCard(read_card); int current_stage_id = eventPlugin()->currentStageId(); int run_id = checked_card.runId(); int course_id = checked_card.courseId(); int current_standings = 0; int competitors_finished = 0; QMap<int, int> best_laps; //< position->time ///QMap<int, int> missing_codes; //< pos->code ///QSet<int> out_of_order_codes; { qf::core::model::SqlTableModel model; qf::core::sql::QueryBuilder qb; qb.select2("competitors", "*") .select2("runs", "*") .select2("classes", "name") .select("COALESCE(competitors.lastName, '') || ' ' || COALESCE(competitors.firstName, '') AS competitorName") .from("runs") .join("runs.competitorId", "competitors.id") .join("competitors.classId", "classes.id") .where("runs.id=" QF_IARG(run_id)); model.setQuery(qb.toString()); model.reload(); if(model.rowCount() == 1) { int class_id = model.value(0, "competitors.classId").toInt(); { // find best laps for competitors class qf::core::sql::QueryBuilder qb_minlaps; // TODO: remove position field from DB in 0.1.5 qb_minlaps.select("runlaps.position, MIN(runlaps.lapTimeMs) AS minLapTimeMs") .from("competitors") .joinRestricted("competitors.id", "runs.competitorId", "runs.stageId=" QF_IARG(current_stage_id) " AND competitors.classId=" QF_IARG(class_id), "JOIN") .joinRestricted("runs.id", "runlaps.runId", "runlaps.position > 0 AND runlaps.lapTimeMs > 0", "JOIN") .groupBy("runlaps.position"); QString qs = qb_minlaps.toString(); //qfInfo() << qs; qf::core::sql::Query q; q.exec(qs); while(q.next()) { int position = q.value("position").toInt(); if(position == 0) { qfWarning() << "position == 0 in best runlaps"; continue; } int lap = q.value("minLapTimeMs").toInt(); if(lap == 0) { qfWarning() << "minLapTimeMs == 0 in best runlaps"; continue; } best_laps[position] = lap; //qfInfo() << "bestlaps[" << pos << "] =" << lap; } } if(checked_card.isOk()) { // find current standings qf::core::sql::QueryBuilder qb; qb.select2("runs", "timeMs") .select("runs.disqualified OR NOT runs.isRunning OR runs.isRunning IS NULL OR runs.misPunch AS dis") .from("competitors") .joinRestricted("competitors.id", "runs.competitorId", "runs.stageId=" QF_IARG(current_stage_id) " AND competitors.classId=" QF_IARG(class_id)) .where("runs.finishTimeMs > 0") .orderBy("misPunch, disqualified, isRunning, runs.timeMs"); //qfInfo() << qb.toString(); qf::core::sql::Query q; q.exec(qb.toString(), qf::core::Exception::Throw); while (q.next()) { bool dis = q.value("dis").toBool(); int time = q.value("timeMs").toInt(); if(!dis) { if(time <= checked_card.timeMs()) current_standings++; } competitors_finished++; } } } qfu::TreeTable tt = model.toTreeTable(); { qf::core::sql::QueryBuilder qb; qb.select2("courses", "length, climb") .select("(SELECT COUNT(*) FROM coursecodes WHERE courseId=courses.id) AS controlCount") .from("courses") .where("courses.id=" QF_IARG(course_id)); qf::core::sql::Query q; q.exec(qb.toString()); if(q.next()) { QSqlRecord rec = q.record(); for (int i = 0; i < rec.count(); ++i) { QString fld_name = rec.fieldName(i); tt.setValue(fld_name, rec.value(i)); } } } { qf::core::sql::QueryBuilder qb; qb.select2("config", "ckey, cvalue, ctype") .from("config") .where("ckey LIKE 'event.%'"); qf::core::sql::Query q; q.exec(qb.toString()); while(q.next()) { QVariant v = qf::core::Utils::retypeStringValue(q.value("cvalue").toString(), q.value("ctype").toString()); tt.setValue(q.value("ckey").toString(), v); } } tt.setValue("stageCount", eventPlugin()->stageCount()); tt.setValue("currentStageId", eventPlugin()->currentStageId()); qfDebug() << "competitor:\n" << tt.toString(); ret["competitor"] = tt.toVariant(); } { qfu::TreeTable tt; tt.appendColumn("position", QVariant::Int); tt.appendColumn("code", QVariant::Int); tt.appendColumn("stpTimeMs", QVariant::Int); tt.appendColumn("lapTimeMs", QVariant::Int); tt.appendColumn("lossMs", QVariant::Int); QMapIterator<QString, QVariant> it(checked_card); while(it.hasNext()) { it.next(); if(it.key() != QLatin1String("punches")) tt.setValue(it.key(), it.value()); } tt.setValue("isOk", checked_card.isOk()); int position = 0; for(auto v : checked_card.punches()) { CardReader::CheckedPunch punch(v.toMap()); qfu::TreeTableRow ttr = tt.appendRow(); ++position; int code = punch.code(); ttr.setValue("position", position); ttr.setValue("code", code); ttr.setValue("stpTimeMs", punch.stpTimeMs()); int lap = punch.lapTimeMs(); ttr.setValue("lapTimeMs", lap); int best_lap = best_laps.value(position); if(lap > 0 && best_lap > 0) { int loss = lap - best_lap; ttr.setValue("lossMs", loss); } } /* { // runlaps table contains also finish time entry, it is under FINISH_PUNCH_POS // currently best_laps[999] contains best finish lap time for this class int loss = 0; int best_lap = best_laps.value(CardReader::CardReaderPlugin::FINISH_PUNCH_POS); if(best_lap > 0) loss = checked_card.finishLapTimeMs() - best_lap; //qfInfo() << "control_count:" << control_count << "finishLapTimeMs:" << checked_card.finishLapTimeMs() << "- best_lap:" << best_lap << "=" << loss; tt.setValue("finishLossMs", loss); } */ { QSet<int> correct_codes; for (int i = 0; i < checked_card.punchCount(); ++i) { correct_codes << checked_card.punchAt(i).code(); } QVariantList xc; for (int i = 0; i < read_card.punchCount(); ++i) { int code = read_card.punchAt(i).code(); if(!correct_codes.contains(code)) { xc.insert(xc.count(), QVariantList() << (i+1) << code); } } tt.setValue("extraCodes", xc); } tt.setValue("currentStandings", current_standings); tt.setValue("competitorsFinished", competitors_finished); tt.setValue("timeMs", checked_card.timeMs()); qfDebug() << "card:\n" << tt.toString(); ret["card"] = tt.toVariant(); } return ret; }
RacomClient::RacomClient(QObject *parent) : Super(RacomClient::serviceName(), parent) { connect(eventPlugin(), &Event::EventPlugin::dbEventNotify, this, &RacomClient::onDbEventNotify, Qt::QueuedConnection); }