From 18b0aa04161fb57e2f21ec715e294d3a96f56613 Mon Sep 17 00:00:00 2001 From: Michael Morgan <84428382+aa5sh@users.noreply.github.com> Date: Tue, 17 Mar 2026 23:49:43 -0400 Subject: [PATCH 1/3] Add LoTW DXCC credits import and UI option Add support for downloading and importing LoTW DXCC credit records. Introduces a LOTW_DXCC enum and a new LogFormat::runQSOCreditImport() that matches QSOs by callsign/band/date and updates credit fields (credit_submitted/granted and app_lotw_credit_submitted/granted). ADI mapping and export params updated to include app_lotw_credit_* fields. LotwQSLDownloader gains a LotwDXCCCredits flag and uses a separate DXCC_CREDIT_API endpoint and parameter set; it calls runQSOCreditImport when DXCC mode is enabled. UI: DownloadQSLDialog adds a "LoTW DXCC Credits" group box, persists its state, and queues a DXCC download task. Also adds /build to .gitignore. --- .gitignore | 1 + logformat/AdiFormat.cpp | 4 ++ logformat/LogFormat.cpp | 127 +++++++++++++++++++++++++++++++++++++++ logformat/LogFormat.h | 2 + service/lotw/Lotw.cpp | 35 +++++++---- service/lotw/Lotw.h | 3 + ui/DownloadQSLDialog.cpp | 23 +++++++ ui/DownloadQSLDialog.ui | 15 ++++- 8 files changed, 196 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 53baecf8..1f63de72 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ compile_commands.json # QtCreator local machine specific files for imported projects *creator.user* .DS_Store +/build diff --git a/logformat/AdiFormat.cpp b/logformat/AdiFormat.cpp index 8d0ca30d..5fbac04a 100644 --- a/logformat/AdiFormat.cpp +++ b/logformat/AdiFormat.cpp @@ -329,6 +329,8 @@ void AdiFormat::contactFields2SQLRecord(QMap &contact, QSqlRe record.setValue("country_intl",contact.take("country_intl")); record.setValue("credit_submitted",contact.take("credit_submitted")); record.setValue("credit_granted",contact.take("credit_granted")); + record.setValue("app_lotw_credit_submitted",contact.take("app_lotw_credit_submitted")); + record.setValue("app_lotw_credit_granted",contact.take("app_lotw_credit_granted")); record.setValue("darc_dok",contact.take("darc_dok").toString().toUpper()); record.setValue("dcl_qslrdate",parseDate(contact.take("dcl_qslrdate").toString())); record.setValue("dcl_qslsdate",parseDate(contact.take("dcl_qslsdate").toString())); @@ -906,6 +908,8 @@ QHash AdiFormat::DB2ADIFExportParams = { "contest_id", ExportParams("contest_id")}, { "credit_submitted", ExportParams("credit_submitted")}, { "credit_granted", ExportParams("credit_granted")}, + { "app_lotw_credit_submitted", ExportParams("app_lotw_credit_submitted")}, + { "app_lotw_credit_granted", ExportParams("app_lotw_credit_granted")}, { "darc_dok", ExportParams("darc_dok")}, { "dcl_qslrdate", ExportParams("dcl_qslrdate", OutputFieldFormatter::TODATE)}, { "dcl_qslsdate", ExportParams("dcl_qslsdate", OutputFieldFormatter::TODATE)}, diff --git a/logformat/LogFormat.cpp b/logformat/LogFormat.cpp index 2e045dab..9e59f638 100644 --- a/logformat/LogFormat.cpp +++ b/logformat/LogFormat.cpp @@ -773,6 +773,133 @@ unsigned long LogFormat::runImport(QTextStream& importLogStream, #undef RECORDIDX +void LogFormat::runQSOCreditImport(QSLFrom /*fromService*/) +{ + FCT_IDENTIFICATION; + + auto reportFormatter = [&](const QDateTime &qsoTime, + const QString &callsign, + const QString &mode, + const QStringList addInfo = QStringList()) + { + return QString("%0; %1; %2%3 %4").arg(qsoTime.isValid() ? qsoTime.toString(locale.formatDateShortWithYYYY()) : "-", + callsign, + mode, + (addInfo.size() > 0 ) ? ";" : "", + addInfo.join(", ")); + }; + + QSLMergeStat stats = {QStringList(), QStringList(), QStringList(), QStringList(), 0}; + this->importStart(); + + QSqlTableModel model; + model.setTable("contacts"); + QSqlRecord QSLRecord = model.record(0); + + while ( true ) + { + QSLRecord.clearValues(); + + if ( !this->importNext(QSLRecord) ) break; + + stats.qsosDownloaded++; + + if ( stats.qsosDownloaded % 100 == 0 ) + { + emit importPosition(stream.pos()); + } + + const QVariant &call = QSLRecord.value("callsign"); + const QVariant &band = QSLRecord.value("band"); + const QVariant &start_time = QSLRecord.value("start_time"); + const QVariant &satName = QSLRecord.value("sat_name"); + + /* require at minimum: callsign, band, and a valid date */ + if ( !start_time.toDateTime().isValid() + || call.toString().isEmpty() + || band.toString().isEmpty() ) + { + qWarning() << "DXCC credit import: missing start_time, callsign, or band"; + qCDebug(runtime) << QSLRecord; + stats.errorQSLs.append(reportFormatter(start_time.toDateTime(), call.toString(), "")); + continue; + } + + /* Match on callsign + band + date (day only) + must already have a QSL confirmed. + * Mode is intentionally omitted because DXCC credit records may use generic + * mode group names that do not precisely match the logged mode. */ + const QString matchFilter = QString( + "callsign=upper('%1') AND upper(band)=upper('%2') AND " + "COALESCE(sat_name, '') = upper('%3') AND " + "date(start_time)=date('%4') AND " + "(qsl_rcvd = 'Y' OR lotw_qsl_rcvd = 'Y')" + ).arg(call.toString(), + band.toString(), + satName.toString(), + start_time.toDateTime().toTimeZone(QTimeZone::utc()).toString("yyyy-MM-dd hh:mm:ss")); + + model.setFilter(matchFilter); + model.select(); + + if ( model.rowCount() != 1 ) + { + stats.unmatchedQSLs.append(reportFormatter(start_time.toDateTime(), call.toString(), "")); + continue; + } + + QSqlRecord originalRecord = model.record(0); + + QStringList updatedFields; + bool callUpdate = false; + + qCDebug(runtime) << "DXCC credit: attempting update for" << call.toString() + << band.toString() << start_time.toString(); + + auto conditionUpdate = [&](const QString &contactKey, + const QString &qslKey) + { + if ( !QSLRecord.value(qslKey).toString().isEmpty() + && originalRecord.value(contactKey).toString().isEmpty() ) + { + qCDebug(runtime) << "Updating:" << contactKey + << "to" << QSLRecord.value(qslKey).toString(); + updatedFields.append(contactKey + "(" + QSLRecord.value(qslKey).toString() + ")"); + originalRecord.setValue(contactKey, QSLRecord.value(qslKey)); + return true; + } + return false; + }; + + callUpdate |= conditionUpdate("credit_granted", "credit_granted"); + callUpdate |= conditionUpdate("credit_submitted", "credit_submitted"); + callUpdate |= conditionUpdate("app_lotw_credit_granted", "app_lotw_credit_granted"); + callUpdate |= conditionUpdate("app_lotw_credit_submitted", "app_lotw_credit_submitted"); + + if ( callUpdate ) + { + qCDebug(runtime) << "Calling update for" << call << band << start_time << satName; + if ( !model.setRecord(0, originalRecord) ) + { + qWarning() << "Cannot update a Contact record - " << model.lastError(); + qCDebug(runtime) << originalRecord; + } + + if ( !model.submitAll() ) + { + qWarning() << "Cannot commit changes to Contact Table - " << model.lastError(); + } + + stats.updatedQSOs.append(reportFormatter(start_time.toDateTime(), call.toString(), "", updatedFields)); + } + } + + emit importPosition(stream.pos()); + + this->importEnd(); + + emit QSLMergeFinished(stats); +} + void LogFormat::runQSLImport(QSLFrom fromService) { FCT_IDENTIFICATION; diff --git a/logformat/LogFormat.h b/logformat/LogFormat.h index eda94ad6..11db57da 100644 --- a/logformat/LogFormat.h +++ b/logformat/LogFormat.h @@ -34,6 +34,7 @@ class LogFormat : public QObject { enum QSLFrom { LOTW, EQSL, + LOTW_DXCC, UNKNOW }; @@ -57,6 +58,7 @@ class LogFormat : public QObject { unsigned long *warnings, unsigned long *errors); void runQSLImport(QSLFrom fromService); + void runQSOCreditImport(QSLFrom fromService); long runExport(); long runExport(const QList&); void setDefaults(QMap& defaults); diff --git a/service/lotw/Lotw.cpp b/service/lotw/Lotw.cpp index c3a29a27..8e121a3d 100644 --- a/service/lotw/Lotw.cpp +++ b/service/lotw/Lotw.cpp @@ -434,21 +434,29 @@ void LotwQSLDownloader::receiveQSL(const QDate &start_date, bool qso_since, cons qCDebug(function_parameters) << start_date << " " << qso_since; QList> params; - params.append(qMakePair(QString("qso_query"), QString("1"))); - params.append(qMakePair(QString("qso_qsldetail"), QString("yes"))); - params.append(qMakePair(QString("qso_owncall"), station_callsign)); - const QString &start = start_date.toString("yyyy-MM-dd"); - - if (qso_since) + if ( LotwDXCCCredits ) { - params.append(qMakePair(QString("qso_qsl"), QString("no"))); - params.append(qMakePair(QString("qso_qsorxsince"), start)); + params.append(qMakePair(QString("ac_acct"), QString("1"))); } else { - params.append(qMakePair(QString("qso_qsl"), QString("yes"))); - params.append(qMakePair(QString("qso_qslsince"), start)); + params.append(qMakePair(QString("qso_query"), QString("1"))); + params.append(qMakePair(QString("qso_qsldetail"), QString("yes"))); + params.append(qMakePair(QString("qso_owncall"), station_callsign)); + + const QString &start = start_date.toString("yyyy-MM-dd"); + + if (qso_since) + { + params.append(qMakePair(QString("qso_qsl"), QString("no"))); + params.append(qMakePair(QString("qso_qsorxsince"), start)); + } + else + { + params.append(qMakePair(QString("qso_qsl"), QString("yes"))); + params.append(qMakePair(QString("qso_qslsince"), start)); + } } get(params); @@ -540,7 +548,10 @@ void LotwQSLDownloader::processReply(QNetworkReply *reply) emit receiveQSLComplete(stats); }); - adi.runQSLImport(adi.LOTW); + if ( LotwDXCCCredits ) + adi.runQSOCreditImport(adi.LOTW_DXCC); + else + adi.runQSLImport(adi.LOTW); tempFile.close(); @@ -559,7 +570,7 @@ void LotwQSLDownloader::get(QList> params) query.addQueryItem("login", username.toUtf8().toPercentEncoding()); query.addQueryItem("password", password.toUtf8().toPercentEncoding()); - QUrl url(ADIF_API); + QUrl url(LotwDXCCCredits ? DXCC_CREDIT_API : ADIF_API); url.setQuery(query); qCDebug(runtime) << Data::safeQueryString(query); diff --git a/service/lotw/Lotw.h b/service/lotw/Lotw.h index 801e6f7f..1928360c 100644 --- a/service/lotw/Lotw.h +++ b/service/lotw/Lotw.h @@ -95,6 +95,8 @@ class LotwQSLDownloader : public GenericQSLDownloader, private LotwBase explicit LotwQSLDownloader(QObject *parent = nullptr); virtual ~LotwQSLDownloader(); + bool LotwDXCCCredits = false; + virtual void receiveQSL(const QDate &, bool, const QString &) override; public slots: @@ -103,6 +105,7 @@ public slots: private: QNetworkReply *currentReply; const QString ADIF_API = "https://lotw.arrl.org/lotwuser/lotwreport.adi"; + const QString DXCC_CREDIT_API = "https://lotw.arrl.org/lotwuser/logbook/qslcards.php"; virtual void processReply(QNetworkReply* reply) override; void get(QList> params); diff --git a/ui/DownloadQSLDialog.cpp b/ui/DownloadQSLDialog.cpp index d3873739..3ec50609 100644 --- a/ui/DownloadQSLDialog.cpp +++ b/ui/DownloadQSLDialog.cpp @@ -25,6 +25,7 @@ DownloadQSLDialog::DownloadQSLDialog(QWidget *parent) "FROM contacts ORDER BY station_callsign", "", ui->lotwMyCallsignCombo)); ui->lotwDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); ui->eqslDateEdit->setDisplayFormat(locale.formatDateShortWithYYYY()); + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("&Download")); const StationProfile &profile = StationProfilesManager::instance()->getCurProfile1(); @@ -44,6 +45,9 @@ DownloadQSLDialog::DownloadQSLDialog(QWidget *parent) ui->lotwGroupBox->setChecked(false); ui->lotwGroupBox->setEnabled(false); ui->lotwGroupBox->setToolTip(tr("LoTW is not configured properly.

