Skip to content

Commit

Permalink
Merge pull request Quick-Event#982 from arnost00/relay-import-start-n…
Browse files Browse the repository at this point in the history
…umbers

Relay import start numbers
  • Loading branch information
fvacek authored Oct 7, 2024
2 parents f68f887 + cbb8451 commit 67da9a3
Show file tree
Hide file tree
Showing 13 changed files with 4,820 additions and 2,346 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@
<item row="15" column="1">
<widget class="QCheckBox" name="ed_iofRace">
<property name="toolTip">
<string>IOF Eventor race - user for IOF XML exports</string>
<string>IOF Eventor race - used for IOF XML exports</string>
</property>
<property name="text">
<string>Is IOF Race (data from Eventor)</string>
Expand All @@ -368,7 +368,7 @@
<item row="16" column="1">
<widget class="QSpinBox" name="ed_xmlRaceNumber">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;IOF Eventor race number - if nonzero, user as &amp;lt;Race&amp;gt; node and for attribute of &amp;lt;Start&amp;gt; node for IOF XML exports&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;IOF Eventor race number - if nonzero, used as &amp;lt;Race&amp;gt; node and for attribute of &amp;lt;Start&amp;gt; node for IOF XML exports&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
Expand Down
8 changes: 4 additions & 4 deletions quickevent/app/quickevent/plugins/Oris/src/txtimporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ void TxtImporter::importCompetitorsCSV()
qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork();
qf::qmlwidgets::dialogs::MessageBox mbx(fwk);
mbx.setIcon(QMessageBox::Information);
mbx.setText(tr("Import comma separated values UTF8 text files with header."));
mbx.setText(tr("Import UTF8 text file with comma separated values with first row as header.<br/>Separator is comma(,)"));
mbx.setInformativeText(tr("Each row should have following columns: "
"<ol>"
"<li>Registration</li>"
Expand Down Expand Up @@ -262,7 +262,7 @@ void TxtImporter::importRunsCzeCSV()
qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork();
qf::qmlwidgets::dialogs::MessageBox mbx(fwk);
mbx.setIcon(QMessageBox::Information);
mbx.setText(tr("Import comma separated values UTF8 text files with header.<br/>Separator is semicolon(;).<br/>Updates only existing runners (key is Czech registration)."));
mbx.setText(tr("Import UTF8 text file with comma separated values with first row as header.<br/>Separator is semicolon(;).<br/>Updates only existing runners (key is Czech registration)."));
mbx.setInformativeText(tr("Each row should have following columns: "
"<ol>"
"<li>Registration <i>- key</i></li>"
Expand Down Expand Up @@ -376,7 +376,7 @@ void TxtImporter::importRunsIdCSV()
qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork();
qf::qmlwidgets::dialogs::MessageBox mbx(fwk);
mbx.setIcon(QMessageBox::Information);
mbx.setText(tr("Import comma separated values UTF8 text files with header.<br/>Separator is semicolon(;).<br/>Updates only existing runners (key is <b>id</b> in module(table) <b>runs</b>)."));
mbx.setText(tr("Import UTF8 text file with comma separated values with first row as header.<br/>Separator is semicolon(;).<br/>Updates only existing runners (key is <b>id</b> in module(table) <b>runs</b>)."));
mbx.setInformativeText(tr("Each row should have following columns: "
"<ol>"
"<li>Runs Id <i>- key</i></li>"
Expand Down Expand Up @@ -490,7 +490,7 @@ void TxtImporter::importRunsIofCSV()
qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork();
qf::qmlwidgets::dialogs::MessageBox mbx(fwk);
mbx.setIcon(QMessageBox::Information);
mbx.setText(tr("Import comma separated values UTF8 text files with header.<br/>Separator is semicolon(;).<br/>Updates only existing runners (key is IOF ID)."));
mbx.setText(tr("Import UTF8 text file with comma separated values with first row as header.<br/>Separator is semicolon(;).<br/>Updates only existing runners (key is IOF ID)."));
mbx.setInformativeText(tr("Each row should have following columns: "
"<ol>"
"<li>IOF ID <i>- key</i></li>"
Expand Down
4 changes: 2 additions & 2 deletions quickevent/app/quickevent/plugins/Oris/src/xmlimporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ bool XmlImporter::importEntries(QXmlStreamReader &reader, const XmlCreators crea
it++;
}
bool ok;
QString item = QInputDialog::getItem(qf::qmlwidgets::framework::MainWindow::frameWork(), tr("Select which race import)"),
QString item = QInputDialog::getItem(qf::qmlwidgets::framework::MainWindow::frameWork(), tr("Select which race import"),
tr("Races:"), items, 0, false, &ok);
if (ok && !item.isEmpty())
selected_race = races[item];
Expand Down Expand Up @@ -778,7 +778,7 @@ bool XmlImporter::importEvent(QXmlStreamReader &reader, const XmlCreators creato
it++;
}
bool ok;
QString item = QInputDialog::getItem(qf::qmlwidgets::framework::MainWindow::frameWork(), tr("Select which race import)"),
QString item = QInputDialog::getItem(qf::qmlwidgets::framework::MainWindow::frameWork(), tr("Select which race import"),
tr("Races:"), items, 0, false, &ok);
if (ok && !item.isEmpty())
event_race = races[item];
Expand Down
128 changes: 128 additions & 0 deletions quickevent/app/quickevent/plugins/Relays/src/relayswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <qf/core/sql/transaction.h>
#include <qf/core/assert.h>
#include <qf/core/utils/treetable.h>
#include <qf/core/utils/csvreader.h>
#include <QCheckBox>
#include <QInputDialog>
#include <QLabel>
Expand Down Expand Up @@ -146,6 +147,11 @@ void RelaysWidget::settleDownInPartWidget(quickevent::gui::PartWidget *part_widg
a_relays->addActionInto(a);
connect(a, &qfw::Action::triggered, this, &RelaysWidget::relays_assignNumbers);
}
{
qfw::Action *a = new qfw::Action("importBibs", tr("&Import bibs from CSV"));
a_relays->addActionInto(a);
connect(a, &qfw::Action::triggered, this, &RelaysWidget::relays_importBibs);
}

qfw::Action *a_print = part_widget->menuBar()->actionForPath("print");
a_print->setText(tr("&Print"));
Expand Down Expand Up @@ -552,3 +558,125 @@ void RelaysWidget::export_results_iofxml3() {
QString fn = getPlugin<EventPlugin>()->eventName() + ".results.iof30.xml";
save_xml_file(getPlugin<RelaysPlugin>()->resultsIofXml30(), fn);
}

void RelaysWidget::relays_importBibs() {
qf::qmlwidgets::framework::MainWindow *fwk = qf::qmlwidgets::framework::MainWindow::frameWork();
qf::qmlwidgets::dialogs::MessageBox mbx(fwk);
mbx.setIcon(QMessageBox::Information);
mbx.setText(tr("Import UTF8 text file with comma separated values with first row as header.<br/>Separator is semicolon(;).<br/>Updates only existing relays (key is Club, Relay Name & Class)."));
mbx.setInformativeText(tr("Each row should have following columns: "
"<ol>"
"<li>Club abbr <i>- key (part1)</i></li>"
"<li>Relay name <i>- key (part2)</i></li>"
"<li>Start number (Bib)</li>"
"<li>Class (Optional - if not filed, trying to guess from the starting number)</li>"
"</ol>"));

mbx.setDoNotShowAgainPersistentKey("importRelaysBibsCSV");
int res = mbx.exec();
if(res != QMessageBox::Ok)
return;
QString fn = qfd::FileDialog::getOpenFileName(fwk, tr("Open file"), QString(), tr("CSV files (*.csv *.txt)"));
if(fn.isEmpty())
return;

QMap<QString, int> classes_map; // classes.name->classes.id
std::map<int, int> classes_map_bibs; // classes.name->classes.id
qf::core::sql::Query q;
q.exec("SELECT classes.id, relayStartNumber, name FROM classdefs JOIN classes ON classdefs.classid = classes.id WHERE stageId=1", qf::core::Exception::Throw);
while(q.next()) {
classes_map[q.value(2).toString()] = q.value(0).toInt();
classes_map_bibs[q.value(1).toInt()] = q.value(0).toInt();
}

try {
QFile f(fn);
if(!f.open(QFile::ReadOnly))
QF_EXCEPTION(tr("Cannot open file '%1' for reading.").arg(fn));
QTextStream ts(&f);
qf::core::utils::CSVReader reader(&ts);
reader.setSeparator(';');
enum {ColRelClub = 0, ColRelName, ColBib, ColClass};

qfLogScope("importRelaysBibsCSV");
qf::core::sql::Transaction transaction;
qf::core::sql::Query q2;
q.prepare("SELECT id FROM relays WHERE club=:club AND name=:name AND classId=:classId", qf::core::Exception::Throw);
q2.prepare("UPDATE relays SET number=:number WHERE id=:id", qf::core::Exception::Throw);

int n = 0;
int i = 0;
QSet<int> loaded_numbers;

while (!ts.atEnd()) {
QStringList line = reader.readCSVLineSplitted();
if(line.count() <= 1)
QF_EXCEPTION(tr("Fields separation error, invalid CSV format, Error reading CSV line: [%1]").arg(line.join(';').mid(0, 100)));
if(n++ == 0) { // skip first row (header)
qfDebug() << "Import CSV - skip header line";
continue;
}
QString relay_club = line.value(ColRelClub).trimmed();
QString relay_name = line.value(ColRelName).trimmed();
int relay_bib = line.value(ColBib).toInt();
QString relay_class = line.value(ColClass).trimmed();
if(relay_club.isEmpty() || relay_name.isEmpty()) {
QF_EXCEPTION(tr("Error reading CSV line: [%1]").arg(line.join(';')));
}
int class_id = -1;
if (relay_class.isEmpty() && relay_bib > 0) {
// guess class from bib number
for (auto&item : classes_map_bibs)
{
if (item.first <= relay_bib)
class_id = item.second;
}
if (class_id == -1)
QF_EXCEPTION(tr("Cannot guess class name from bib: '%1'").arg(relay_bib));
}
else if (!relay_class.isEmpty() && relay_bib >= 0){
class_id = classes_map.value(relay_class,-1);
if(class_id == -1)
QF_EXCEPTION(tr("Undefined class name: '%1'").arg(relay_class));
}
else {
if (relay_bib == 0)
qfWarning() << "Import CSV line" << n << "with" << relay_club << relay_name <<", cannot update, bib number 0 without class name";
else
qfWarning() << "Import CSV line" << n << "with" << relay_club << relay_name <<", cannot update, bib number"<< relay_bib <<"is negative";
}
if (relay_bib > 0) { // zero is for clear bib, negative is ignored
if (loaded_numbers.contains(relay_bib))
qfWarning() << "Import CSV line" << n << "with" << relay_club << relay_name <<", duplicate bib number"<< relay_bib;
else
loaded_numbers.insert(relay_bib);
}

q.bindValue(":club", relay_club);
q.bindValue(":name", relay_name);
q.bindValue(":classId", class_id);

q.exec(qf::core::Exception::Throw);
if(q.next()) {
// if club & name found in db - start update data
int relay_id = q.value(0).toInt();

if (relay_bib != 0) {
q2.bindValue(":number", relay_bib);
q2.bindValue(":id", relay_id);
q2.exec(qf::core::Exception::Throw);
i++;
qfDebug() << "Import CSV line" << n << "with" << relay_club << relay_name <<"bib"<< relay_bib << "["<< relay_class << "|"<< class_id << "].";
}
}
else
qfWarning() << "CSV line" << n << "with" << relay_club << relay_name <<"bib"<< relay_bib << "["<< relay_class << "|"<< class_id << "] not found in database.";
}
transaction.commit();
qfInfo() << fn << "Imported"<< i << "of" << n-1 << "data lines"; // -1 is header
QMessageBox::information(this, tr("Information"), QString(tr("Import file finished. Imported %1 of %2 lines\n\nPress refresh button to show imported data.").arg(i).arg(n-1)));
}
catch (const qf::core::Exception &e) {
qf::qmlwidgets::dialogs::MessageBox::showException(fwk, e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class RelaysWidget : public QFrame
QVariant startListByClubsTableData();

void relays_assignNumbers();
void relays_importBibs();

void print_start_list_classes();
void print_start_list_clubs();
Expand Down
Loading

0 comments on commit 67da9a3

Please sign in to comment.