Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POTA file export #514

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions QLog.pro
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ SOURCES += \
logformat/CSVFormat.cpp \
logformat/JsonFormat.cpp \
logformat/LogFormat.cpp \
logformat/PotaAdiFormat.cpp \
models/AlertTableModel.cpp \
models/AwardsTableModel.cpp \
models/DxccTableModel.cpp \
Expand Down Expand Up @@ -241,6 +242,7 @@ HEADERS += \
logformat/CSVFormat.h \
logformat/JsonFormat.h \
logformat/LogFormat.h \
logformat/PotaAdiFormat.h \
models/AlertTableModel.h \
models/AwardsTableModel.h \
models/DxccTableModel.h \
Expand Down
1 change: 0 additions & 1 deletion logformat/AdiFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ void AdiFormat::exportStart()
{
FCT_IDENTIFICATION;

stream << "### QLog ADIF Export\n";
writeField("ADIF_VER", ALWAYS_PRESENT, ADIF_VERSION_STRING);
writeField("PROGRAMID", ALWAYS_PRESENT, PROGRAMID_STRING);
writeField("PROGRAMVERSION", ALWAYS_PRESENT, VERSION);
Expand Down
7 changes: 7 additions & 0 deletions logformat/LogFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "LogFormat.h"
#include "AdiFormat.h"
#include "AdxFormat.h"
#include "PotaAdiFormat.h"
#include "JsonFormat.h"
#include "CSVFormat.h"
#include "data/Data.h"
Expand Down Expand Up @@ -51,6 +52,9 @@ LogFormat* LogFormat::open(QString type, QTextStream& stream) {
else if (type == "cabrillo") {
return open(LogFormat::JSON, stream);
}
else if (type == "pota") {
return open(LogFormat::POTA, stream);
}
else {
return nullptr;
}
Expand All @@ -77,6 +81,9 @@ LogFormat* LogFormat::open(LogFormat::Type type, QTextStream& stream) {
case LogFormat::CABRILLO:
return nullptr;

case LogFormat::POTA:
return new PotaAdiFormat(stream);

default:
return nullptr;
}
Expand Down
3 changes: 2 additions & 1 deletion logformat/LogFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class LogFormat : public QObject {
ADX,
CABRILLO,
JSON,
CSV
CSV,
POTA
};

enum QSLFrom {
Expand Down
153 changes: 153 additions & 0 deletions logformat/PotaAdiFormat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include "PotaAdiFormat.h"
#include <QDebug>
#include <QSqlField>
#include <QSqlRecord>
#include "core/debug.h"
#include <models/LogbookModel.h>

MODULE_IDENTIFICATION("qlog.logformat.potalogformat");

#define ALWAYS_PRESENT true

PotaAdiFormat::PotaAdiFormat(QTextStream &stream)
: AdiFormat(stream)
, currentDate(QDateTime::currentDateTime())
{
FCT_IDENTIFICATION;
}

void PotaAdiFormat::setExportInfo(QFile &exportFile)
{
this->exportInfo = new QFileInfo(exportFile);
}

void PotaAdiFormat::exportContact(const QSqlRecord &sourceRecord, QMap<QString, QString> *applTags)
{
FCT_IDENTIFICATION;
if (this->exportInfo == nullptr) {
return;
}
// break single record into child activated park records
// assign records to files
QList<QSqlRecord> records = PotaAdiFormat::splitActivatedParks(sourceRecord);
for (QSqlRecord &record : records) {
duplicateField(record, "my_pota_ref", "my_sig");
record.field("my_sig").setValue(QString("POTA"));
duplicateField(record, "my_pota_ref", "my_sig_info");
if (!record.field("pota_ref").isNull()) {
duplicateField(record, "pota_ref", "sig_info");
duplicateField(record, "pota_ref", "sig");
record.field("sig").setValue(QString("POTA"));
}
AdiFormat *parkOut = this->getParkFile(record);
parkOut->exportContact(record, applTags);
// let parent do ADI export as normal to specified file
AdiFormat::exportContact(record, applTags);
}
}

AdiFormat *PotaAdiFormat::getParkFile(const QSqlRecord &record)
{
// https://docs.pota.app/docs/activator_reference/logging_made_easy.html#naming-your-files
// station_callsign@park#-yyyymmdd
const QString parkFileName(record.field("station_callsign").value().toString() + "@"
+ record.field("my_sig_info").value().toString() + "-"
+ currentDate.toString("yyyyMMdd-hhmm") + ".adif");

if (!parkFormats.contains(parkFileName)) {
parkFiles[parkFileName] = new QFile(exportInfo->canonicalPath() + QDir::separator()
+ parkFileName);
if (!parkFiles[parkFileName]->open(QFile::WriteOnly | QFile::Text)) {
qCCritical(runtime) << "Could not open POTA park file for writing "
<< parkFiles[parkFileName]->fileName();
}
QTextStream *parkStream = new QTextStream(parkFiles[parkFileName]);
parkFormats[parkFileName] = new AdiFormat(*parkStream);
parkFormats[parkFileName]->exportStart();
}
qCDebug(runtime) << "using park file " << parkFileName << " is open? "
<< parkFiles[parkFileName]->isOpen();

return parkFormats[parkFileName];
}

QList<QSqlRecord> PotaAdiFormat::splitActivatedParks(const QSqlRecord &record)
{
FCT_IDENTIFICATION;
// can contain multiple parks as a csv:
// <MY_POTA_REF:40>K-0817,K-4566,K-4576,K-4573,K-4578@US-WY
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
const QStringList activatedParks = record.field("my_pota_ref")
.value()
.toString()
.split(QRegularExpression("\\s*,\\s*"),
Qt::SplitBehaviorFlags::SkipEmptyParts);
#else /* Due to ubuntu 20.04 where qt5.12 is present */
const QStringList activatedParks = record.field("my_pota_ref")
.value()
.toString()
.split(QRegularExpression("\\s*,\\s*"),
QString::SkipEmptyParts);
#endif

if (activatedParks.length() <= 0) {
return QList<QSqlRecord>();
} else if (activatedParks.length() == 1) {
return QList<QSqlRecord>({record});
} else {
QList<QSqlRecord> records = QList<QSqlRecord>();
for (const QString &parkID : activatedParks) {
QSqlRecord single = QSqlRecord(record);
single.setValue("my_pota_ref", parkID);

// If this is a park to park - the remote park can also be a multi park
// activation. These must also be split into multiple records.
const QSqlField parkToPark = record.field("pota_ref");
if (parkToPark.isNull() || !parkToPark.value().toString().contains(",")) {
records.append(single);
} else {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
QStringList remoteParks
= parkToPark.value().toString().split(QRegularExpression("\\s*,\\s*"),
Qt::SplitBehaviorFlags::SkipEmptyParts);
#else /* Due to ubuntu 20.04 where qt5.12 is present */
QStringList remoteParks
= parkToPark.value().toString().split(QRegularExpression("\\s*,\\s*"),
QString::SkipEmptyParts);
#endif
for (const QString &remoteParkID : remoteParks) {
QSqlRecord remoteSingle = QSqlRecord(single);
remoteSingle.setValue("pota_ref", remoteParkID);
records.append(remoteSingle);
}
}
}
return records;
}
}

void PotaAdiFormat::exportEnd()
{
for (AdiFormat *parkFormat : parkFormats.values()) {
parkFormat->exportEnd();
}
}

void PotaAdiFormat::duplicateField(QSqlRecord &record,
const QString &fromFieldName,
const QString &toFieldName)
{
QSqlField dupped(record.field(fromFieldName));
record.remove(record.indexOf(toFieldName));
dupped.setName(toFieldName);
record.append(dupped);
}

PotaAdiFormat::~PotaAdiFormat()
{
FCT_IDENTIFICATION;
qDeleteAll(parkFormats.values());
parkFormats.clear();
qDeleteAll(parkFiles.values());
parkFiles.clear();
}
37 changes: 37 additions & 0 deletions logformat/PotaAdiFormat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef QLOG_LOGFORMAT_POTALOGFORMAT_H
#define QLOG_LOGFORMAT_POTALOGFORMAT_H
#include "AdiFormat.h"

/*
* A specialized case of ADI export, where each activated park gets T'd into its
* own file with some denormalization and values set to satisfy the pota.app
* upload processes.
*/
class PotaAdiFormat : public AdiFormat
{
public:
explicit PotaAdiFormat(QTextStream &stream);

virtual void exportContact(const QSqlRecord &,
QMap<QString, QString> *applTags = nullptr) override;
virtual void exportEnd() override;

virtual bool importNext(QSqlRecord &) override { return false; }

void setExportInfo(QFile &exportFile);
~PotaAdiFormat();

static QList<QSqlRecord> splitActivatedParks(const QSqlRecord &);

private:
QFileInfo *exportInfo;
QMap<QString, AdiFormat *> parkFormats;
QMap<QString, QFile *> parkFiles;
QDateTime currentDate;
AdiFormat *getParkFile(const QSqlRecord &record);
static void duplicateField(QSqlRecord &record,
const QString &fromFieldName,
const QString &toFieldName);
};

#endif // QLOG_LOGFORMAT_POTALOGFORMAT_H
32 changes: 25 additions & 7 deletions ui/ExportDialog.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#include <QFileDialog>
#include "ui/ExportDialog.h"
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QSqlError>
#include "ui/ExportDialog.h"
#include "ui_ExportDialog.h"
#include <logformat/PotaAdiFormat.h>

#include "core/debug.h"
#include "models/SqlListModel.h"
Expand Down Expand Up @@ -142,14 +143,18 @@ void ExportDialog::runExport()

QTextStream out(&file);

LogFormat* format = LogFormat::open(ui->typeSelect->currentText(), out);
LogFormat *format = LogFormat::open(ui->typeSelect->currentText(), out);

if (!format)
{
qCritical() << "unknown log format";
return;
}

if (PotaAdiFormat *potaFormat = dynamic_cast<PotaAdiFormat *>(format)) {
potaFormat->setExportInfo(file);
}

if ( ui->dateRangeCheckBox->isChecked() )
format->setFilterDateRange(ui->startDateEdit->date(), ui->endDateEdit->date());

Expand Down Expand Up @@ -334,6 +339,7 @@ void ExportDialog::fillExportedColumnsCombo()

ui->exportedColumnsCombo->addItem(tr("All"), "all");
ui->exportedColumnsCombo->addItem(tr("Minimal"), "min");
ui->exportedColumnsCombo->addItem(tr("POTA"), "pota");
ui->exportedColumnsCombo->addItem(tr("QSL-specific"), "qsl");
ui->exportedColumnsCombo->addItem(tr("Custom 1"), "c1");
ui->exportedColumnsCombo->addItem(tr("Custom 2"), "c2");
Expand Down Expand Up @@ -402,6 +408,10 @@ void ExportDialog::exportedColumnsComboChanged(int index)
{
exportedColumns = settings.value("export/" + comboValue, QVariant::fromValue(qslColumns)).value<QSet<int>>();
}
else if ( comboValue == "pota" )
{
exportedColumns = potaColumns;
}
}
}

Expand All @@ -418,12 +428,20 @@ void ExportDialog::fillQSLSendViaCombo()
FCT_IDENTIFICATION;

QMapIterator<QString, QString> iter(Data::instance()->qslSentViaEnum);
int iter_index = 0;
while ( iter.hasNext() )
{
while (iter.hasNext()) {
iter.next();
ui->qslSendViaComboBox->addItem(iter.value(), iter.key());
iter_index++;
}
}

void ExportDialog::exportFormatChanged(const QString &format)
{
FCT_IDENTIFICATION;

if (format == "POTA") {
ui->exportedColumnsCombo->setCurrentIndex(ui->exportedColumnsCombo->findData("pota"));
} else {
ui->exportedColumnsCombo->setCurrentIndex(ui->exportedColumnsCombo->findData("all"));
}
}

Expand Down
19 changes: 18 additions & 1 deletion ui/ExportDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public slots:
void exportedColumnStateChanged(int index, bool state);
void exportTypeChanged(int index);
void exportedColumnsComboChanged(int);

void exportFormatChanged(const QString &format);
private:
Ui::ExportDialog *ui;
LogLocale locale;
Expand All @@ -59,6 +59,23 @@ public slots:
LogbookModel::COLUMN_RST_SENT,
LogbookModel::COLUMN_RST_RCVD
};
const QSet<int> potaColumns{
LogbookModel::COLUMN_TIME_ON,
LogbookModel::COLUMN_CALL,
LogbookModel::COLUMN_OPERATOR,
LogbookModel::COLUMN_STATION_CALLSIGN,
LogbookModel::COLUMN_FREQUENCY,
LogbookModel::COLUMN_MODE,
LogbookModel::COLUMN_SUBMODE,
LogbookModel::COLUMN_MY_STATE,
LogbookModel::COLUMN_MY_COUNTRY,
LogbookModel::COLUMN_MY_POTA_REF,
LogbookModel::COLUMN_POTA_REF,
LogbookModel::COLUMN_MY_SIG,
LogbookModel::COLUMN_MY_SIG_INFO,
LogbookModel::COLUMN_SIG,
LogbookModel::COLUMN_SIG_INFO
};
LogbookModel logbookmodel;
QSettings settings;
const QList<QSqlRecord> qsos4export;
Expand Down
Loading
Loading