Please, use Settings dialog to configure it.

")); + ui->lotwDXCCGroupBox->setChecked(false); + ui->lotwDXCCGroupBox->setEnabled(false); + ui->lotwDXCCGroupBox->setToolTip(tr("LoTW is not configured properly.

Please, use Settings dialog to configure it.

")); } if ( EQSLBase::getUsername().isEmpty() ) @@ -97,6 +101,11 @@ void DownloadQSLDialog::loadDialogState() ui->eqslDateTypeCombo->setCurrentIndex((LogParam::getDownloadQSLServiceLastQSOQSL("eqsl")) ? 0 : 1); ui->eqslQTHProfileEdit->setText(LogParam::getDownloadQSLeQSLLastProfile()); + + /*************/ + /* LoTW DXCC */ + /*************/ + ui->lotwDXCCGroupBox->setChecked(LogParam::getDownloadQSLServiceState("lotwdxcc")); } void DownloadQSLDialog::saveDialogState() @@ -117,6 +126,11 @@ void DownloadQSLDialog::saveDialogState() LogParam::setDownloadQSLServiceLastDate("eqsl", QDateTime::currentDateTimeUtc().date()); LogParam::setDownloadQSLServiceLastQSOQSL("eqsl", ui->eqslDateTypeCombo->currentIndex() == 0); LogParam::setDownloadQSLeQSLLastProfile(ui->eqslQTHProfileEdit->text()); + + /*************/ + /* LoTW DXCC */ + /*************/ + LogParam::setDownloadQSLServiceState("lotwdxcc", ui->lotwDXCCGroupBox->isChecked()); } void DownloadQSLDialog::prepareDownload(GenericQSLDownloader *service, @@ -205,6 +219,15 @@ void DownloadQSLDialog::downloadQSLs() lotw->receiveQSL(ui->lotwDateEdit->date(), !qslSinceActive, ui->lotwMyCallsignCombo->currentText()); }); + if ( ui->lotwDXCCGroupBox->isChecked() ) + downloadQueue.enqueue([=]() + { + LotwQSLDownloader* lotw = new LotwQSLDownloader(this); + lotw->LotwDXCCCredits = true; + prepareDownload(lotw, tr("LoTW DXCC Credits"), false, "lotwdxcc"); + lotw->receiveQSL(QDate(), false, QString()); + }); + if ( downloadQueue.isEmpty() ) { QMessageBox::information(this, tr("QLog Information"), tr("No service selected")); diff --git a/ui/DownloadQSLDialog.ui b/ui/DownloadQSLDialog.ui index 5a553ccb..406b2dae 100644 --- a/ui/DownloadQSLDialog.ui +++ b/ui/DownloadQSLDialog.ui @@ -7,7 +7,7 @@ 0 0 403 - 270 + 300 @@ -109,7 +109,17 @@ - + + + + LoTW DXCC Credits + + + true + + + + Qt::Horizontal @@ -130,6 +140,7 @@ lotwDateTypeCombo lotwDateEdit lotwMyCallsignCombo + lotwDXCCGroupBox From 052c4425a26380dbbd7e7a272084ce90964424bd Mon Sep 17 00:00:00 2001 From: Michael Morgan <84428382+aa5sh@users.noreply.github.com> Date: Wed, 18 Mar 2026 00:06:51 -0400 Subject: [PATCH 2/3] Fix ADI missing time, DXCC import and UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AdiFormat: default to midnight UTC when a record has a valid date but no valid time to avoid creating invalid QDateTime values. LogFormat: make DXCC credit import more robust by matching records using a ±1 day tolerance (JULIANDAY difference) instead of exact same-date-only matching, log found matches, and update every matching contact (handling multiple QSOs) by setting each record and committing changes; also improve error/debug messages and adjust row-count handling. UI (DownloadQSLDialog): replace the LoTW DXCC GroupBox with a one-off checkbox (lotwDXCCCheckBox), default it to unchecked/disabled when LoTW is not configured, stop persisting its state, and update the .ui layout and tabstops accordingly. --- logformat/AdiFormat.cpp | 8 ++++ logformat/LogFormat.cpp | 81 +++++++++++++++++++++------------------- ui/DownloadQSLDialog.cpp | 19 +++------- ui/DownloadQSLDialog.ui | 11 ++---- 4 files changed, 61 insertions(+), 58 deletions(-) diff --git a/logformat/AdiFormat.cpp b/logformat/AdiFormat.cpp index 5fbac04a..6d6836b6 100644 --- a/logformat/AdiFormat.cpp +++ b/logformat/AdiFormat.cpp @@ -494,6 +494,14 @@ void AdiFormat::contactFields2SQLRecord(QMap &contact, QSqlRe time_on = time_off; } + /* Records that have a date but no time (e.g. LoTW DXCC credit exports) would + * produce an invalid QDateTime, making them impossible to match later. + * Default to midnight UTC so the record remains usable. */ + if ( !time_on.isValid() && date_on.isValid() ) + { + time_on = QTime(0, 0, 0); + } + QDateTime start_time(date_on, time_on, QTimeZone::utc()); QDateTime end_time(date_off, time_off, QTimeZone::utc()); diff --git a/logformat/LogFormat.cpp b/logformat/LogFormat.cpp index 9e59f638..d4ae91c6 100644 --- a/logformat/LogFormat.cpp +++ b/logformat/LogFormat.cpp @@ -825,13 +825,16 @@ void LogFormat::runQSOCreditImport(QSLFrom /*fromService*/) continue; } - /* Match on callsign + band + date (day only) + must already have a QSL confirmed. + /* Match on callsign + band + date + must already have a QSL confirmed. * Mode is intentionally omitted because DXCC credit records may use generic - * mode group names that do not precisely match the logged mode. */ + * mode group names that do not precisely match the logged mode. + * A ±1 day tolerance is applied because the credit file may record the QSO + * date in the operator's local time zone while the DB stores UTC, causing a + * one-day discrepancy for QSOs made near midnight. */ const QString matchFilter = QString( "callsign=upper('%1') AND upper(band)=upper('%2') AND " "COALESCE(sat_name, '') = upper('%3') AND " - "date(start_time)=date('%4') AND " + "ABS(JULIANDAY(date(start_time)) - JULIANDAY(date('%4'))) <= 1 AND " "(qsl_rcvd = 'Y' OR lotw_qsl_rcvd = 'Y')" ).arg(call.toString(), band.toString(), @@ -841,55 +844,57 @@ void LogFormat::runQSOCreditImport(QSLFrom /*fromService*/) model.setFilter(matchFilter); model.select(); - if ( model.rowCount() != 1 ) + if ( model.rowCount() < 1 ) { stats.unmatchedQSLs.append(reportFormatter(start_time.toDateTime(), call.toString(), "")); continue; } - QSqlRecord originalRecord = model.record(0); - - QStringList updatedFields; - bool callUpdate = false; + qCDebug(runtime) << "DXCC credit: found" << model.rowCount() << "match(es) for" + << call.toString() << band.toString() << start_time.toString(); - qCDebug(runtime) << "DXCC credit: attempting update for" << call.toString() - << band.toString() << start_time.toString(); - - auto conditionUpdate = [&](const QString &contactKey, - const QString &qslKey) + /* Update every matching contact — multiple QSOs on the same date/band are possible. */ + for ( int row = 0; row < model.rowCount(); ++row ) { - if ( !QSLRecord.value(qslKey).toString().isEmpty() - && originalRecord.value(contactKey).toString().isEmpty() ) - { - qCDebug(runtime) << "Updating:" << contactKey - << "to" << QSLRecord.value(qslKey).toString(); - updatedFields.append(contactKey + "(" + QSLRecord.value(qslKey).toString() + ")"); - originalRecord.setValue(contactKey, QSLRecord.value(qslKey)); - return true; - } - return false; - }; + QSqlRecord originalRecord = model.record(row); - callUpdate |= conditionUpdate("credit_granted", "credit_granted"); - callUpdate |= conditionUpdate("credit_submitted", "credit_submitted"); - callUpdate |= conditionUpdate("app_lotw_credit_granted", "app_lotw_credit_granted"); - callUpdate |= conditionUpdate("app_lotw_credit_submitted", "app_lotw_credit_submitted"); + QStringList updatedFields; + bool callUpdate = false; - if ( callUpdate ) - { - qCDebug(runtime) << "Calling update for" << call << band << start_time << satName; - if ( !model.setRecord(0, originalRecord) ) + auto conditionUpdate = [&](const QString &contactKey, + const QString &qslKey) { - qWarning() << "Cannot update a Contact record - " << model.lastError(); - qCDebug(runtime) << originalRecord; - } + if ( !QSLRecord.value(qslKey).toString().isEmpty() + && originalRecord.value(contactKey).toString().isEmpty() ) + { + qCDebug(runtime) << "Updating:" << contactKey + << "to" << QSLRecord.value(qslKey).toString(); + updatedFields.append(contactKey + "(" + QSLRecord.value(qslKey).toString() + ")"); + originalRecord.setValue(contactKey, QSLRecord.value(qslKey)); + return true; + } + return false; + }; + + callUpdate |= conditionUpdate("credit_granted", "credit_granted"); + callUpdate |= conditionUpdate("credit_submitted", "credit_submitted"); + callUpdate |= conditionUpdate("app_lotw_credit_granted", "app_lotw_credit_granted"); + callUpdate |= conditionUpdate("app_lotw_credit_submitted", "app_lotw_credit_submitted"); - if ( !model.submitAll() ) + if ( callUpdate ) { - qWarning() << "Cannot commit changes to Contact Table - " << model.lastError(); + if ( !model.setRecord(row, originalRecord) ) + { + qWarning() << "Cannot update a Contact record - " << model.lastError(); + qCDebug(runtime) << originalRecord; + } + stats.updatedQSOs.append(reportFormatter(start_time.toDateTime(), call.toString(), "", updatedFields)); } + } - stats.updatedQSOs.append(reportFormatter(start_time.toDateTime(), call.toString(), "", updatedFields)); + if ( !model.submitAll() ) + { + qWarning() << "Cannot commit changes to Contact Table - " << model.lastError(); } } diff --git a/ui/DownloadQSLDialog.cpp b/ui/DownloadQSLDialog.cpp index 3ec50609..1aadff0e 100644 --- a/ui/DownloadQSLDialog.cpp +++ b/ui/DownloadQSLDialog.cpp @@ -45,9 +45,9 @@ DownloadQSLDialog::DownloadQSLDialog(QWidget *parent) ui->lotwGroupBox->setChecked(false); ui->lotwGroupBox->setEnabled(false); ui->lotwGroupBox->setToolTip(tr("LoTW is not configured properly.

