mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-25 05:38:46 -05:00
Class abstracting LotW user lookups which includes asynchronous data loading
This commit is contained in:
parent
d9cf9bed3c
commit
62a4569a4c
@ -261,6 +261,7 @@ set (wsjt_qt_CXXSRCS
|
|||||||
EqualizationToolsDialog.cpp
|
EqualizationToolsDialog.cpp
|
||||||
DoubleClickablePushButton.cpp
|
DoubleClickablePushButton.cpp
|
||||||
DoubleClickableRadioButton.cpp
|
DoubleClickableRadioButton.cpp
|
||||||
|
LotWUsers.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set (wsjt_qtmm_CXXSRCS
|
set (wsjt_qtmm_CXXSRCS
|
||||||
|
89
LotWUsers.cpp
Normal file
89
LotWUsers.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "LotWUsers.hpp"
|
||||||
|
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDate>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "Configuration.hpp"
|
||||||
|
|
||||||
|
#include "pimpl_impl.hpp"
|
||||||
|
|
||||||
|
#include "moc_LotWUsers.cpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Dictionary mapping call sign to date of last upload to LotW
|
||||||
|
using dictionary = QHash<QString, QDate>;
|
||||||
|
|
||||||
|
// Load the database from the given file name
|
||||||
|
//
|
||||||
|
// Expects the file to be in CSV format with no header with one
|
||||||
|
// record per line. Record fields are call sign followed by upload
|
||||||
|
// date in yyyy-MM-dd format followed by upload time (ignored)
|
||||||
|
dictionary load (QString const& lotw_users_file)
|
||||||
|
{
|
||||||
|
dictionary result;
|
||||||
|
QFile f {lotw_users_file};
|
||||||
|
if (f.open (QFile::ReadOnly | QFile::Text))
|
||||||
|
{
|
||||||
|
QTextStream s {&f};
|
||||||
|
for (auto l = s.readLine (); !l.isNull (); l = s.readLine ())
|
||||||
|
{
|
||||||
|
auto pos = l.indexOf (',');
|
||||||
|
result[l.left (pos)] = QDate::fromString (l.mid (pos + 1, l.indexOf (',', pos + 1) - pos - 1), "yyyy-MM-dd");
|
||||||
|
}
|
||||||
|
qDebug () << "LotW User Data Loaded";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error {QObject::tr ("Failed to open LotW users CSV file: '%1'").arg (f.fileName ()).toLocal8Bit ()};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LotWUsers::impl final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::future<dictionary> future_load_;
|
||||||
|
dictionary last_uploaded_;
|
||||||
|
};
|
||||||
|
|
||||||
|
LotWUsers::LotWUsers (Configuration const * configuration, QObject * parent)
|
||||||
|
: QObject {parent}
|
||||||
|
{
|
||||||
|
// load the database asynchronously
|
||||||
|
m_->future_load_ = std::async (std::launch::async, load, configuration->writeable_data_dir ().absoluteFilePath ("lotw-user-activity.csv"));
|
||||||
|
}
|
||||||
|
|
||||||
|
LotWUsers::~LotWUsers ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LotWUsers::user (QString const& call, qint64 uploaded_since_days) const
|
||||||
|
{
|
||||||
|
if (m_->future_load_.valid ())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// wait for the load to finish if necessary
|
||||||
|
const_cast<dictionary&> (m_->last_uploaded_) = const_cast<std::future<dictionary>&> (m_->future_load_).get ();
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
Q_EMIT LotW_users_error (e.what ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto p = m_->last_uploaded_.constFind (call);
|
||||||
|
if (p != m_->last_uploaded_.end ())
|
||||||
|
{
|
||||||
|
return p.value ().daysTo (QDate::currentDate ()) <= uploaded_since_days;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
35
LotWUsers.hpp
Normal file
35
LotWUsers.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef LOTW_USERS_HPP_
|
||||||
|
#define LOTW_USERS_HPP_
|
||||||
|
|
||||||
|
#include <boost/core/noncopyable.hpp>
|
||||||
|
#include <QObject>
|
||||||
|
#include "pimpl_h.hpp"
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QDate;
|
||||||
|
class Configuration;
|
||||||
|
|
||||||
|
//
|
||||||
|
// LotWUsers - Lookup Logbook of the World users
|
||||||
|
//
|
||||||
|
class LotWUsers final
|
||||||
|
: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LotWUsers (Configuration const * configuration, QObject * parent = 0);
|
||||||
|
~LotWUsers ();
|
||||||
|
|
||||||
|
// returns true if the specified call sign 'call' has uploaded their
|
||||||
|
// log to LotW in the last 'uploaded_since_days' days
|
||||||
|
Q_SLOT bool user (QString const& call, qint64 uploaded_since_days) const;
|
||||||
|
|
||||||
|
Q_SIGNAL void LotW_users_error (QString const& reason) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class impl;
|
||||||
|
pimpl<impl> m_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,6 @@ DecodedText::DecodedText (QString const& the_string)
|
|||||||
, message_ {string_.mid (column_qsoText + padding_).trimmed ()}
|
, message_ {string_.mid (column_qsoText + padding_).trimmed ()}
|
||||||
, is_standard_ {false}
|
, is_standard_ {false}
|
||||||
{
|
{
|
||||||
qDebug () << "DecodedText: the_string:" << the_string << "Nbsp pos:" << the_string.indexOf (QChar::Nbsp);
|
|
||||||
if (message_.length() >= 1)
|
if (message_.length() >= 1)
|
||||||
{
|
{
|
||||||
message0_ = message_.left(36);
|
message0_ = message_.left(36);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "displaytext.h"
|
#include "displaytext.h"
|
||||||
#include "mainwindow.h"
|
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QTextCharFormat>
|
#include <QTextCharFormat>
|
||||||
@ -8,12 +7,14 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
||||||
#include "qt_helpers.hpp"
|
#include "LotWUsers.hpp"
|
||||||
|
|
||||||
|
#include "qt_helpers.hpp"
|
||||||
#include "moc_displaytext.cpp"
|
#include "moc_displaytext.cpp"
|
||||||
|
|
||||||
DisplayText::DisplayText(QWidget *parent)
|
DisplayText::DisplayText(QWidget *parent)
|
||||||
: QTextEdit(parent)
|
: QTextEdit(parent)
|
||||||
|
, m_lotw_users {0}
|
||||||
, erase_action_ {new QAction {tr ("&Erase"), this}}
|
, erase_action_ {new QAction {tr ("&Erase"), this}}
|
||||||
{
|
{
|
||||||
setReadOnly (true);
|
setReadOnly (true);
|
||||||
@ -76,7 +77,6 @@ void DisplayText::insertLineSpacer(QString const& line)
|
|||||||
void DisplayText::appendText(QString const& text, QColor bg,
|
void DisplayText::appendText(QString const& text, QColor bg,
|
||||||
QString const& call1, QString const& call2)
|
QString const& call1, QString const& call2)
|
||||||
{
|
{
|
||||||
qDebug () << "DisplayText::appendText: text:" << text << "Nbsp pos:" << text.indexOf (QChar::Nbsp);
|
|
||||||
auto cursor = textCursor ();
|
auto cursor = textCursor ();
|
||||||
cursor.movePosition (QTextCursor::End);
|
cursor.movePosition (QTextCursor::End);
|
||||||
auto block_format = cursor.blockFormat ();
|
auto block_format = cursor.blockFormat ();
|
||||||
@ -144,7 +144,7 @@ void DisplayText::appendText(QString const& text, QColor bg,
|
|||||||
}
|
}
|
||||||
format.setBackground (bg);
|
format.setBackground (bg);
|
||||||
format.clearForeground ();
|
format.clearForeground ();
|
||||||
if(call2.size()>0 and !m_LoTW.contains(call2)) {
|
if(call2.size () && !m_lotw_users->user (call2, 365)) {
|
||||||
format.setForeground(m_color_LoTW); //Mark LoTW non-users
|
format.setForeground(m_color_LoTW); //Mark LoTW non-users
|
||||||
}
|
}
|
||||||
cursor.insertText(text.mid (text_index), format);
|
cursor.insertText(text.mid (text_index), format);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "decodedtext.h"
|
#include "decodedtext.h"
|
||||||
|
|
||||||
class QAction;
|
class QAction;
|
||||||
|
class LotWUsers;
|
||||||
|
|
||||||
class DisplayText
|
class DisplayText
|
||||||
: public QTextEdit
|
: public QTextEdit
|
||||||
@ -19,7 +20,7 @@ class DisplayText
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit DisplayText(QWidget *parent = 0);
|
explicit DisplayText(QWidget *parent = 0);
|
||||||
|
void setLotWUsers (LotWUsers const * lotw_users) {m_lotw_users = lotw_users;}
|
||||||
void setContentFont (QFont const&);
|
void setContentFont (QFont const&);
|
||||||
void insertLineSpacer(QString const&);
|
void insertLineSpacer(QString const&);
|
||||||
void displayDecodedText(DecodedText const& decodedText, QString const& myCall,
|
void displayDecodedText(DecodedText const& decodedText, QString const& myCall,
|
||||||
@ -45,6 +46,7 @@ protected:
|
|||||||
void mouseDoubleClickEvent(QMouseEvent *e);
|
void mouseDoubleClickEvent(QMouseEvent *e);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
LotWUsers const * m_lotw_users;
|
||||||
bool m_bPrincipalPrefix;
|
bool m_bPrincipalPrefix;
|
||||||
QString appendWorkedB4(QString message, QString const& callsign, QString grid, QColor * bg,
|
QString appendWorkedB4(QString message, QString const& callsign, QString grid, QColor * bg,
|
||||||
LogBook const& logBook, QString currentBand);
|
LogBook const& logBook, QString currentBand);
|
||||||
|
@ -162,7 +162,6 @@ int fast_jhpeak {0};
|
|||||||
int fast_jh2 {0};
|
int fast_jh2 {0};
|
||||||
int narg[15];
|
int narg[15];
|
||||||
QVector<QColor> g_ColorTbl;
|
QVector<QColor> g_ColorTbl;
|
||||||
QHash<QString,int> m_LoTW;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -204,6 +203,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
m_settings {multi_settings->settings ()},
|
m_settings {multi_settings->settings ()},
|
||||||
ui(new Ui::MainWindow),
|
ui(new Ui::MainWindow),
|
||||||
m_config {temp_directory, m_settings, this},
|
m_config {temp_directory, m_settings, this},
|
||||||
|
m_lotw_users {&m_config},
|
||||||
m_WSPR_band_hopping {m_settings, &m_config, this},
|
m_WSPR_band_hopping {m_settings, &m_config, this},
|
||||||
m_WSPR_tx_next {false},
|
m_WSPR_tx_next {false},
|
||||||
m_rigErrorMessageBox {MessageBox::Critical, tr ("Rig Control Error")
|
m_rigErrorMessageBox {MessageBox::Critical, tr ("Rig Control Error")
|
||||||
@ -386,6 +386,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
ui->dxGridEntry->setValidator (new MaidenheadLocatorValidator {this});
|
ui->dxGridEntry->setValidator (new MaidenheadLocatorValidator {this});
|
||||||
ui->dxCallEntry->setValidator (new CallsignValidator {this});
|
ui->dxCallEntry->setValidator (new CallsignValidator {this});
|
||||||
ui->sbTR->values ({5, 10, 15, 30});
|
ui->sbTR->values ({5, 10, 15, 30});
|
||||||
|
ui->decodedTextBrowser->setLotWUsers (&m_lotw_users);
|
||||||
|
ui->decodedTextBrowser2->setLotWUsers (&m_lotw_users);
|
||||||
|
|
||||||
m_baseCall = Radio::base_callsign (m_config.my_callsign ());
|
m_baseCall = Radio::base_callsign (m_config.my_callsign ());
|
||||||
m_opCall = m_config.opCall();
|
m_opCall = m_config.opCall();
|
||||||
@ -555,6 +557,10 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
m_equalizationToolsDialog->show ();
|
m_equalizationToolsDialog->show ();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect (&m_lotw_users, &LotWUsers::LotW_users_error, this, [this] (QString const& reason) {
|
||||||
|
MessageBox::warning_message (this, tr ("Error Loading LotW Users Data"), reason);
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
QButtonGroup* txMsgButtonGroup = new QButtonGroup {this};
|
QButtonGroup* txMsgButtonGroup = new QButtonGroup {this};
|
||||||
txMsgButtonGroup->addButton(ui->txrb1,1);
|
txMsgButtonGroup->addButton(ui->txrb1,1);
|
||||||
txMsgButtonGroup->addButton(ui->txrb2,2);
|
txMsgButtonGroup->addButton(ui->txrb2,2);
|
||||||
@ -919,31 +925,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
ui->cbMenus->setChecked(false);
|
ui->cbMenus->setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile f{m_config.data_dir().absoluteFilePath ("lotw-user-activity.csv")};
|
|
||||||
if(f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
QTextStream s(&f);
|
|
||||||
QString line,call;
|
|
||||||
int nLoTW=0;
|
|
||||||
int i1;
|
|
||||||
QDateTime now=QDateTime::currentDateTime();
|
|
||||||
QDateTime callDateTime;
|
|
||||||
// Read and process the file of LoTW-active stations
|
|
||||||
while(!s.atEnd()) {
|
|
||||||
line=s.readLine();
|
|
||||||
i1=line.indexOf(",");
|
|
||||||
call=line.left(i1);
|
|
||||||
line=line.mid(i1+1);
|
|
||||||
i1=line.indexOf(",");
|
|
||||||
callDateTime=QDateTime::fromString(line.left(i1),"yyyy-MM-dd");
|
|
||||||
int ndays=callDateTime.daysTo(now);
|
|
||||||
if(ndays < 366) {
|
|
||||||
nLoTW++;
|
|
||||||
m_LoTW[call]=ndays;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// this must be the last statement of constructor
|
// this must be the last statement of constructor
|
||||||
if (!m_valid) throw std::runtime_error {"Fatal initialization exception"};
|
if (!m_valid) throw std::runtime_error {"Fatal initialization exception"};
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "astro.h"
|
#include "astro.h"
|
||||||
#include "MessageBox.hpp"
|
#include "MessageBox.hpp"
|
||||||
#include "NetworkAccessManager.hpp"
|
#include "NetworkAccessManager.hpp"
|
||||||
|
#include "LotWUsers.hpp"
|
||||||
|
|
||||||
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
|
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
|
||||||
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
|
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
|
||||||
@ -341,8 +342,8 @@ private:
|
|||||||
QSettings * m_settings;
|
QSettings * m_settings;
|
||||||
QScopedPointer<Ui::MainWindow> ui;
|
QScopedPointer<Ui::MainWindow> ui;
|
||||||
|
|
||||||
// other windows
|
|
||||||
Configuration m_config;
|
Configuration m_config;
|
||||||
|
LotWUsers m_lotw_users;
|
||||||
WSPRBandHopping m_WSPR_band_hopping;
|
WSPRBandHopping m_WSPR_band_hopping;
|
||||||
bool m_WSPR_tx_next;
|
bool m_WSPR_tx_next;
|
||||||
MessageBox m_rigErrorMessageBox;
|
MessageBox m_rigErrorMessageBox;
|
||||||
|
Loading…
Reference in New Issue
Block a user