Please, use Settings dialog to configure it.

")); - ui->lotwDXCCGroupBox->setChecked(false); - ui->lotwDXCCGroupBox->setEnabled(false); - ui->lotwDXCCGroupBox->setToolTip(tr("LoTW is not configured properly.

Please, use Settings dialog to configure it.

")); + ui->lotwDXCCCheckBox->setChecked(false); + ui->lotwDXCCCheckBox->setEnabled(false); + ui->lotwDXCCCheckBox->setToolTip(tr("LoTW is not configured properly.

Please, use Settings dialog to configure it.

")); } if ( EQSLBase::getUsername().isEmpty() ) @@ -102,10 +102,8 @@ void DownloadQSLDialog::loadDialogState() ui->eqslQTHProfileEdit->setText(LogParam::getDownloadQSLeQSLLastProfile()); - /*************/ - /* LoTW DXCC */ - /*************/ - ui->lotwDXCCGroupBox->setChecked(LogParam::getDownloadQSLServiceState("lotwdxcc")); + /* LoTW DXCC Credits checkbox always starts unchecked — this is a one-off task. */ + ui->lotwDXCCCheckBox->setChecked(false); } void DownloadQSLDialog::saveDialogState() @@ -126,11 +124,6 @@ void DownloadQSLDialog::saveDialogState() LogParam::setDownloadQSLServiceLastDate("eqsl", QDateTime::currentDateTimeUtc().date()); LogParam::setDownloadQSLServiceLastQSOQSL("eqsl", ui->eqslDateTypeCombo->currentIndex() == 0); LogParam::setDownloadQSLeQSLLastProfile(ui->eqslQTHProfileEdit->text()); - - /*************/ - /* LoTW DXCC */ - /*************/ - LogParam::setDownloadQSLServiceState("lotwdxcc", ui->lotwDXCCGroupBox->isChecked()); } void DownloadQSLDialog::prepareDownload(GenericQSLDownloader *service, @@ -219,7 +212,7 @@ void DownloadQSLDialog::downloadQSLs() lotw->receiveQSL(ui->lotwDateEdit->date(), !qslSinceActive, ui->lotwMyCallsignCombo->currentText()); }); - if ( ui->lotwDXCCGroupBox->isChecked() ) + if ( ui->lotwDXCCCheckBox->isChecked() ) downloadQueue.enqueue([=]() { LotwQSLDownloader* lotw = new LotwQSLDownloader(this); diff --git a/ui/DownloadQSLDialog.ui b/ui/DownloadQSLDialog.ui index 406b2dae..a6dc23bb 100644 --- a/ui/DownloadQSLDialog.ui +++ b/ui/DownloadQSLDialog.ui @@ -7,7 +7,7 @@ 0 0 403 - 300 + 290
@@ -110,13 +110,10 @@
- - + + LoTW DXCC Credits - - true - @@ -140,7 +137,7 @@ lotwDateTypeCombo lotwDateEdit lotwMyCallsignCombo - lotwDXCCGroupBox + lotwDXCCCheckBox From 4d73d52e9a47efdb3eb530d0b6d4b77042096bd2 Mon Sep 17 00:00:00 2001 From: Michael Morgan <84428382+aa5sh@users.noreply.github.com> Date: Wed, 18 Mar 2026 00:15:15 -0400 Subject: [PATCH 3/3] Remove app_lotw_credit_* field handling Remove handling of app_lotw_credit_submitted and app_lotw_credit_granted across the logging code. Changes remove setting these fields into SQL records (AdiFormat::contactFields2SQLRecord), remove them from DB->ADIF export parameters (AdiFormat::DB2ADIFExportParams), and stop checking them during QSO credit import condition updates (LogFormat::runQSOCreditImport). This cleans up unused/removed LoTW app credit fields. --- logformat/AdiFormat.cpp | 4 ---- logformat/LogFormat.cpp | 2 -- 2 files changed, 6 deletions(-) diff --git a/logformat/AdiFormat.cpp b/logformat/AdiFormat.cpp index 6d6836b6..a2128636 100644 --- a/logformat/AdiFormat.cpp +++ b/logformat/AdiFormat.cpp @@ -329,8 +329,6 @@ void AdiFormat::contactFields2SQLRecord(QMap &contact, QSqlRe record.setValue("country_intl",contact.take("country_intl")); record.setValue("credit_submitted",contact.take("credit_submitted")); record.setValue("credit_granted",contact.take("credit_granted")); - record.setValue("app_lotw_credit_submitted",contact.take("app_lotw_credit_submitted")); - record.setValue("app_lotw_credit_granted",contact.take("app_lotw_credit_granted")); record.setValue("darc_dok",contact.take("darc_dok").toString().toUpper()); record.setValue("dcl_qslrdate",parseDate(contact.take("dcl_qslrdate").toString())); record.setValue("dcl_qslsdate",parseDate(contact.take("dcl_qslsdate").toString())); @@ -916,8 +914,6 @@ QHash AdiFormat::DB2ADIFExportParams = { "contest_id", ExportParams("contest_id")}, { "credit_submitted", ExportParams("credit_submitted")}, { "credit_granted", ExportParams("credit_granted")}, - { "app_lotw_credit_submitted", ExportParams("app_lotw_credit_submitted")}, - { "app_lotw_credit_granted", ExportParams("app_lotw_credit_granted")}, { "darc_dok", ExportParams("darc_dok")}, { "dcl_qslrdate", ExportParams("dcl_qslrdate", OutputFieldFormatter::TODATE)}, { "dcl_qslsdate", ExportParams("dcl_qslsdate", OutputFieldFormatter::TODATE)}, diff --git a/logformat/LogFormat.cpp b/logformat/LogFormat.cpp index d4ae91c6..a200f7a9 100644 --- a/logformat/LogFormat.cpp +++ b/logformat/LogFormat.cpp @@ -878,8 +878,6 @@ void LogFormat::runQSOCreditImport(QSLFrom /*fromService*/) callUpdate |= conditionUpdate("credit_granted", "credit_granted"); callUpdate |= conditionUpdate("credit_submitted", "credit_submitted"); - callUpdate |= conditionUpdate("app_lotw_credit_granted", "app_lotw_credit_granted"); - callUpdate |= conditionUpdate("app_lotw_credit_submitted", "app_lotw_credit_submitted"); if ( callUpdate ) {