Imported existing sources
This commit is contained in:
30
common.h
Normal file
30
common.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QDate>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using Host = QString;
|
||||||
|
using Process = QString;
|
||||||
|
using Filename = QString;
|
||||||
|
struct Logfile {
|
||||||
|
QString filename;
|
||||||
|
QString filepath;
|
||||||
|
qint64 filesize;
|
||||||
|
bool gzipCompressed;
|
||||||
|
};
|
||||||
|
using ScanResult = QHash<Host, QHash<Process, QHash<QDate, Logfile> > >;
|
||||||
|
Q_DECLARE_METATYPE(ScanResult)
|
||||||
|
|
||||||
|
inline bool scanResultEmpty(const ScanResult &result)
|
||||||
|
{
|
||||||
|
return result.isEmpty() || std::all_of(result.constBegin(), result.constEnd(), [](const auto &host){
|
||||||
|
return host.isEmpty() || std::all_of(host.constBegin(), host.constEnd(), [](const auto &process){
|
||||||
|
return process.isEmpty();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
104
dialogs/graphdialog.cpp
Normal file
104
dialogs/graphdialog.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include "graphdialog.h"
|
||||||
|
#include "ui_graphdialog.h"
|
||||||
|
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <QLineSeries>
|
||||||
|
#include <QDateTimeAxis>
|
||||||
|
#include <QValueAxis>
|
||||||
|
|
||||||
|
GraphDialog::GraphDialog(QSqlDatabase &database, QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
m_ui(std::make_unique<Ui::GraphDialog>()),
|
||||||
|
m_database(database)
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
QString sql;
|
||||||
|
|
||||||
|
if (m_database.driverName() == "QSQLITE")
|
||||||
|
{
|
||||||
|
sql = "SELECT "
|
||||||
|
"`Timestamp`, "
|
||||||
|
"COUNT(`Timestamp`) "
|
||||||
|
"FROM "
|
||||||
|
"`Logs` "
|
||||||
|
"GROUP BY "
|
||||||
|
"STRFTIME('%Y-%m-%d %H:00:00.000', `Timestamp`)";
|
||||||
|
}
|
||||||
|
else if (m_database.driverName() == "QMYSQL")
|
||||||
|
{
|
||||||
|
sql = "SELECT "
|
||||||
|
"`Timestamp`, "
|
||||||
|
"COUNT(`Timestamp`) "
|
||||||
|
"FROM "
|
||||||
|
"`Logs` "
|
||||||
|
"GROUP BY "
|
||||||
|
"DATE_FORMAT(`Timestamp`, '%Y-%m-%d %H:%i:00.000')";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qFatal("unknown sql driver");
|
||||||
|
|
||||||
|
QSqlQuery query(sql, m_database);
|
||||||
|
if (query.lastError().isValid())
|
||||||
|
qCritical() << query.lastError().text();
|
||||||
|
|
||||||
|
auto chart = new QtCharts::QChart();
|
||||||
|
chart->legend()->hide();
|
||||||
|
chart->setTitle(tr("Charts-Test"));
|
||||||
|
|
||||||
|
auto series = new QtCharts::QLineSeries();
|
||||||
|
chart->addSeries(series);
|
||||||
|
|
||||||
|
QDateTime minDt, maxDt;
|
||||||
|
int maxCount{};
|
||||||
|
|
||||||
|
while (query.next())
|
||||||
|
{
|
||||||
|
const auto timestampStr = query.value(0).toString();
|
||||||
|
qDebug() << timestampStr;
|
||||||
|
|
||||||
|
const auto timestamp = QDateTime::fromString(timestampStr, QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz"));
|
||||||
|
Q_ASSERT(timestamp.isValid());
|
||||||
|
|
||||||
|
if (minDt.isNull() || timestamp < minDt)
|
||||||
|
minDt = timestamp;
|
||||||
|
|
||||||
|
if (maxDt.isNull() || timestamp > maxDt)
|
||||||
|
maxDt = timestamp;
|
||||||
|
|
||||||
|
const auto count = query.value(1).toInt();
|
||||||
|
|
||||||
|
if (count > maxCount)
|
||||||
|
maxCount = count;
|
||||||
|
|
||||||
|
qDebug() << timestamp << count;
|
||||||
|
|
||||||
|
series->append(timestamp.toMSecsSinceEpoch(), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << minDt << maxDt;
|
||||||
|
|
||||||
|
auto axisX = new QtCharts::QDateTimeAxis;
|
||||||
|
axisX->setRange(minDt, maxDt);
|
||||||
|
axisX->setTickCount(20);
|
||||||
|
axisX->setFormat("HH:mm:ss");
|
||||||
|
axisX->setTitleText("Timestamp");
|
||||||
|
chart->addAxis(axisX, Qt::AlignBottom);
|
||||||
|
series->attachAxis(axisX);
|
||||||
|
|
||||||
|
auto axisY = new QtCharts::QValueAxis;
|
||||||
|
axisY->setMax(maxCount);
|
||||||
|
axisY->setLabelFormat("%i");
|
||||||
|
axisY->setTitleText("Logs count");
|
||||||
|
chart->addAxis(axisY, Qt::AlignLeft);
|
||||||
|
series->attachAxis(axisY);
|
||||||
|
|
||||||
|
m_ui->chartView->setRenderHint(QPainter::Antialiasing);
|
||||||
|
m_ui->chartView->setChart(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphDialog::~GraphDialog() = default;
|
23
dialogs/graphdialog.h
Normal file
23
dialogs/graphdialog.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class QSqlDatabase;
|
||||||
|
|
||||||
|
namespace Ui { class GraphDialog; }
|
||||||
|
|
||||||
|
class GraphDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GraphDialog(QSqlDatabase &database, QWidget *parent = nullptr);
|
||||||
|
~GraphDialog() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unique_ptr<Ui::GraphDialog> m_ui;
|
||||||
|
|
||||||
|
QSqlDatabase &m_database;
|
||||||
|
};
|
55
dialogs/graphdialog.ui
Normal file
55
dialogs/graphdialog.ui
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>GraphDialog</class>
|
||||||
|
<widget class="QDialog" name="GraphDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QtCharts::QChartView" name="chartView"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>QtCharts::QChartView</class>
|
||||||
|
<extends>QGraphicsView</extends>
|
||||||
|
<header>qchartview.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>GraphDialog</receiver>
|
||||||
|
<slot>close()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>199</x>
|
||||||
|
<y>278</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>199</x>
|
||||||
|
<y>149</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
36
dialogs/opendialog.cpp
Normal file
36
dialogs/opendialog.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include "opendialog.h"
|
||||||
|
#include "ui_opendialog.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
#include <QSqlError>
|
||||||
|
|
||||||
|
OpenDialog::OpenDialog(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
m_ui(std::make_unique<Ui::OpenDialog>())
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &OpenDialog::submit);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenDialog::~OpenDialog() = default;
|
||||||
|
|
||||||
|
QSqlDatabase OpenDialog::database()
|
||||||
|
{
|
||||||
|
return m_database;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenDialog::submit()
|
||||||
|
{
|
||||||
|
m_database = m_ui->databaseWidget->createConnection();
|
||||||
|
|
||||||
|
if (!m_database.open())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Could not open database!"), tr("Could not open database!") % "\n\n" % m_database.lastError().text());
|
||||||
|
m_database = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
accept();
|
||||||
|
}
|
27
dialogs/opendialog.h
Normal file
27
dialogs/opendialog.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Ui { class OpenDialog; }
|
||||||
|
|
||||||
|
class OpenDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit OpenDialog(QWidget *parent = nullptr);
|
||||||
|
~OpenDialog() override;
|
||||||
|
|
||||||
|
QSqlDatabase database();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void submit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unique_ptr<Ui::OpenDialog> m_ui;
|
||||||
|
|
||||||
|
QSqlDatabase m_database;
|
||||||
|
};
|
59
dialogs/opendialog.ui
Normal file
59
dialogs/opendialog.ui
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>OpenDialog</class>
|
||||||
|
<widget class="QDialog" name="OpenDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
|
||||||
|
<item>
|
||||||
|
<widget class="DatabaseWidget" name="databaseWidget" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>DatabaseWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>widgets/databasewidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>OpenDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
BIN
failed.png
Normal file
BIN
failed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 991 B |
77
gzipdevice.cpp
Normal file
77
gzipdevice.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include "gzipdevice.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
GzipDevice::GzipDevice(QFile &file, QObject *parent) :
|
||||||
|
QIODevice(parent),
|
||||||
|
m_file(file)
|
||||||
|
{
|
||||||
|
if (!m_file.isOpen())
|
||||||
|
throw std::runtime_error("file is not open");
|
||||||
|
|
||||||
|
setOpenMode(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
// Prepare inflater status
|
||||||
|
strm.zalloc = Z_NULL;
|
||||||
|
strm.zfree = Z_NULL;
|
||||||
|
strm.opaque = Z_NULL;
|
||||||
|
strm.avail_in = 0;
|
||||||
|
strm.next_in = Z_NULL;
|
||||||
|
|
||||||
|
// Initialize inflater
|
||||||
|
m_result = inflateInit2(&strm, 15 + 16);
|
||||||
|
if (m_result != Z_OK)
|
||||||
|
throw std::runtime_error("could not init z_stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
GzipDevice::~GzipDevice()
|
||||||
|
{
|
||||||
|
inflateEnd(&strm);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GzipDevice::isSequential() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GzipDevice::atEnd() const
|
||||||
|
{
|
||||||
|
return m_result == Z_STREAM_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 GzipDevice::readData(char *data, qint64 maxlen)
|
||||||
|
{
|
||||||
|
if (strm.avail_in == 0)
|
||||||
|
{
|
||||||
|
strm.next_in = reinterpret_cast<unsigned char *>(m_readBuffer);
|
||||||
|
strm.avail_in = m_file.read(m_readBuffer, m_readBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
strm.next_out = reinterpret_cast<unsigned char*>(data);
|
||||||
|
strm.avail_out = maxlen;
|
||||||
|
|
||||||
|
m_result = inflate(&strm, Z_NO_FLUSH);
|
||||||
|
|
||||||
|
switch (m_result) {
|
||||||
|
case Z_NEED_DICT:
|
||||||
|
throw std::runtime_error("decompression failed: Z_NEED_DICT");
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
throw std::runtime_error("decompression failed: Z_DATA_ERROR");
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
throw std::runtime_error("decompression failed: Z_MEM_ERROR");
|
||||||
|
case Z_STREAM_ERROR:
|
||||||
|
throw std::runtime_error("decompression failed: Z_STREAM_ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxlen-strm.avail_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 GzipDevice::writeData(const char *data, qint64 len)
|
||||||
|
{
|
||||||
|
Q_UNUSED(data)
|
||||||
|
Q_UNUSED(len)
|
||||||
|
qFatal("no writes allowed in GzipDevice!");
|
||||||
|
return -1;
|
||||||
|
}
|
28
gzipdevice.h
Normal file
28
gzipdevice.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QIODevice>
|
||||||
|
|
||||||
|
#include "zlib.h"
|
||||||
|
|
||||||
|
class QFile;
|
||||||
|
|
||||||
|
class GzipDevice : public QIODevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GzipDevice(QFile &file, QObject *parent = nullptr);
|
||||||
|
~GzipDevice() override;
|
||||||
|
|
||||||
|
bool isSequential() const override;
|
||||||
|
bool atEnd() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
qint64 readData(char *data, qint64 maxlen) override;
|
||||||
|
qint64 writeData(const char *data, qint64 len) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFile &m_file;
|
||||||
|
static constexpr std::size_t m_readBufferSize { 32 * 1024 };
|
||||||
|
char m_readBuffer[m_readBufferSize];
|
||||||
|
z_stream strm;
|
||||||
|
int m_result;
|
||||||
|
};
|
BIN
loading.gif
Normal file
BIN
loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
76
loganalyzer.pro
Normal file
76
loganalyzer.pro
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
QT += core gui widgets network sql charts
|
||||||
|
|
||||||
|
TARGET = loganalyzer
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000
|
||||||
|
|
||||||
|
CONFIG += c++14
|
||||||
|
QMAKE_CXXFLAGS_RELEASE -= -O1
|
||||||
|
QMAKE_CXXFLAGS_RELEASE -= -O2
|
||||||
|
QMAKE_CXXFLAGS_RELEASE *= -O3
|
||||||
|
QMAKE_CXXFLAGS_RELEASE -= -march=x86-64
|
||||||
|
QMAKE_CXXFLAGS_RELEASE -= -mtune=generic
|
||||||
|
QMAKE_CXXFLAGS_RELEASE *= -march=native
|
||||||
|
QMAKE_CXXFLAGS_RELEASE *= -mtune=native
|
||||||
|
|
||||||
|
LIBS += -lz
|
||||||
|
|
||||||
|
SOURCES += main.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
wizard/importwizard.cpp \
|
||||||
|
wizard/intropage.cpp \
|
||||||
|
wizard/databasepage.cpp \
|
||||||
|
wizard/importtypepage.cpp \
|
||||||
|
wizard/localimportpage.cpp \
|
||||||
|
wizard/conclusionpage.cpp \
|
||||||
|
wizard/remoteimportoverviewpage.cpp \
|
||||||
|
wizard/tablespage.cpp \
|
||||||
|
threads/tablecreatorthread.cpp \
|
||||||
|
models/checklistmodel.cpp \
|
||||||
|
threads/remotescannerthread.cpp \
|
||||||
|
wizard/remoteimportscanpage.cpp \
|
||||||
|
threads/importthread.cpp \
|
||||||
|
wizard/importprogresspage.cpp \
|
||||||
|
dialogs/opendialog.cpp \
|
||||||
|
widgets/fileselectionwidget.cpp \
|
||||||
|
widgets/databasewidget.cpp \
|
||||||
|
gzipdevice.cpp \
|
||||||
|
dialogs/graphdialog.cpp \
|
||||||
|
models/sqlrelationaltablemodel.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
mainwindow.h \
|
||||||
|
wizard/importwizard.h \
|
||||||
|
wizard/intropage.h \
|
||||||
|
wizard/databasepage.h \
|
||||||
|
wizard/importtypepage.h \
|
||||||
|
wizard/localimportpage.h \
|
||||||
|
wizard/conclusionpage.h \
|
||||||
|
wizard/remoteimportoverviewpage.h \
|
||||||
|
wizard/tablespage.h \
|
||||||
|
threads/tablecreatorthread.h \
|
||||||
|
models/checklistmodel.h \
|
||||||
|
threads/remotescannerthread.h \
|
||||||
|
wizard/remoteimportscanpage.h \
|
||||||
|
common.h \
|
||||||
|
threads/importthread.h \
|
||||||
|
wizard/importprogresspage.h \
|
||||||
|
dialogs/opendialog.h \
|
||||||
|
widgets/fileselectionwidget.h \
|
||||||
|
widgets/databasewidget.h \
|
||||||
|
gzipdevice.h \
|
||||||
|
dialogs/graphdialog.h \
|
||||||
|
models/sqlrelationaltablemodel.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui \
|
||||||
|
dialogs/opendialog.ui \
|
||||||
|
widgets/fileselectionwidget.ui \
|
||||||
|
widgets/databasewidget.ui \
|
||||||
|
wizard/intropage.ui \
|
||||||
|
wizard/databasepage.ui \
|
||||||
|
dialogs/graphdialog.ui
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
resources.qrc
|
17
main.cpp
Normal file
17
main.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include <QApplication>
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
qRegisterMetaType<ScanResult>();
|
||||||
|
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
217
mainwindow.cpp
Normal file
217
mainwindow.cpp
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
#include <QSqlRelationalDelegate>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QSqlRecord>
|
||||||
|
#include <QSqlRelation>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "wizard/importwizard.h"
|
||||||
|
#include "dialogs/opendialog.h"
|
||||||
|
#include "dialogs/graphdialog.h"
|
||||||
|
#include "models/sqlrelationaltablemodel.h"
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
m_ui(std::make_unique<Ui::MainWindow>())
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->actionNew->setShortcut(QKeySequence::New);
|
||||||
|
m_ui->actionOpen->setShortcut(QKeySequence::Open);
|
||||||
|
m_ui->actionQuit->setShortcut(QKeySequence::Quit);
|
||||||
|
|
||||||
|
connect(m_ui->actionNew, &QAction::triggered, this, &MainWindow::newClicked);
|
||||||
|
connect(m_ui->actionOpen, &QAction::triggered, this, &MainWindow::openClicked);
|
||||||
|
connect(m_ui->actionClose, &QAction::triggered, this, &MainWindow::closeClicked);
|
||||||
|
connect(m_ui->actionQuit, &QAction::triggered, QCoreApplication::instance(), &QCoreApplication::quit);
|
||||||
|
connect(m_ui->actionGraph, &QAction::triggered, this, &MainWindow::graphClicked);
|
||||||
|
|
||||||
|
for (QAction *action : { m_ui->actionTimestamp, m_ui->actionHost, m_ui->actionProcess, m_ui->actionFilename, m_ui->actionThread, m_ui->actionType, m_ui->actionMessage })
|
||||||
|
connect(action, &QAction::toggled, this, &MainWindow::showColumns);
|
||||||
|
|
||||||
|
connect(m_ui->lineEdit, &QLineEdit::returnPressed, this, &MainWindow::updateQuery);
|
||||||
|
connect(m_ui->pushButton, &QPushButton::pressed, this, &MainWindow::updateQuery);
|
||||||
|
|
||||||
|
m_ui->tableView->setItemDelegate(new QSqlRelationalDelegate(m_ui->tableView));
|
||||||
|
|
||||||
|
connect(m_ui->tableView, &QWidget::customContextMenuRequested, this, &MainWindow::showContextMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow() = default;
|
||||||
|
|
||||||
|
void MainWindow::newClicked()
|
||||||
|
{
|
||||||
|
ImportWizard wizard(this);
|
||||||
|
if (wizard.exec() == QDialog::Accepted && wizard.field("open").toBool())
|
||||||
|
{
|
||||||
|
m_ui->actionNew->setVisible(false);
|
||||||
|
m_ui->actionOpen->setVisible(false);
|
||||||
|
m_ui->actionClose->setVisible(true);
|
||||||
|
m_ui->actionGraph->setEnabled(true);
|
||||||
|
m_ui->lineEdit->setEnabled(true);
|
||||||
|
m_ui->pushButton->setEnabled(true);
|
||||||
|
|
||||||
|
m_database = wizard.database();
|
||||||
|
setupModel();
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::openClicked()
|
||||||
|
{
|
||||||
|
OpenDialog dialog(this);
|
||||||
|
if (dialog.exec() == QDialog::Accepted)
|
||||||
|
{
|
||||||
|
m_ui->actionNew->setVisible(false);
|
||||||
|
m_ui->actionOpen->setVisible(false);
|
||||||
|
m_ui->actionClose->setVisible(true);
|
||||||
|
m_ui->actionGraph->setEnabled(true);
|
||||||
|
m_ui->lineEdit->setEnabled(true);
|
||||||
|
m_ui->pushButton->setEnabled(true);
|
||||||
|
|
||||||
|
m_database = dialog.database();
|
||||||
|
setupModel();
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::closeClicked()
|
||||||
|
{
|
||||||
|
m_ui->actionNew->setVisible(true);
|
||||||
|
m_ui->actionOpen->setVisible(true);
|
||||||
|
m_ui->actionClose->setVisible(false);
|
||||||
|
m_ui->actionGraph->setEnabled(false);
|
||||||
|
m_ui->lineEdit->setEnabled(false);
|
||||||
|
m_ui->pushButton->setEnabled(false);
|
||||||
|
|
||||||
|
m_ui->tableView->setModel(nullptr);
|
||||||
|
m_model = nullptr;
|
||||||
|
m_database.close();
|
||||||
|
m_database = QSqlDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::graphClicked()
|
||||||
|
{
|
||||||
|
GraphDialog(m_database, this).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateQuery()
|
||||||
|
{
|
||||||
|
auto filter = m_ui->lineEdit->text();
|
||||||
|
if (!filter.trimmed().isEmpty())
|
||||||
|
{
|
||||||
|
filter.replace("||", "OR");
|
||||||
|
filter.replace("&&", "AND");
|
||||||
|
m_model->setFilter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_model->select())
|
||||||
|
QMessageBox::warning(this, tr("Query failed!"), tr("Query failed!") % "\n\n" % m_model->query().lastError().text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showColumns()
|
||||||
|
{
|
||||||
|
for (const auto &pair : {
|
||||||
|
std::make_pair(m_ui->actionTimestamp, ColumnTimestamp),
|
||||||
|
std::make_pair(m_ui->actionHost, ColumnHost),
|
||||||
|
std::make_pair(m_ui->actionProcess, ColumnProcess),
|
||||||
|
std::make_pair(m_ui->actionFilename, ColumnFilename),
|
||||||
|
std::make_pair(m_ui->actionThread, ColumnThread),
|
||||||
|
std::make_pair(m_ui->actionType, ColumnType),
|
||||||
|
std::make_pair(m_ui->actionMessage, ColumnMessage)
|
||||||
|
})
|
||||||
|
m_ui->tableView->setColumnHidden(std::get<1>(pair), !std::get<0>(pair)->isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showContextMenu(const QPoint &pos)
|
||||||
|
{
|
||||||
|
const auto index = m_ui->tableView->indexAt(pos);
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMenu menu(this);
|
||||||
|
const auto exec = [this,&menu,&pos](){ return menu.exec(m_ui->tableView->viewport()->mapToGlobal(pos)); };
|
||||||
|
|
||||||
|
qDebug() << m_model->record(index.row()).value(2);
|
||||||
|
const auto data = m_model->data(index, Qt::EditRole).toString();
|
||||||
|
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case ColumnTimestamp:
|
||||||
|
{
|
||||||
|
auto minute = menu.addAction(tr("Filter by minute"));
|
||||||
|
auto second = menu.addAction(tr("Filter by second"));
|
||||||
|
auto action = exec();
|
||||||
|
if (action == minute || action == second)
|
||||||
|
{
|
||||||
|
const auto format = QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz");
|
||||||
|
|
||||||
|
auto dateTime = QDateTime::fromString(data, format);
|
||||||
|
auto time = dateTime.time();
|
||||||
|
time.setHMS(time.hour(), time.minute(), action==minute ? 0 : time.second());
|
||||||
|
dateTime.setTime(time);
|
||||||
|
m_ui->lineEdit->setText(QString("`Timestamp` BETWEEN \"%0\" AND \"%1\"").arg(dateTime.toString(format), dateTime.addMSecs(action==minute ? 59999 : 999).toString(format)));
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ColumnHost:
|
||||||
|
if (menu.addAction(tr("Filter by host")) == exec())
|
||||||
|
{
|
||||||
|
m_ui->lineEdit->setText(QString("`Hosts`.`Name` = \"%0\"").arg(data));
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ColumnProcess:
|
||||||
|
if (menu.addAction(tr("Filter by process")) == exec())
|
||||||
|
{
|
||||||
|
m_ui->lineEdit->setText(QString("`Processes`.`Name` = \"%0\"").arg(data));
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ColumnFilename:
|
||||||
|
if (menu.addAction(tr("Filter by filename")) == exec())
|
||||||
|
{
|
||||||
|
m_ui->lineEdit->setText(QString("`Filenames`.`Name` = \"%0\"").arg(data));
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ColumnThread:
|
||||||
|
if (menu.addAction(tr("Filter by thread")) == exec())
|
||||||
|
{
|
||||||
|
m_ui->lineEdit->setText(QString("`Threads`.`Name` = \"%0\"").arg(data));
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ColumnType:
|
||||||
|
if (menu.addAction(tr("Filter by type")) == exec())
|
||||||
|
{
|
||||||
|
m_ui->lineEdit->setText(QString("`Types`.`Name` = \"%0\"").arg(data));
|
||||||
|
updateQuery();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setupModel()
|
||||||
|
{
|
||||||
|
m_ui->tableView->setModel(nullptr);
|
||||||
|
m_model = std::make_unique<SqlRelationalTableModel>(this, m_database);
|
||||||
|
m_model->setTable("Logs");
|
||||||
|
m_model->setRelation(ColumnHost, QSqlRelation("Hosts", "ID", "Name"));
|
||||||
|
m_model->setRelation(ColumnProcess, QSqlRelation("Processes", "ID", "Name"));
|
||||||
|
m_model->setRelation(ColumnFilename, QSqlRelation("Filenames", "ID", "Name"));
|
||||||
|
m_model->setRelation(ColumnThread, QSqlRelation("Threads", "ID", "Name"));
|
||||||
|
m_model->setRelation(ColumnType, QSqlRelation("Types", "ID", "Name"));
|
||||||
|
m_ui->tableView->setModel(m_model.get());
|
||||||
|
m_ui->tableView->setColumnHidden(ColumnID, true);
|
||||||
|
showColumns();
|
||||||
|
}
|
38
mainwindow.h
Normal file
38
mainwindow.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class QSqlRelationalTableModel;
|
||||||
|
|
||||||
|
namespace Ui { class MainWindow; }
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void newClicked();
|
||||||
|
void openClicked();
|
||||||
|
void closeClicked();
|
||||||
|
void graphClicked();
|
||||||
|
void updateQuery();
|
||||||
|
void showColumns();
|
||||||
|
void showContextMenu(const QPoint &pos);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupModel();
|
||||||
|
|
||||||
|
enum { ColumnID, ColumnTimestamp, ColumnHost, ColumnProcess, ColumnFilename, ColumnThread, ColumnType, ColumnMessage };
|
||||||
|
|
||||||
|
const std::unique_ptr<Ui::MainWindow> m_ui;
|
||||||
|
|
||||||
|
QSqlDatabase m_database;
|
||||||
|
std::unique_ptr<QSqlRelationalTableModel> m_model;
|
||||||
|
};
|
216
mainwindow.ui
Normal file
216
mainwindow.ui
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>965</width>
|
||||||
|
<height>578</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralWidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Filter</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView">
|
||||||
|
<property name="contextMenuPolicy">
|
||||||
|
<enum>Qt::CustomContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="showGrid">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menuBar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>965</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menu_File">
|
||||||
|
<property name="title">
|
||||||
|
<string>&File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionNew"/>
|
||||||
|
<addaction name="actionOpen"/>
|
||||||
|
<addaction name="actionClose"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionQuit"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuView">
|
||||||
|
<property name="title">
|
||||||
|
<string>&View</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionTimestamp"/>
|
||||||
|
<addaction name="actionHost"/>
|
||||||
|
<addaction name="actionProcess"/>
|
||||||
|
<addaction name="actionFilename"/>
|
||||||
|
<addaction name="actionThread"/>
|
||||||
|
<addaction name="actionType"/>
|
||||||
|
<addaction name="actionMessage"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuTools">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Tools</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionGraph"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menu_File"/>
|
||||||
|
<addaction name="menuView"/>
|
||||||
|
<addaction name="menuTools"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusBar"/>
|
||||||
|
<action name="actionNew">
|
||||||
|
<property name="text">
|
||||||
|
<string>&New project</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionOpen">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Open</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionQuit">
|
||||||
|
<property name="text">
|
||||||
|
<string>Quit</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionClose">
|
||||||
|
<property name="text">
|
||||||
|
<string>Close Database</string>
|
||||||
|
</property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionTimestamp">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Timestamp</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHost">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Host</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionProcess">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Process</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionFilename">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Filename</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionThread">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Thread</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionType">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Type</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionMessage">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Message</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionGraph">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Graph</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionRawData">
|
||||||
|
<property name="text">
|
||||||
|
<string>Raw data</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
141
models/checklistmodel.cpp
Normal file
141
models/checklistmodel.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include "checklistmodel.h"
|
||||||
|
|
||||||
|
ChecklistModel::ChecklistModel(QObject *parent) :
|
||||||
|
QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ChecklistModel::ChecklistModel(const QStringList &items, QObject *parent) :
|
||||||
|
QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
for (const auto &item : items)
|
||||||
|
m_items.append(std::make_pair(item, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
ChecklistModel::ChecklistModel(const QList<std::pair<QString, bool> > &items, QObject *parent) :
|
||||||
|
QAbstractListModel(parent),
|
||||||
|
m_items(items)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ChecklistModel::items() const
|
||||||
|
{
|
||||||
|
QStringList items;
|
||||||
|
|
||||||
|
for (const auto &pair : m_items)
|
||||||
|
items.append(std::get<0>(pair));
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChecklistModel::setItems(const QStringList &items)
|
||||||
|
{
|
||||||
|
emit beginResetModel();
|
||||||
|
|
||||||
|
m_items.clear();
|
||||||
|
|
||||||
|
for (const auto &item : items)
|
||||||
|
m_items.append(std::make_pair(item, true));
|
||||||
|
|
||||||
|
emit endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChecklistModel::setItems(const QList<std::pair<QString, bool> > &items)
|
||||||
|
{
|
||||||
|
emit beginResetModel();
|
||||||
|
|
||||||
|
m_items = items;
|
||||||
|
|
||||||
|
emit endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ChecklistModel::enabledItems() const
|
||||||
|
{
|
||||||
|
QStringList items;
|
||||||
|
|
||||||
|
for (const auto &pair : m_items)
|
||||||
|
if (std::get<1>(pair))
|
||||||
|
items.append(std::get<0>(pair));
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ChecklistModel::disabledItems() const
|
||||||
|
{
|
||||||
|
QStringList items;
|
||||||
|
|
||||||
|
for (const auto &pair : m_items)
|
||||||
|
if (!std::get<1>(pair))
|
||||||
|
items.append(std::get<0>(pair));
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChecklistModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
return m_items.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex ChecklistModel::sibling(int row, int column, const QModelIndex &idx) const
|
||||||
|
{
|
||||||
|
if (!idx.isValid() || column != 0 || row >= m_items.count() || row < 0)
|
||||||
|
return QModelIndex();
|
||||||
|
return createIndex(row, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<int, QVariant> ChecklistModel::itemData(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::ParentIsInvalid))
|
||||||
|
return QMap<int, QVariant>{};
|
||||||
|
const auto &item = m_items.at(index.row());
|
||||||
|
return QMap<int, QVariant>{{
|
||||||
|
std::make_pair<int>(Qt::DisplayRole, std::get<0>(item)),
|
||||||
|
std::make_pair<int>(Qt::EditRole, std::get<0>(item)),
|
||||||
|
std::make_pair<int>(Qt::CheckStateRole, std::get<1>(item) ? Qt::Checked : Qt::Unchecked)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChecklistModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
|
||||||
|
{
|
||||||
|
if (roles.isEmpty())
|
||||||
|
return false;
|
||||||
|
if (std::any_of(roles.keyBegin(), roles.keyEnd(), [](int role) { return role != Qt::CheckStateRole; }))
|
||||||
|
return false;
|
||||||
|
auto roleIter = roles.constFind(Qt::CheckStateRole);
|
||||||
|
Q_ASSERT(roleIter != roles.constEnd());
|
||||||
|
return setData(index, roleIter.value(), roleIter.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ChecklistModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (index.row() < 0 || index.row() >= m_items.size())
|
||||||
|
return QVariant();
|
||||||
|
const auto &item = m_items.at(index.row());
|
||||||
|
if (role == Qt::DisplayRole || role == Qt::EditRole)
|
||||||
|
return std::get<0>(item);
|
||||||
|
if (role == Qt::CheckStateRole)
|
||||||
|
return std::get<1>(item) ? Qt::Checked : Qt::Unchecked;
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChecklistModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (index.row() >= 0 && index.row() < m_items.size() && role == Qt::CheckStateRole)
|
||||||
|
{
|
||||||
|
auto &item = m_items[index.row()];
|
||||||
|
std::get<1>(item) = value.toBool();
|
||||||
|
emit dataChanged(index, index, { Qt::CheckStateRole });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags ChecklistModel::flags(const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QAbstractListModel::flags(index);
|
||||||
|
return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable;
|
||||||
|
}
|
33
models/checklistmodel.h
Normal file
33
models/checklistmodel.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
class Q_CORE_EXPORT ChecklistModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ChecklistModel(QObject *parent = nullptr);
|
||||||
|
explicit ChecklistModel(const QStringList &items, QObject *parent = nullptr);
|
||||||
|
explicit ChecklistModel(const QList<std::pair<QString, bool> > &strings, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QStringList items() const;
|
||||||
|
void setItems(const QStringList &items);
|
||||||
|
void setItems(const QList<std::pair<QString, bool> > &items);
|
||||||
|
QStringList enabledItems() const;
|
||||||
|
QStringList disabledItems() const;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override;
|
||||||
|
QMap<int, QVariant> itemData(const QModelIndex &index) const override;
|
||||||
|
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<std::pair<QString, bool> > m_items;
|
||||||
|
};
|
14
models/sqlrelationaltablemodel.cpp
Normal file
14
models/sqlrelationaltablemodel.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "sqlrelationaltablemodel.h"
|
||||||
|
|
||||||
|
SqlRelationalTableModel::SqlRelationalTableModel(QObject *parent, QSqlDatabase db) :
|
||||||
|
QSqlRelationalTableModel(parent, db)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant SqlRelationalTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (orientation == Qt::Vertical)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return QSqlRelationalTableModel::headerData(section, orientation, role);
|
||||||
|
}
|
13
models/sqlrelationaltablemodel.h
Normal file
13
models/sqlrelationaltablemodel.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QSqlRelationalTableModel>
|
||||||
|
|
||||||
|
class SqlRelationalTableModel : public QSqlRelationalTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SqlRelationalTableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
|
||||||
|
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
|
};
|
7
resources.qrc
Normal file
7
resources.qrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/loganalyzer">
|
||||||
|
<file>loading.gif</file>
|
||||||
|
<file>failed.png</file>
|
||||||
|
<file>succeeded.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
BIN
succeeded.png
Normal file
BIN
succeeded.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
353
threads/importthread.cpp
Normal file
353
threads/importthread.cpp
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
#include "importthread.h"
|
||||||
|
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "gzipdevice.h"
|
||||||
|
|
||||||
|
ImportThread::ImportThread(QSqlDatabase &database, const ScanResult &result, const QString &timeFormat, QObject *parent) :
|
||||||
|
QThread(parent),
|
||||||
|
m_database(database),
|
||||||
|
m_result(result),
|
||||||
|
m_timeFormat(timeFormat),
|
||||||
|
m_queryInsertLog(database),
|
||||||
|
m_queryFindHost(database),
|
||||||
|
m_queryInsertHost(database),
|
||||||
|
m_queryFindProcess(database),
|
||||||
|
m_queryInsertProcess(database),
|
||||||
|
m_queryFindFilename(database),
|
||||||
|
m_queryInsertFilename(database),
|
||||||
|
m_queryFindThread(database),
|
||||||
|
m_queryInsertThread(database),
|
||||||
|
m_queryFindType(database),
|
||||||
|
m_queryInsertType(database),
|
||||||
|
m_totalSize(calculateTotalSize())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportThread::run()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
int query { 0 };
|
||||||
|
typedef std::tuple<QString, QSqlQuery&, QString> Row;
|
||||||
|
for (const auto &tuple : {
|
||||||
|
Row(tr("insert log"), m_queryInsertLog, "INSERT INTO `Logs` (`Timestamp`, `HostID`, `ProcessID`, `FilenameID`, `ThreadID`, `TypeID`, `Message`) "
|
||||||
|
"VALUES (:Timestamp, :HostID, :ProcessID, :FilenameID, :ThreadID, :TypeID, :Message);"),
|
||||||
|
Row(tr("find host"), m_queryFindHost, "SELECT `ID` FROM `Hosts` WHERE `Name` = :Name;"),
|
||||||
|
Row(tr("insert host"), m_queryInsertHost, "INSERT INTO `Hosts` (`Name`) VALUES (:Name);"),
|
||||||
|
Row(tr("find host"), m_queryFindProcess, "SELECT `ID` FROM `Processes` WHERE `Name` = :Name;"),
|
||||||
|
Row(tr("insert host"), m_queryInsertProcess, "INSERT INTO `Processes` (`Name`) VALUES (:Name);"),
|
||||||
|
Row(tr("find host"), m_queryFindFilename, "SELECT `ID` FROM `Filenames` WHERE `Name` = :Name;"),
|
||||||
|
Row(tr("insert host"), m_queryInsertFilename, "INSERT INTO `Filenames` (`Name`) VALUES (:Name);"),
|
||||||
|
Row(tr("find host"), m_queryFindThread, "SELECT `ID` FROM `Threads` WHERE `Name` = :Name;"),
|
||||||
|
Row(tr("insert host"), m_queryInsertThread, "INSERT INTO `Threads` (`Name`) VALUES (:Name);"),
|
||||||
|
Row(tr("find host"), m_queryFindType, "SELECT `ID` FROM `Types` WHERE `Name` = :Name;"),
|
||||||
|
Row(tr("insert host"), m_queryInsertType, "INSERT INTO `Types` (`Name`) VALUES (:Name);")
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit statusUpdate(tr("Preparing query to %0...").arg(std::get<0>(tuple)));
|
||||||
|
if (!std::get<1>(tuple).prepare(std::get<2>(tuple)))
|
||||||
|
{
|
||||||
|
emit statusUpdate(tr("Failed."));
|
||||||
|
emit logMessage(tr("Could not prepare query to %0: %1").arg(std::get<0>(tuple), std::get<1>(tuple).lastError().text()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit progressUpdate(query++, 11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 processedSize { 0 };
|
||||||
|
|
||||||
|
for (auto hostsIter = m_result.constBegin(); hostsIter != m_result.constEnd(); hostsIter++)
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_queryInsertLog.bindValue(":HostID", getHostID(hostsIter.key()));
|
||||||
|
|
||||||
|
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_queryInsertLog.bindValue(":ProcessID", getProcessID(processesIter.key()));
|
||||||
|
|
||||||
|
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_queryInsertLog.bindValue(":FilenameID", getFilenameID(datesIter.value().filename));
|
||||||
|
|
||||||
|
emit logMessage(datesIter.value().filename);
|
||||||
|
|
||||||
|
QFile file(datesIter.value().filepath);
|
||||||
|
QFile::OpenMode flags = QIODevice::ReadOnly;
|
||||||
|
if (!datesIter.value().gzipCompressed)
|
||||||
|
flags |= QIODevice::Text;
|
||||||
|
if (!file.open(flags))
|
||||||
|
{
|
||||||
|
emit logMessage(tr("Could not open logfile: %0").arg(file.errorString()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
QDateTime dateTime;
|
||||||
|
QString thread { "main" };
|
||||||
|
QString type;
|
||||||
|
QString message;
|
||||||
|
} test;
|
||||||
|
|
||||||
|
const auto insert = [&test,this,&processedSize,&file](){
|
||||||
|
m_queryInsertLog.bindValue(":Timestamp", test.dateTime.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz")));
|
||||||
|
m_queryInsertLog.bindValue(":ThreadID", getThreadID(test.thread));
|
||||||
|
m_queryInsertLog.bindValue(":TypeID", getTypeID(test.type));
|
||||||
|
m_queryInsertLog.bindValue(":Message", test.message);
|
||||||
|
if (!m_queryInsertLog.exec())
|
||||||
|
emit logMessage(tr("could not execute query to insert log: %0").arg(m_queryInsertLog.lastError().text()));
|
||||||
|
|
||||||
|
m_logsInserted++;
|
||||||
|
const auto now = QDateTime::currentDateTime();
|
||||||
|
if (m_lastProgressUpdate.isNull() || m_lastProgressUpdate.msecsTo(now) >= 100)
|
||||||
|
{
|
||||||
|
emit statusUpdate(tr("%0 logs inserted...").arg(m_logsInserted));
|
||||||
|
emit progressUpdate(processedSize + file.pos(), m_totalSize);
|
||||||
|
m_lastProgressUpdate = now;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int indentionOffset;
|
||||||
|
|
||||||
|
if (!m_database.transaction())
|
||||||
|
{
|
||||||
|
emit statusUpdate(tr("Aborted."));
|
||||||
|
emit logMessage(tr("Could not start transaction: %0").arg(m_database.lastError().text()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const std::unique_ptr<GzipDevice> gzipProxy([&datesIter,&file](){
|
||||||
|
if (datesIter.value().gzipCompressed)
|
||||||
|
return std::make_unique<GzipDevice>(file);
|
||||||
|
return std::unique_ptr<GzipDevice>{};
|
||||||
|
}());
|
||||||
|
|
||||||
|
QTextStream textStream(gzipProxy != nullptr ? static_cast<QIODevice*>(gzipProxy.get()) : &file);
|
||||||
|
while(!textStream.atEnd())
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
{
|
||||||
|
if (!m_database.rollback())
|
||||||
|
{
|
||||||
|
emit statusUpdate(tr("Aborted."));
|
||||||
|
emit logMessage(tr("Could not rollback transaction: %0").arg(m_database.lastError().text()));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto line = textStream.readLine();
|
||||||
|
const auto match = m_lineRegex.match(line);
|
||||||
|
if (match.hasMatch())
|
||||||
|
{
|
||||||
|
if (!test.dateTime.isNull())
|
||||||
|
insert();
|
||||||
|
|
||||||
|
test.message = match.captured(4);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto threadMatch = m_threadRegex.match(test.message);
|
||||||
|
if (threadMatch.hasMatch())
|
||||||
|
test.thread = threadMatch.captured(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
test.dateTime = { datesIter.key(), QTime::fromString(match.captured(2), m_timeFormat) };
|
||||||
|
|
||||||
|
test.type = match.captured(3);
|
||||||
|
test.type = test.type.left(test.type.indexOf(':'));
|
||||||
|
|
||||||
|
indentionOffset = match.captured(1).length();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!test.dateTime.isNull())
|
||||||
|
{
|
||||||
|
if (line.length() >= indentionOffset &&
|
||||||
|
std::all_of(line.constBegin(), line.constBegin() + indentionOffset, [](const QChar &c){ return c == ' '; }))
|
||||||
|
line.remove(0, indentionOffset);
|
||||||
|
|
||||||
|
test.message.append("\n");
|
||||||
|
test.message.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!test.dateTime.isNull())
|
||||||
|
insert();
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
emit logMessage(tr("Aborted: %0").arg(ex.what()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_database.commit())
|
||||||
|
{
|
||||||
|
emit statusUpdate(tr("Aborted."));
|
||||||
|
emit logMessage(tr("Could not commit transaction: %0").arg(m_database.lastError().text()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processedSize += file.size();
|
||||||
|
emit statusUpdate(tr("%0 logs inserted...").arg(m_logsInserted));
|
||||||
|
emit progressUpdate(processedSize, m_totalSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportThread::getHostID(const QString &host)
|
||||||
|
{
|
||||||
|
const auto iter = m_hosts.find(host);
|
||||||
|
if (iter != m_hosts.constEnd())
|
||||||
|
return *iter;
|
||||||
|
|
||||||
|
m_queryFindHost.bindValue(":Name", host);
|
||||||
|
if (!m_queryFindHost.exec())
|
||||||
|
qFatal("could not execute query to find host: %s", qPrintable(m_queryFindHost.lastError().text()));
|
||||||
|
|
||||||
|
if (m_queryFindHost.next())
|
||||||
|
{
|
||||||
|
const auto id = m_queryFindHost.value(0).toInt();
|
||||||
|
m_hosts.insert(host, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_queryInsertHost.bindValue(":Name", host);
|
||||||
|
if (!m_queryInsertHost.exec())
|
||||||
|
qFatal("could not execute query to insert host: %s", qPrintable(m_queryInsertHost.lastError().text()));
|
||||||
|
|
||||||
|
const auto id = m_queryInsertHost.lastInsertId().toInt();
|
||||||
|
m_hosts.insert(host, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportThread::getProcessID(const QString &process)
|
||||||
|
{
|
||||||
|
const auto iter = m_processes.find(process);
|
||||||
|
if (iter != m_processes.constEnd())
|
||||||
|
return *iter;
|
||||||
|
|
||||||
|
m_queryFindProcess.bindValue(":Name", process);
|
||||||
|
if (!m_queryFindProcess.exec())
|
||||||
|
qFatal("could not execute query to find process: %s", qPrintable(m_queryFindProcess.lastError().text()));
|
||||||
|
|
||||||
|
if (m_queryFindProcess.next())
|
||||||
|
{
|
||||||
|
const auto id = m_queryFindProcess.value(0).toInt();
|
||||||
|
m_processes.insert(process, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_queryInsertProcess.bindValue(":Name", process);
|
||||||
|
if (!m_queryInsertProcess.exec())
|
||||||
|
qFatal("could not execute query to insert process: %s", qPrintable(m_queryInsertProcess.lastError().text()));
|
||||||
|
|
||||||
|
const auto id = m_queryInsertProcess.lastInsertId().toInt();
|
||||||
|
m_processes.insert(process, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportThread::getFilenameID(const QString &filename)
|
||||||
|
{
|
||||||
|
const auto iter = m_filenames.find(filename);
|
||||||
|
if (iter != m_filenames.constEnd())
|
||||||
|
return *iter;
|
||||||
|
|
||||||
|
m_queryFindFilename.bindValue(":Name", filename);
|
||||||
|
if (!m_queryFindFilename.exec())
|
||||||
|
qFatal("could not execute query to find filename: %s", qPrintable(m_queryFindFilename.lastError().text()));
|
||||||
|
|
||||||
|
if (m_queryFindFilename.next())
|
||||||
|
{
|
||||||
|
const auto id = m_queryFindFilename.value(0).toInt();
|
||||||
|
m_filenames.insert(filename, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_queryInsertFilename.bindValue(":Name", filename);
|
||||||
|
if (!m_queryInsertFilename.exec())
|
||||||
|
qFatal("could not execute query to insert filename: %s", qPrintable(m_queryInsertFilename.lastError().text()));
|
||||||
|
|
||||||
|
const auto id = m_queryInsertFilename.lastInsertId().toInt();
|
||||||
|
m_filenames.insert(filename, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportThread::getThreadID(const QString &thread)
|
||||||
|
{
|
||||||
|
const auto iter = m_threads.find(thread);
|
||||||
|
if (iter != m_threads.constEnd())
|
||||||
|
return *iter;
|
||||||
|
|
||||||
|
m_queryFindThread.bindValue(":Name", thread);
|
||||||
|
if (!m_queryFindThread.exec())
|
||||||
|
qFatal("could not execute query to find thread: %s", qPrintable(m_queryFindThread.lastError().text()));
|
||||||
|
|
||||||
|
if (m_queryFindThread.next())
|
||||||
|
{
|
||||||
|
const auto id = m_queryFindThread.value(0).toInt();
|
||||||
|
m_threads.insert(thread, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_queryInsertThread.bindValue(":Name", thread);
|
||||||
|
if (!m_queryInsertThread.exec())
|
||||||
|
qFatal("could not execute query to insert thread: %s", qPrintable(m_queryInsertThread.lastError().text()));
|
||||||
|
|
||||||
|
const auto id = m_queryInsertThread.lastInsertId().toInt();
|
||||||
|
m_threads.insert(thread, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportThread::getTypeID(const QString &type)
|
||||||
|
{
|
||||||
|
const auto iter = m_types.find(type);
|
||||||
|
if (iter != m_types.constEnd())
|
||||||
|
return *iter;
|
||||||
|
|
||||||
|
m_queryFindType.bindValue(":Name", type);
|
||||||
|
if (!m_queryFindType.exec())
|
||||||
|
qFatal("could not execute query to find type: %s", qPrintable(m_queryFindType.lastError().text()));
|
||||||
|
|
||||||
|
if (m_queryFindType.next())
|
||||||
|
{
|
||||||
|
const auto id = m_queryFindType.value(0).toInt();
|
||||||
|
m_types.insert(type, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_queryInsertType.bindValue(":Name", type);
|
||||||
|
if (!m_queryInsertType.exec())
|
||||||
|
qFatal("could not execute query to insert type: %s", qPrintable(m_queryInsertType.lastError().text()));
|
||||||
|
|
||||||
|
const auto id = m_queryInsertType.lastInsertId().toInt();
|
||||||
|
m_types.insert(type, id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 ImportThread::calculateTotalSize() const
|
||||||
|
{
|
||||||
|
qint64 totalSize { 0 };
|
||||||
|
for (auto hostsIter = m_result.constBegin(); hostsIter != m_result.constEnd(); hostsIter++)
|
||||||
|
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||||
|
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||||
|
totalSize += datesIter.value().filesize;
|
||||||
|
return totalSize;
|
||||||
|
}
|
63
threads/importthread.h
Normal file
63
threads/importthread.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class QSqlDatabase;
|
||||||
|
|
||||||
|
class ImportThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ImportThread(QSqlDatabase &database, const ScanResult &result, const QString &timeFormat, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void statusUpdate(const QString &message);
|
||||||
|
void progressUpdate(qint64 finished, qint64 total);
|
||||||
|
void logMessage(const QString &message);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int getHostID(const QString &host);
|
||||||
|
int getProcessID(const QString &process);
|
||||||
|
int getFilenameID(const QString &filename);
|
||||||
|
int getThreadID(const QString &thread);
|
||||||
|
int getTypeID(const QString &type);
|
||||||
|
|
||||||
|
qint64 calculateTotalSize() const;
|
||||||
|
|
||||||
|
QSqlDatabase &m_database;
|
||||||
|
ScanResult m_result;
|
||||||
|
QString m_timeFormat;
|
||||||
|
|
||||||
|
QHash<QString, int> m_hosts;
|
||||||
|
QHash<QString, int> m_processes;
|
||||||
|
QHash<QString, int> m_filenames;
|
||||||
|
QHash<QString, int> m_threads;
|
||||||
|
QHash<QString, int> m_types;
|
||||||
|
|
||||||
|
QSqlQuery m_queryInsertLog;
|
||||||
|
QSqlQuery m_queryFindHost;
|
||||||
|
QSqlQuery m_queryInsertHost;
|
||||||
|
QSqlQuery m_queryFindProcess;
|
||||||
|
QSqlQuery m_queryInsertProcess;
|
||||||
|
QSqlQuery m_queryFindFilename;
|
||||||
|
QSqlQuery m_queryInsertFilename;
|
||||||
|
QSqlQuery m_queryFindThread;
|
||||||
|
QSqlQuery m_queryInsertThread;
|
||||||
|
QSqlQuery m_queryFindType;
|
||||||
|
QSqlQuery m_queryInsertType;
|
||||||
|
|
||||||
|
const qint64 m_totalSize;
|
||||||
|
quint64 m_logsInserted { 0 };
|
||||||
|
QDateTime m_lastProgressUpdate;
|
||||||
|
|
||||||
|
const QRegularExpression m_lineRegex { "^(([0-9]+:[0-9]+:[0-9]+(?:\\.[0-9]+)?) (FATAL: |ERROR: |WARNING: |INFO: |DEBUG: |DEBUG1: |DEBUG2: ))(.*)$" };
|
||||||
|
const QRegularExpression m_threadRegex { "----- Thread context: (.*) -----$" };
|
||||||
|
};
|
86
threads/remotescannerthread.cpp
Normal file
86
threads/remotescannerthread.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#include "remotescannerthread.h"
|
||||||
|
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
|
||||||
|
RemoteScannerThread::RemoteScannerThread(const QString &dir, QObject *parent) :
|
||||||
|
QThread(parent),
|
||||||
|
m_dir(dir)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteScannerThread::run()
|
||||||
|
{
|
||||||
|
scanForHosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteScannerThread::scanForHosts()
|
||||||
|
{
|
||||||
|
if (m_files)
|
||||||
|
qFatal("thread was already run");
|
||||||
|
|
||||||
|
QDirIterator iter(m_dir, QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
while (iter.hasNext())
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QFileInfo fileInfo(iter.next());
|
||||||
|
|
||||||
|
const auto host = fileInfo.fileName();
|
||||||
|
|
||||||
|
emit logMessage(tr("Scanning host %0...").arg(host));
|
||||||
|
|
||||||
|
scanForLogfiles(host, fileInfo.absoluteFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
emit progressUpdate(m_files, m_files - m_valid);
|
||||||
|
emit finished(m_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteScannerThread::scanForLogfiles(const QString &hostname, const QString &hostDir)
|
||||||
|
{
|
||||||
|
auto &hostEntry = m_result[hostname];
|
||||||
|
|
||||||
|
QDirIterator dirIter(hostDir, { "*.log.gz" }, QDir::Files);
|
||||||
|
while (dirIter.hasNext())
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QFileInfo fileInfo(dirIter.next());
|
||||||
|
|
||||||
|
m_files++;
|
||||||
|
|
||||||
|
const auto match = m_fileExpression.match(fileInfo.fileName());
|
||||||
|
if (!match.hasMatch())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto process = match.captured(1);
|
||||||
|
if (process.endsWith(QStringLiteral(".olog")))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto date = QDate::fromString(match.captured(2), QStringLiteral("yyyyMMdd"));
|
||||||
|
if (date.isNull())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_valid++;
|
||||||
|
|
||||||
|
Q_ASSERT(!hostEntry[process].contains(date));
|
||||||
|
|
||||||
|
hostEntry[process].insert(date, {
|
||||||
|
hostname % "/" % fileInfo.fileName(),
|
||||||
|
fileInfo.absoluteFilePath(),
|
||||||
|
fileInfo.size(),
|
||||||
|
true
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto now = QDateTime::currentDateTime();
|
||||||
|
if (m_lastUpdate.isNull() || m_lastUpdate.msecsTo(now) >= 100)
|
||||||
|
{
|
||||||
|
emit progressUpdate(m_files, m_files - m_valid);
|
||||||
|
m_lastUpdate = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
threads/remotescannerthread.h
Normal file
40
threads/remotescannerthread.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDate>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class RemoteScannerThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
const QRegularExpression m_fileExpression { "^(.+)-([0-9]{8})\\.log?" };
|
||||||
|
|
||||||
|
public:
|
||||||
|
RemoteScannerThread(const QString &dir, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void progressUpdate(int totalFiles, int skippedFiles);
|
||||||
|
void logMessage(const QString &message);
|
||||||
|
void finished(const ScanResult &result);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void scanForHosts();
|
||||||
|
void scanForLogfiles(const QString &hostname, const QString &hostDir);
|
||||||
|
|
||||||
|
QString m_dir;
|
||||||
|
|
||||||
|
ScanResult m_result;
|
||||||
|
|
||||||
|
int m_files { 0 };
|
||||||
|
int m_valid { 0 };
|
||||||
|
|
||||||
|
QDateTime m_lastUpdate;
|
||||||
|
};
|
101
threads/tablecreatorthread.cpp
Normal file
101
threads/tablecreatorthread.cpp
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#include "tablecreatorthread.h"
|
||||||
|
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
const QStringList TableCreatorThread::m_tables { "Hosts", "Processes", "Filenames", "Threads", "Types", "Logs" };
|
||||||
|
|
||||||
|
TableCreatorThread::TableCreatorThread(QSqlDatabase &database, QObject *parent) :
|
||||||
|
QThread(parent), m_database(database)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList &TableCreatorThread::tables()
|
||||||
|
{
|
||||||
|
return m_tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TableCreatorThread::run()
|
||||||
|
{
|
||||||
|
int index { 0 };
|
||||||
|
|
||||||
|
for (const QString tableName : m_tables)
|
||||||
|
{
|
||||||
|
if (isInterruptionRequested())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto sql = [&tableName,type=m_database.driverName()]() -> QString {
|
||||||
|
if (tableName == "Logs")
|
||||||
|
{
|
||||||
|
if (type == "QSQLITE")
|
||||||
|
{
|
||||||
|
return "CREATE TABLE IF NOT EXISTS `Logs` ("
|
||||||
|
"`ID` INTEGER PRIMARY KEY, "
|
||||||
|
"`Timestamp` TEXT NOT NULL, "
|
||||||
|
"`HostID` INTEGER NOT NULL, "
|
||||||
|
"`ProcessID` INTEGER NOT NULL, "
|
||||||
|
"`FilenameID` INTEGER NOT NULL, "
|
||||||
|
"`ThreadID` INTEGER NOT NULL, "
|
||||||
|
"`TypeID` INTEGER NOT NULL, "
|
||||||
|
"`Message` TEXT NOT NULL, "
|
||||||
|
"FOREIGN KEY (`HostID`) REFERENCES `Hosts`(`ID`), "
|
||||||
|
"FOREIGN KEY (`ProcessID`) REFERENCES `Processes`(`ID`), "
|
||||||
|
"FOREIGN KEY (`FilenameID`) REFERENCES `Filenames`(`ID`), "
|
||||||
|
"FOREIGN KEY (`ThreadID`) REFERENCES `Threads`(`ID`), "
|
||||||
|
"FOREIGN KEY (`TypeID`) REFERENCES `Types`(`ID`)"
|
||||||
|
");";
|
||||||
|
}
|
||||||
|
else if (type == "QMYSQL")
|
||||||
|
{
|
||||||
|
return "CREATE TABLE IF NOT EXISTS `Logs` ("
|
||||||
|
"`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT, "
|
||||||
|
"`Timestamp` DATETIME NOT NULL, "
|
||||||
|
"`HostID` INT UNSIGNED NOT NULL, "
|
||||||
|
"`ProcessID` INT UNSIGNED NOT NULL, "
|
||||||
|
"`FilenameID` INT UNSIGNED NOT NULL, "
|
||||||
|
"`ThreadID` INT UNSIGNED NOT NULL, "
|
||||||
|
"`TypeID` INT UNSIGNED NOT NULL, "
|
||||||
|
"`Message` LONGTEXT NOT NULL, "
|
||||||
|
"PRIMARY KEY(`ID`), "
|
||||||
|
"INDEX(`Timestamp`), "
|
||||||
|
"FOREIGN KEY (`HostID`) REFERENCES `Hosts`(`ID`), "
|
||||||
|
"FOREIGN KEY (`ProcessID`) REFERENCES `Processes`(`ID`), "
|
||||||
|
"FOREIGN KEY (`FilenameID`) REFERENCES `Filenames`(`ID`), "
|
||||||
|
"FOREIGN KEY (`ThreadID`) REFERENCES `Threads`(`ID`), "
|
||||||
|
"FOREIGN KEY (`TypeID`) REFERENCES `Types`(`ID`)"
|
||||||
|
");";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (type == "QSQLITE")
|
||||||
|
{
|
||||||
|
return QString("CREATE TABLE IF NOT EXISTS `%0` ("
|
||||||
|
"`ID` INTEGER PRIMARY KEY, "
|
||||||
|
"`Name` TEXT NOT NULL UNIQUE"
|
||||||
|
");").arg(tableName);
|
||||||
|
}
|
||||||
|
else if (type == "QMYSQL")
|
||||||
|
{
|
||||||
|
return QString("CREATE TABLE IF NOT EXISTS `%0` ("
|
||||||
|
"`ID` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, "
|
||||||
|
"`Name` VARCHAR(255) NOT NULL UNIQUE"
|
||||||
|
");").arg(tableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qFatal("unknown database type %s", qPrintable(type));
|
||||||
|
}();
|
||||||
|
|
||||||
|
QSqlQuery query(sql, m_database);
|
||||||
|
if (query.lastError().isValid())
|
||||||
|
{
|
||||||
|
qCritical() << query.lastError().text();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit someSignal(index++);
|
||||||
|
}
|
||||||
|
}
|
26
threads/tablecreatorthread.h
Normal file
26
threads/tablecreatorthread.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
class QSqlDatabase;
|
||||||
|
|
||||||
|
class TableCreatorThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
static const QStringList m_tables;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TableCreatorThread(QSqlDatabase &database, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
static const QStringList &tables();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void someSignal(int index);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSqlDatabase &m_database;
|
||||||
|
};
|
104
widgets/databasewidget.cpp
Normal file
104
widgets/databasewidget.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include "databasewidget.h"
|
||||||
|
#include "ui_databasewidget.h"
|
||||||
|
|
||||||
|
DatabaseWidget::DatabaseWidget(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
m_ui(std::make_unique<Ui::DatabaseWidget>())
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->comboBox->addItem(tr("SQLite"), "QSQLITE");
|
||||||
|
m_ui->comboBox->addItem(tr("MySQL"), "QMYSQL");
|
||||||
|
|
||||||
|
// for debugging
|
||||||
|
setDriver("QMYSQL");
|
||||||
|
// setMysqlHostname("sql7.freemysqlhosting.net");
|
||||||
|
// setMysqlUsername("sql7285815");
|
||||||
|
// setMysqlPassword("BKhysrtqKl");
|
||||||
|
// setMysqlDatabase("sql7285815");
|
||||||
|
|
||||||
|
//setMysqlHostname("brunner.ninja");
|
||||||
|
setMysqlHostname("localhost");
|
||||||
|
setMysqlUsername("logtest");
|
||||||
|
setMysqlPassword("logtest");
|
||||||
|
setMysqlDatabase("logtest");
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseWidget::~DatabaseWidget() = default;
|
||||||
|
|
||||||
|
QString DatabaseWidget::driver() const
|
||||||
|
{
|
||||||
|
return m_ui->comboBox->currentData().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::setDriver(const QString &driver)
|
||||||
|
{
|
||||||
|
m_ui->comboBox->setCurrentIndex(m_ui->comboBox->findData(driver));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseWidget::sqliteFilepath() const
|
||||||
|
{
|
||||||
|
return m_ui->fileSelectionWidget->path();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::setSqliteFilepath(const QString &sqliteFilepath)
|
||||||
|
{
|
||||||
|
m_ui->fileSelectionWidget->setPath(sqliteFilepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseWidget::mysqlHostname() const
|
||||||
|
{
|
||||||
|
return m_ui->lineEditHostname->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::setMysqlHostname(const QString &mysqlHostname)
|
||||||
|
{
|
||||||
|
m_ui->lineEditHostname->setText(mysqlHostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseWidget::mysqlUsername() const
|
||||||
|
{
|
||||||
|
return m_ui->lineEditUsername->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::setMysqlUsername(const QString &mysqlUsername)
|
||||||
|
{
|
||||||
|
m_ui->lineEditUsername->setText(mysqlUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseWidget::mysqlPassword() const
|
||||||
|
{
|
||||||
|
return m_ui->lineEditPassword->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::setMysqlPassword(const QString &mysqlPassword)
|
||||||
|
{
|
||||||
|
m_ui->lineEditPassword->setText(mysqlPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DatabaseWidget::mysqlDatabase() const
|
||||||
|
{
|
||||||
|
return m_ui->lineEditDatabase->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::setMysqlDatabase(const QString &mysqlDatabase)
|
||||||
|
{
|
||||||
|
m_ui->lineEditDatabase->setText(mysqlDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase DatabaseWidget::createConnection(const QString &connectionName)
|
||||||
|
{
|
||||||
|
auto db = QSqlDatabase::addDatabase(driver(), connectionName);
|
||||||
|
|
||||||
|
if (db.driverName() == "QSQLITE")
|
||||||
|
db.setDatabaseName(sqliteFilepath());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db.setHostName(mysqlHostname());
|
||||||
|
db.setUserName(mysqlUsername());
|
||||||
|
db.setPassword(mysqlPassword());
|
||||||
|
db.setDatabaseName(mysqlDatabase());
|
||||||
|
}
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
40
widgets/databasewidget.h
Normal file
40
widgets/databasewidget.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Ui { class DatabaseWidget; }
|
||||||
|
|
||||||
|
class DatabaseWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DatabaseWidget(QWidget *parent = nullptr);
|
||||||
|
~DatabaseWidget() override;
|
||||||
|
|
||||||
|
QString driver() const;
|
||||||
|
void setDriver(const QString &driver);
|
||||||
|
|
||||||
|
QString sqliteFilepath() const;
|
||||||
|
void setSqliteFilepath(const QString &sqliteFilepath);
|
||||||
|
|
||||||
|
QString mysqlHostname() const;
|
||||||
|
void setMysqlHostname(const QString &mysqlHostname);
|
||||||
|
|
||||||
|
QString mysqlUsername() const;
|
||||||
|
void setMysqlUsername(const QString &mysqlUsername);
|
||||||
|
|
||||||
|
QString mysqlPassword() const;
|
||||||
|
void setMysqlPassword(const QString &mysqlPassword);
|
||||||
|
|
||||||
|
QString mysqlDatabase() const;
|
||||||
|
void setMysqlDatabase(const QString &mysqlDatabase);
|
||||||
|
|
||||||
|
QSqlDatabase createConnection(const QString& connectionName = QLatin1String(QSqlDatabase::defaultConnection));
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unique_ptr<Ui::DatabaseWidget> m_ui;
|
||||||
|
};
|
124
widgets/databasewidget.ui
Normal file
124
widgets/databasewidget.ui
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DatabaseWidget</class>
|
||||||
|
<widget class="QWidget" name="DatabaseWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>175</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboBox"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="page">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="FileSelectionWidget" name="fileSelectionWidget" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="page_2">
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEditUsername"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEditHostname"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="labelHostname">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Hostname:</b></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="labelUsername">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Username:</b></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="labelPassword">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Password:</b></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="labelDatabase">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Database:</b></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEditPassword">
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEditDatabase"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>FileSelectionWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>widgets/fileselectionwidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>comboBox</sender>
|
||||||
|
<signal>currentIndexChanged(int)</signal>
|
||||||
|
<receiver>stackedWidget</receiver>
|
||||||
|
<slot>setCurrentIndex(int)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>199</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>199</x>
|
||||||
|
<y>101</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
75
widgets/fileselectionwidget.cpp
Normal file
75
widgets/fileselectionwidget.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include "fileselectionwidget.h"
|
||||||
|
#include "ui_fileselectionwidget.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
FileSelectionWidget::FileSelectionWidget(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
m_ui(std::make_unique<Ui::FileSelectionWidget>()),
|
||||||
|
m_mode(Mode::OpenFile)
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(m_ui->lineEdit, &QLineEdit::textChanged, this, &FileSelectionWidget::pathChanged);
|
||||||
|
connect(m_ui->pushButton, &QAbstractButton::pressed, this, &FileSelectionWidget::selectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSelectionWidget::FileSelectionWidget(const Mode mode, QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
m_ui(std::make_unique<Ui::FileSelectionWidget>()),
|
||||||
|
m_mode(mode)
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(m_ui->lineEdit, &QLineEdit::textChanged, this, &FileSelectionWidget::pathChanged);
|
||||||
|
connect(m_ui->pushButton, &QAbstractButton::pressed, this, &FileSelectionWidget::selectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSelectionWidget::FileSelectionWidget(const Mode mode, const QString &path, QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
m_ui(std::make_unique<Ui::FileSelectionWidget>()),
|
||||||
|
m_mode(mode)
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->lineEdit->setText(path);
|
||||||
|
|
||||||
|
connect(m_ui->lineEdit, &QLineEdit::textChanged, this, &FileSelectionWidget::pathChanged);
|
||||||
|
connect(m_ui->pushButton, &QAbstractButton::pressed, this, &FileSelectionWidget::selectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSelectionWidget::~FileSelectionWidget() = default;
|
||||||
|
|
||||||
|
FileSelectionWidget::Mode FileSelectionWidget::mode() const
|
||||||
|
{
|
||||||
|
return m_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSelectionWidget::setMode(const FileSelectionWidget::Mode mode)
|
||||||
|
{
|
||||||
|
m_mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileSelectionWidget::path() const
|
||||||
|
{
|
||||||
|
return m_ui->lineEdit->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSelectionWidget::setPath(const QString &path)
|
||||||
|
{
|
||||||
|
m_ui->lineEdit->setText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSelectionWidget::selectPath()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
switch (m_mode)
|
||||||
|
{
|
||||||
|
case Mode::OpenFile: path = QFileDialog::getOpenFileName(this); break;
|
||||||
|
case Mode::SaveFile: path = QFileDialog::getSaveFileName(this); break;
|
||||||
|
case Mode::ExistingDirectory: path = QFileDialog::getExistingDirectory(this); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path.isEmpty())
|
||||||
|
m_ui->lineEdit->setText(path);
|
||||||
|
}
|
38
widgets/fileselectionwidget.h
Normal file
38
widgets/fileselectionwidget.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Ui { class FileSelectionWidget; }
|
||||||
|
|
||||||
|
class FileSelectionWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class Mode {
|
||||||
|
OpenFile, SaveFile, ExistingDirectory
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit FileSelectionWidget(QWidget *parent = nullptr);
|
||||||
|
FileSelectionWidget(const Mode mode, QWidget *parent = nullptr);
|
||||||
|
FileSelectionWidget(const Mode mode, const QString &path, QWidget *parent = nullptr);
|
||||||
|
~FileSelectionWidget() override;
|
||||||
|
|
||||||
|
Mode mode() const;
|
||||||
|
void setMode(const Mode mode);
|
||||||
|
|
||||||
|
QString path() const;
|
||||||
|
void setPath(const QString &path);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pathChanged(const QString &path);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void selectPath();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unique_ptr<Ui::FileSelectionWidget> m_ui;
|
||||||
|
Mode m_mode;
|
||||||
|
};
|
31
widgets/fileselectionwidget.ui
Normal file
31
widgets/fileselectionwidget.ui
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>FileSelectionWidget</class>
|
||||||
|
<widget class="QWidget" name="FileSelectionWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>41</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Select...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
54
wizard/conclusionpage.cpp
Normal file
54
wizard/conclusionpage.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "conclusionpage.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
|
||||||
|
ConclusionPage::ConclusionPage(QWidget *parent) :
|
||||||
|
QWizardPage(parent)
|
||||||
|
{
|
||||||
|
setTitle(tr("Conclusion"));
|
||||||
|
setSubTitle(tr("Import successfully finished!"));
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
|
||||||
|
m_label = new QLabel;
|
||||||
|
layout->addWidget(m_label);
|
||||||
|
|
||||||
|
layout->addStretch(1);
|
||||||
|
|
||||||
|
m_checkBox = new QCheckBox(tr("Open new database"));
|
||||||
|
m_checkBox->setChecked(true);
|
||||||
|
layout->addWidget(m_checkBox);
|
||||||
|
registerField("open", m_checkBox);
|
||||||
|
|
||||||
|
layout->addStretch(1);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConclusionPage::initializePage()
|
||||||
|
{
|
||||||
|
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||||
|
Q_ASSERT(importWizard);
|
||||||
|
Q_ASSERT(importWizard->database().isOpen());
|
||||||
|
|
||||||
|
QSqlQuery query("SELECT COUNT(*) FROM `Logs`;", importWizard->database());
|
||||||
|
if (query.lastError().isValid())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(nullptr, tr("Could not get count!"), tr("Could not get count!") % "\n\n" % query.lastError().text());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fetched = query.next();
|
||||||
|
Q_ASSERT(fetched);
|
||||||
|
|
||||||
|
m_label->setText(tr("<b>%0 rows</b> have been imported.").arg(query.value(0).toInt()));
|
||||||
|
}
|
20
wizard/conclusionpage.h
Normal file
20
wizard/conclusionpage.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QCheckBox;
|
||||||
|
|
||||||
|
class ConclusionPage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ConclusionPage(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void initializePage() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *m_label;
|
||||||
|
QCheckBox *m_checkBox;
|
||||||
|
};
|
46
wizard/databasepage.cpp
Normal file
46
wizard/databasepage.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "databasepage.h"
|
||||||
|
#include "ui_databasepage.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QSqlError>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
#include <QSqlQuery>
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "widgets/databasewidget.h"
|
||||||
|
#include "importwizard.h"
|
||||||
|
|
||||||
|
DatabasePage::DatabasePage(QWidget *parent) :
|
||||||
|
QWizardPage(parent),
|
||||||
|
m_ui(std::make_unique<Ui::DatabasePage>())
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
setCommitPage(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabasePage::~DatabasePage() = default;
|
||||||
|
|
||||||
|
int DatabasePage::nextId() const
|
||||||
|
{
|
||||||
|
return int(ImportWizard::Pages::Tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabasePage::validatePage()
|
||||||
|
{
|
||||||
|
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||||
|
Q_ASSERT(importWizard);
|
||||||
|
Q_ASSERT(!importWizard->database().isOpen());
|
||||||
|
|
||||||
|
importWizard->database() = m_ui->databaseWidget->createConnection();
|
||||||
|
|
||||||
|
if (!importWizard->database().open())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Could not open database!"), tr("Could not open database!") % "\n\n" % importWizard->database().lastError().text());
|
||||||
|
importWizard->database() = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
23
wizard/databasepage.h
Normal file
23
wizard/databasepage.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Ui { class DatabasePage; }
|
||||||
|
|
||||||
|
class DatabasePage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DatabasePage(QWidget *parent = nullptr);
|
||||||
|
~DatabasePage() override;
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
bool validatePage() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unique_ptr<Ui::DatabasePage> m_ui;
|
||||||
|
};
|
51
wizard/databasepage.ui
Normal file
51
wizard/databasepage.ui
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>DatabasePage</class>
|
||||||
|
<widget class="QWizardPage" name="DatabasePage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Database</string>
|
||||||
|
</property>
|
||||||
|
<property name="subTitle">
|
||||||
|
<string>Please setup the database connection.</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
|
||||||
|
<item>
|
||||||
|
<widget class="DatabaseWidget" name="databaseWidget" native="true"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>263</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>DatabaseWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>widgets/databasewidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
117
wizard/importprogresspage.cpp
Normal file
117
wizard/importprogresspage.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include "importprogresspage.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
#include "threads/importthread.h"
|
||||||
|
|
||||||
|
ImportProgressPage::ImportProgressPage(QWidget *parent) :
|
||||||
|
QWizardPage(parent)
|
||||||
|
{
|
||||||
|
setTitle(tr("Import Progress"));
|
||||||
|
setSubTitle(tr("TODO..."));
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hboxLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
m_labelIcon = new QLabel;
|
||||||
|
hboxLayout->addWidget(m_labelIcon);
|
||||||
|
|
||||||
|
m_labelStatus = new QLabel;
|
||||||
|
hboxLayout->addWidget(m_labelStatus, 1);
|
||||||
|
|
||||||
|
layout->addLayout(hboxLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_progressBar = new QProgressBar;
|
||||||
|
m_progressBar->setMaximum(100);
|
||||||
|
layout->addWidget(m_progressBar);
|
||||||
|
|
||||||
|
m_logView = new QPlainTextEdit;
|
||||||
|
m_logView->setReadOnly(true);
|
||||||
|
layout->addWidget(m_logView, 1);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportProgressPage::~ImportProgressPage() = default;
|
||||||
|
|
||||||
|
void ImportProgressPage::initializePage()
|
||||||
|
{
|
||||||
|
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||||
|
Q_ASSERT(importWizard);
|
||||||
|
Q_ASSERT(importWizard->database().isOpen());
|
||||||
|
|
||||||
|
const auto result = wizard()->property("result").value<ScanResult>();
|
||||||
|
const auto timeFormat = wizard()->property("timeFormat").toString();
|
||||||
|
|
||||||
|
m_logView->clear();
|
||||||
|
|
||||||
|
m_thread = std::make_unique<ImportThread>(importWizard->database(), result, timeFormat, this);
|
||||||
|
connect(m_thread.get(), &ImportThread::statusUpdate, this, &ImportProgressPage::statusUpdate);
|
||||||
|
connect(m_thread.get(), &ImportThread::progressUpdate, this, &ImportProgressPage::progressUpdate);
|
||||||
|
connect(m_thread.get(), &ImportThread::logMessage, this, &ImportProgressPage::logMessage);
|
||||||
|
connect(m_thread.get(), &QThread::finished, this, &ImportProgressPage::finished);
|
||||||
|
m_thread->start();
|
||||||
|
|
||||||
|
m_labelIcon->setMovie(&m_movieLoading);
|
||||||
|
m_movieLoading.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportProgressPage::cleanupPage()
|
||||||
|
{
|
||||||
|
if (m_thread)
|
||||||
|
{
|
||||||
|
m_thread->requestInterruption();
|
||||||
|
m_thread->wait();
|
||||||
|
m_thread = nullptr;
|
||||||
|
}
|
||||||
|
m_movieLoading.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportProgressPage::nextId() const
|
||||||
|
{
|
||||||
|
return int(ImportWizard::Pages::Conclusion);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImportProgressPage::isComplete() const
|
||||||
|
{
|
||||||
|
return m_thread == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportProgressPage::statusUpdate(const QString &message)
|
||||||
|
{
|
||||||
|
m_labelStatus->setText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportProgressPage::progressUpdate(qint64 finished, qint64 total)
|
||||||
|
{
|
||||||
|
while (total & 0xFFFFFFFF00000000)
|
||||||
|
{
|
||||||
|
finished = finished >> 8;
|
||||||
|
total = total >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_progressBar->setMaximum(total);
|
||||||
|
m_progressBar->setValue(finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportProgressPage::logMessage(const QString &message)
|
||||||
|
{
|
||||||
|
m_logView->appendHtml(QString("%0: %1<br/>").arg(QTime::currentTime().toString(), message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportProgressPage::finished()
|
||||||
|
{
|
||||||
|
cleanupPage();
|
||||||
|
emit completeChanged();
|
||||||
|
|
||||||
|
m_labelIcon->setPixmap(m_pixmapSucceeded);
|
||||||
|
logMessage(tr("Finished."));
|
||||||
|
}
|
46
wizard/importprogresspage.h
Normal file
46
wizard/importprogresspage.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QMovie>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QProgressBar;
|
||||||
|
class QPlainTextEdit;
|
||||||
|
|
||||||
|
class ImportThread;
|
||||||
|
|
||||||
|
class ImportProgressPage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
const QPixmap m_pixmapSucceeded { ":/loganalyzer/succeeded.png" };
|
||||||
|
const QPixmap m_pixmapFailed { ":/loganalyzer/failed.png" };
|
||||||
|
QMovie m_movieLoading { ":/loganalyzer/loading.gif" };
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ImportProgressPage(QWidget *parent = nullptr);
|
||||||
|
~ImportProgressPage() override;
|
||||||
|
|
||||||
|
void initializePage() override;
|
||||||
|
void cleanupPage() override;
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
bool isComplete() const override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void statusUpdate(const QString &message);
|
||||||
|
void progressUpdate(qint64 finished, qint64 total);
|
||||||
|
void logMessage(const QString &message);
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *m_labelIcon;
|
||||||
|
QLabel *m_labelStatus;
|
||||||
|
QProgressBar *m_progressBar;
|
||||||
|
QPlainTextEdit *m_logView;
|
||||||
|
|
||||||
|
std::unique_ptr<ImportThread> m_thread;
|
||||||
|
};
|
117
wizard/importtypepage.cpp
Normal file
117
wizard/importtypepage.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include "importtypepage.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QRadioButton>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
ImportTypePage::ImportTypePage(QWidget *parent) :
|
||||||
|
QWizardPage(parent)
|
||||||
|
{
|
||||||
|
setTitle(tr("Import type"));
|
||||||
|
setSubTitle(tr("Please select which type of log files you would like to import."));
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
|
||||||
|
m_radioLocal = new QRadioButton(tr("Local: Typically found under /tmp/testfw_log/tests"));
|
||||||
|
m_radioLocal->setChecked(true);
|
||||||
|
layout->addWidget(m_radioLocal);
|
||||||
|
|
||||||
|
m_radioRemote = new QRadioButton(tr("Remote: Typically found under /log or /log2"));
|
||||||
|
layout->addWidget(m_radioRemote);
|
||||||
|
|
||||||
|
layout->addStretch(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hboxLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
m_lineEdit = new QLineEdit;
|
||||||
|
hboxLayout->addWidget(m_lineEdit, 1);
|
||||||
|
registerField("folder", m_lineEdit);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto toolButton = new QToolButton;
|
||||||
|
toolButton->setText(tr("Select..."));
|
||||||
|
connect(toolButton, &QAbstractButton::pressed, this, &ImportTypePage::selectFolder);
|
||||||
|
hboxLayout->addWidget(toolButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addLayout(hboxLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addStretch(1);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImportTypePage::nextId() const
|
||||||
|
{
|
||||||
|
if (m_radioLocal->isChecked())
|
||||||
|
return int(ImportWizard::Pages::LocalImport);
|
||||||
|
if (m_radioRemote->isChecked())
|
||||||
|
return int(ImportWizard::Pages::RemoteImportScan);
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImportTypePage::validatePage()
|
||||||
|
{
|
||||||
|
if (m_lineEdit->text().isEmpty())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("No logfolder defined!"), tr("No logfolder defined!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir dir(m_lineEdit->text());
|
||||||
|
if (!dir.exists())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Could not find logfolder!"), tr("Could not find logfolder!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_radioLocal->isChecked())
|
||||||
|
{
|
||||||
|
ScanResult result;
|
||||||
|
auto &host = result["__dummyHost"];
|
||||||
|
|
||||||
|
for (const auto &fileInfo : dir.entryInfoList({ "*.log" }, QDir::Files))
|
||||||
|
{
|
||||||
|
if (fileInfo.baseName().endsWith("_console"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
host[fileInfo.baseName()][QDate()] = {
|
||||||
|
fileInfo.fileName(),
|
||||||
|
fileInfo.absoluteFilePath(),
|
||||||
|
fileInfo.size(),
|
||||||
|
false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host.isEmpty())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Could not find any logs!"), tr("Could not find any logs!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_radioRemote->isChecked())
|
||||||
|
wizard()->setProperty("folder", dir.absolutePath());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImportTypePage::selectFolder()
|
||||||
|
{
|
||||||
|
const auto path = QFileDialog::getExistingDirectory(this, tr("Select log folder"));
|
||||||
|
if (!path.isEmpty())
|
||||||
|
m_lineEdit->setText(path);
|
||||||
|
}
|
27
wizard/importtypepage.h
Normal file
27
wizard/importtypepage.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
class QRadioButton;
|
||||||
|
class QLineEdit;
|
||||||
|
|
||||||
|
class ImportTypePage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ImportTypePage(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
bool validatePage() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void selectFolder();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QRadioButton *m_radioLocal;
|
||||||
|
QRadioButton *m_radioRemote;
|
||||||
|
|
||||||
|
QLineEdit *m_lineEdit;
|
||||||
|
};
|
35
wizard/importwizard.cpp
Normal file
35
wizard/importwizard.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include "importwizard.h"
|
||||||
|
|
||||||
|
#include "intropage.h"
|
||||||
|
#include "databasepage.h"
|
||||||
|
#include "tablespage.h"
|
||||||
|
#include "importtypepage.h"
|
||||||
|
#include "localimportpage.h"
|
||||||
|
#include "remoteimportscanpage.h"
|
||||||
|
#include "remoteimportoverviewpage.h"
|
||||||
|
#include "importprogresspage.h"
|
||||||
|
#include "conclusionpage.h"
|
||||||
|
|
||||||
|
ImportWizard::ImportWizard(QWidget *parent, Qt::WindowFlags flags) :
|
||||||
|
QWizard(parent, flags)
|
||||||
|
{
|
||||||
|
setPage(int(Pages::Introduction), new IntroPage);
|
||||||
|
setPage(int(Pages::Database), new DatabasePage);
|
||||||
|
setPage(int(Pages::Tables), new TablesPage);
|
||||||
|
setPage(int(Pages::ImportType), new ImportTypePage);
|
||||||
|
setPage(int(Pages::LocalImport), new LocalImportPage);
|
||||||
|
setPage(int(Pages::RemoteImportScan), new RemoteImportScanPage);
|
||||||
|
setPage(int(Pages::RemoteImportOverview), new RemoteImportOverviewPage);
|
||||||
|
setPage(int(Pages::ImportProgress), new ImportProgressPage);
|
||||||
|
setPage(int(Pages::Conclusion), new ConclusionPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlDatabase &ImportWizard::database()
|
||||||
|
{
|
||||||
|
return m_database;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QSqlDatabase &ImportWizard::database() const
|
||||||
|
{
|
||||||
|
return m_database;
|
||||||
|
}
|
20
wizard/importwizard.h
Normal file
20
wizard/importwizard.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizard>
|
||||||
|
#include <QSqlDatabase>
|
||||||
|
|
||||||
|
class ImportWizard : public QWizard
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class Pages { Introduction, Database, Tables, ImportType, LocalImport, RemoteImportScan, RemoteImportOverview, ImportProgress, Conclusion };
|
||||||
|
|
||||||
|
ImportWizard(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||||
|
|
||||||
|
QSqlDatabase &database();
|
||||||
|
const QSqlDatabase &database() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSqlDatabase m_database;
|
||||||
|
};
|
18
wizard/intropage.cpp
Normal file
18
wizard/intropage.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "intropage.h"
|
||||||
|
#include "ui_intropage.h"
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
|
||||||
|
IntroPage::IntroPage(QWidget *parent) :
|
||||||
|
QWizardPage(parent),
|
||||||
|
m_ui(std::make_unique<Ui::IntroPage>())
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IntroPage::~IntroPage() = default;
|
||||||
|
|
||||||
|
int IntroPage::nextId() const
|
||||||
|
{
|
||||||
|
return int(ImportWizard::Pages::Database);
|
||||||
|
}
|
21
wizard/intropage.h
Normal file
21
wizard/intropage.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Ui { class IntroPage; }
|
||||||
|
|
||||||
|
class IntroPage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit IntroPage(QWidget *parent = nullptr);
|
||||||
|
~IntroPage() override;
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::unique_ptr<Ui::IntroPage> m_ui;
|
||||||
|
};
|
34
wizard/intropage.ui
Normal file
34
wizard/intropage.ui
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>IntroPage</class>
|
||||||
|
<widget class="QWizardPage" name="IntroPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Introduction</string>
|
||||||
|
</property>
|
||||||
|
<property name="subTitle">
|
||||||
|
<string>TODO...</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>TODO: long introduction...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
161
wizard/localimportpage.cpp
Normal file
161
wizard/localimportpage.cpp
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#include "localimportpage.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QHostInfo>
|
||||||
|
#include <QDateEdit>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
|
||||||
|
LocalImportPage::LocalImportPage(QWidget *parent) :
|
||||||
|
QWizardPage(parent)
|
||||||
|
{
|
||||||
|
setTitle(tr("Local Import"));
|
||||||
|
setSubTitle(tr("TODO..."));
|
||||||
|
setCommitPage(true);
|
||||||
|
|
||||||
|
connect(&m_model, &ChecklistModel::dataChanged, this, &LocalImportPage::updateSummary);
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hboxLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto formLayout = new QFormLayout;
|
||||||
|
|
||||||
|
m_lineEditHost = new QLineEdit(QHostInfo::localHostName());
|
||||||
|
formLayout->addRow(tr("Host:"), m_lineEditHost);
|
||||||
|
|
||||||
|
m_dateEdit = new QDateEdit(QDate::currentDate());
|
||||||
|
formLayout->addRow(tr("Date:"), m_dateEdit);
|
||||||
|
|
||||||
|
m_comboBox = new QComboBox;
|
||||||
|
m_comboBox->addItem(tr("Without milliseconds"), "HH:mm:ss");
|
||||||
|
m_comboBox->addItem(tr("With milliseconds"), "HH:mm:ss.zzz");
|
||||||
|
formLayout->addRow(tr("Timestamp:"), m_comboBox);
|
||||||
|
|
||||||
|
hboxLayout->addLayout(formLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto view = new QListView;
|
||||||
|
view->setModel(&m_model);
|
||||||
|
hboxLayout->addWidget(view, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addLayout(hboxLayout, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_labelSummary = new QLabel;
|
||||||
|
layout->addWidget(m_labelSummary);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LocalImportPage::nextId() const
|
||||||
|
{
|
||||||
|
return int(ImportWizard::Pages::ImportProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalImportPage::initializePage()
|
||||||
|
{
|
||||||
|
m_result = wizard()->property("result").value<ScanResult>();
|
||||||
|
|
||||||
|
Q_ASSERT(m_result.count() == 1);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto processes = m_result.values().first().keys();
|
||||||
|
processes.sort();
|
||||||
|
m_model.setItems(processes);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LocalImportPage::validatePage()
|
||||||
|
{
|
||||||
|
auto result = filterResult(m_result);
|
||||||
|
|
||||||
|
if (scanResultEmpty(result))
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("No files to import!"), tr("No files to import!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(result.count() == 1);
|
||||||
|
|
||||||
|
auto host = result.values().first();
|
||||||
|
for (auto iter = host.begin(); iter != host.end(); iter++)
|
||||||
|
{
|
||||||
|
auto &dates = iter.value();
|
||||||
|
Q_ASSERT(dates.count() == 1);
|
||||||
|
|
||||||
|
const auto logfile = dates.values().first();
|
||||||
|
dates.clear();
|
||||||
|
dates.insert(m_dateEdit->date(), logfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.clear();
|
||||||
|
result.insert(m_lineEditHost->text(), host);
|
||||||
|
|
||||||
|
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||||
|
wizard()->setProperty("timeFormat", m_comboBox->currentData().toString());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalImportPage::updateSummary()
|
||||||
|
{
|
||||||
|
if (m_result.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto result = filterResult(m_result);
|
||||||
|
|
||||||
|
int logFiles { 0 };
|
||||||
|
qint64 totalSize { 0 };
|
||||||
|
|
||||||
|
for (auto hostsIter = result.constBegin(); hostsIter != result.constEnd(); hostsIter++)
|
||||||
|
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||||
|
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||||
|
{
|
||||||
|
logFiles++;
|
||||||
|
totalSize += datesIter.value().filesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sizeStr;
|
||||||
|
for (const QString prefix : { "K", "M", "G", "T" })
|
||||||
|
{
|
||||||
|
if (totalSize > 1024)
|
||||||
|
{
|
||||||
|
totalSize /= 1024;
|
||||||
|
sizeStr = QString::number(totalSize) % prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_labelSummary->setText(tr("Filters match %0 files (%1B)").arg(logFiles).arg(sizeStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanResult LocalImportPage::filterResult(ScanResult result) const
|
||||||
|
{
|
||||||
|
const auto processes = m_model.enabledItems().toSet();
|
||||||
|
|
||||||
|
for (auto hostsIter = result.begin(); hostsIter != result.end(); hostsIter++)
|
||||||
|
{
|
||||||
|
for (auto processesIter = hostsIter.value().begin(); processesIter != hostsIter.value().end(); )
|
||||||
|
{
|
||||||
|
if (processes.contains(processesIter.key()))
|
||||||
|
processesIter++;
|
||||||
|
else
|
||||||
|
processesIter = hostsIter.value().erase(processesIter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
39
wizard/localimportpage.h
Normal file
39
wizard/localimportpage.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include "models/checklistmodel.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class QLineEdit;
|
||||||
|
class QDateEdit;
|
||||||
|
class QComboBox;
|
||||||
|
class QLabel;
|
||||||
|
|
||||||
|
class LocalImportPage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LocalImportPage(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateSummary();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScanResult filterResult(ScanResult result) const;
|
||||||
|
|
||||||
|
ScanResult m_result;
|
||||||
|
|
||||||
|
ChecklistModel m_model;
|
||||||
|
|
||||||
|
QLineEdit *m_lineEditHost;
|
||||||
|
QDateEdit *m_dateEdit;
|
||||||
|
QComboBox *m_comboBox;
|
||||||
|
QLabel *m_labelSummary;
|
||||||
|
};
|
248
wizard/remoteimportoverviewpage.cpp
Normal file
248
wizard/remoteimportoverviewpage.cpp
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
#include "remoteimportoverviewpage.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QDateEdit>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QListView>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QDate>
|
||||||
|
#include <QStringBuilder>
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
RemoteImportOverviewPage::RemoteImportOverviewPage(QWidget *parent) :
|
||||||
|
QWizardPage(parent)
|
||||||
|
{
|
||||||
|
setTitle(tr("Remote Import Overview"));
|
||||||
|
setSubTitle(tr("TODO...."));
|
||||||
|
setCommitPage(true);
|
||||||
|
|
||||||
|
connect(&m_modelHosts, &ChecklistModel::dataChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||||
|
connect(&m_modelProcesses, &ChecklistModel::dataChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto groupBox = new QGroupBox(tr("Date-Range:"));
|
||||||
|
|
||||||
|
auto hboxLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto label = new QLabel(tr("From:"));
|
||||||
|
hboxLayout->addWidget(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dateEditFrom = new QDateEdit;
|
||||||
|
connect(m_dateEditFrom, &QDateTimeEdit::dateChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||||
|
hboxLayout->addWidget(m_dateEditFrom);
|
||||||
|
|
||||||
|
hboxLayout->addStretch(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto label = new QLabel(tr("To:"));
|
||||||
|
hboxLayout->addWidget(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dateEditTo = new QDateEdit;
|
||||||
|
connect(m_dateEditTo, &QDateTimeEdit::dateChanged, this, &RemoteImportOverviewPage::updateSummary);
|
||||||
|
hboxLayout->addWidget(m_dateEditTo);
|
||||||
|
|
||||||
|
hboxLayout->addStretch(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto label = new QLabel(tr("Timestamp:"));
|
||||||
|
hboxLayout->addWidget(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_comboBox = new QComboBox;
|
||||||
|
m_comboBox->addItem(tr("Without milliseconds"), "HH:mm:ss");
|
||||||
|
m_comboBox->addItem(tr("With milliseconds"), "HH:mm:ss.zzz");
|
||||||
|
hboxLayout->addWidget(m_comboBox);
|
||||||
|
|
||||||
|
groupBox->setLayout(hboxLayout);
|
||||||
|
|
||||||
|
layout->addWidget(groupBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hboxLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto vboxLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto label = new QLabel(tr("Hosts:"));
|
||||||
|
vboxLayout->addWidget(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto view = new QListView;
|
||||||
|
view->setModel(&m_modelHosts);
|
||||||
|
vboxLayout->addWidget(view, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
hboxLayout->addLayout(vboxLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto vboxLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto label = new QLabel(tr("Processes:"));
|
||||||
|
vboxLayout->addWidget(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto view = new QListView;
|
||||||
|
view->setModel(&m_modelProcesses);
|
||||||
|
vboxLayout->addWidget(view, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
hboxLayout->addLayout(vboxLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout->addLayout(hboxLayout, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_labelSummary = new QLabel;
|
||||||
|
layout->addWidget(m_labelSummary);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteImportOverviewPage::initializePage()
|
||||||
|
{
|
||||||
|
m_result = wizard()->property("result").value<ScanResult>();
|
||||||
|
|
||||||
|
QDate minDate, maxDate;
|
||||||
|
QSet<QString> processes;
|
||||||
|
|
||||||
|
for (auto hostsIter = m_result.constBegin(); hostsIter != m_result.constEnd(); hostsIter++)
|
||||||
|
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||||
|
{
|
||||||
|
processes.insert(processesIter.key());
|
||||||
|
|
||||||
|
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||||
|
{
|
||||||
|
if (minDate.isNull() || datesIter.key() < minDate)
|
||||||
|
minDate = datesIter.key();
|
||||||
|
if (maxDate.isNull() || datesIter.key() > maxDate)
|
||||||
|
maxDate = datesIter.key();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dateEditFrom->setDate(minDate);
|
||||||
|
m_dateEditTo->setDate(maxDate);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hosts = m_result.keys();
|
||||||
|
hosts.sort();
|
||||||
|
m_modelHosts.setItems(hosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto processesList = processes.toList();
|
||||||
|
processesList.sort();
|
||||||
|
m_modelProcesses.setItems(processesList);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
int RemoteImportOverviewPage::nextId() const
|
||||||
|
{
|
||||||
|
return int(ImportWizard::Pages::ImportProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoteImportOverviewPage::validatePage()
|
||||||
|
{
|
||||||
|
const auto result = filterResult(m_result);
|
||||||
|
|
||||||
|
if (scanResultEmpty(result))
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("No files to import!"), tr("No files to import!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||||
|
wizard()->setProperty("timeFormat", m_comboBox->currentData().toString());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteImportOverviewPage::updateSummary()
|
||||||
|
{
|
||||||
|
if (m_result.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto result = filterResult(m_result);
|
||||||
|
|
||||||
|
int logFiles { 0 };
|
||||||
|
qint64 totalSize { 0 };
|
||||||
|
|
||||||
|
for (auto hostsIter = result.constBegin(); hostsIter != result.constEnd(); hostsIter++)
|
||||||
|
for (auto processesIter = hostsIter.value().constBegin(); processesIter != hostsIter.value().constEnd(); processesIter++)
|
||||||
|
for (auto datesIter = processesIter.value().constBegin(); datesIter != processesIter.value().constEnd(); datesIter++)
|
||||||
|
{
|
||||||
|
logFiles++;
|
||||||
|
totalSize += datesIter.value().filesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString sizeStr;
|
||||||
|
for (const QString prefix : { "K", "M", "G", "T" })
|
||||||
|
{
|
||||||
|
if (totalSize > 1024)
|
||||||
|
{
|
||||||
|
totalSize /= 1024;
|
||||||
|
sizeStr = QString::number(totalSize) % prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_labelSummary->setText(tr("Filters match %0 files (%1B)").arg(logFiles).arg(sizeStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanResult RemoteImportOverviewPage::filterResult(ScanResult result) const
|
||||||
|
{
|
||||||
|
const auto hosts = m_modelHosts.enabledItems().toSet();
|
||||||
|
const auto processes = m_modelProcesses.enabledItems().toSet();
|
||||||
|
|
||||||
|
for (auto hostsIter = result.begin(); hostsIter != result.end(); )
|
||||||
|
{
|
||||||
|
if (hosts.contains(hostsIter.key()))
|
||||||
|
{
|
||||||
|
for (auto processesIter = hostsIter.value().begin(); processesIter != hostsIter.value().end(); )
|
||||||
|
{
|
||||||
|
if (processes.contains(processesIter.key()))
|
||||||
|
{
|
||||||
|
for (auto datesIter = processesIter.value().begin(); datesIter != processesIter.value().end(); )
|
||||||
|
{
|
||||||
|
if (datesIter.key() >= m_dateEditFrom->date() && datesIter.key() <= m_dateEditTo->date())
|
||||||
|
datesIter++;
|
||||||
|
else
|
||||||
|
datesIter = processesIter.value().erase(datesIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
processesIter++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
processesIter = hostsIter.value().erase(processesIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
hostsIter++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hostsIter = result.erase(hostsIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
40
wizard/remoteimportoverviewpage.h
Normal file
40
wizard/remoteimportoverviewpage.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include "models/checklistmodel.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class QDateEdit;
|
||||||
|
class QComboBox;
|
||||||
|
class QLabel;
|
||||||
|
|
||||||
|
class RemoteImportOverviewPage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RemoteImportOverviewPage(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void initializePage() override;
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
bool validatePage() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateSummary();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScanResult filterResult(ScanResult result) const;
|
||||||
|
|
||||||
|
QDateEdit *m_dateEditFrom;
|
||||||
|
QDateEdit *m_dateEditTo;
|
||||||
|
QComboBox *m_comboBox;
|
||||||
|
QLabel *m_labelSummary;
|
||||||
|
|
||||||
|
ChecklistModel m_modelHosts;
|
||||||
|
ChecklistModel m_modelProcesses;
|
||||||
|
|
||||||
|
ScanResult m_result;
|
||||||
|
};
|
111
wizard/remoteimportscanpage.cpp
Normal file
111
wizard/remoteimportscanpage.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#include "remoteimportscanpage.h"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
#include "threads/remotescannerthread.h"
|
||||||
|
|
||||||
|
RemoteImportScanPage::RemoteImportScanPage(QWidget *parent) :
|
||||||
|
QWizardPage(parent)
|
||||||
|
{
|
||||||
|
setTitle(tr("Remote Import Scan"));
|
||||||
|
setSubTitle(tr("TODO..."));
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto hboxLayout = new QHBoxLayout;
|
||||||
|
|
||||||
|
m_labelAnimation = new QLabel;
|
||||||
|
hboxLayout->addWidget(m_labelAnimation);
|
||||||
|
|
||||||
|
m_labelStatus = new QLabel;
|
||||||
|
hboxLayout->addWidget(m_labelStatus, 1);
|
||||||
|
|
||||||
|
layout->addLayout(hboxLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_logView = new QPlainTextEdit;
|
||||||
|
m_logView->setReadOnly(true);
|
||||||
|
|
||||||
|
layout->addWidget(m_logView, 1);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteImportScanPage::~RemoteImportScanPage() = default;
|
||||||
|
|
||||||
|
int RemoteImportScanPage::nextId() const
|
||||||
|
{
|
||||||
|
return int(ImportWizard::Pages::RemoteImportOverview);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteImportScanPage::initializePage()
|
||||||
|
{
|
||||||
|
wizard()->setProperty("result", QVariant::fromValue(ScanResult()));
|
||||||
|
|
||||||
|
m_labelAnimation->setMovie(&m_movieLoading);
|
||||||
|
m_movieLoading.start();
|
||||||
|
|
||||||
|
m_logView->clear();
|
||||||
|
|
||||||
|
m_thread = std::make_unique<RemoteScannerThread>(field("folder").toString(), this);
|
||||||
|
|
||||||
|
connect(m_thread.get(), &RemoteScannerThread::progressUpdate, this, &RemoteImportScanPage::progressUpdate);
|
||||||
|
connect(m_thread.get(), &RemoteScannerThread::logMessage, this, &RemoteImportScanPage::logMessage);
|
||||||
|
connect(m_thread.get(), &RemoteScannerThread::finished, this, &RemoteImportScanPage::finished);
|
||||||
|
|
||||||
|
m_thread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteImportScanPage::cleanupPage()
|
||||||
|
{
|
||||||
|
if (m_thread)
|
||||||
|
{
|
||||||
|
m_thread->requestInterruption();
|
||||||
|
m_thread->wait();
|
||||||
|
m_thread = nullptr;
|
||||||
|
}
|
||||||
|
m_movieLoading.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoteImportScanPage::isComplete() const
|
||||||
|
{
|
||||||
|
return m_thread == nullptr && !scanResultEmpty(wizard()->property("result").value<ScanResult>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteImportScanPage::progressUpdate(int totalFiles, int skippedFiles)
|
||||||
|
{
|
||||||
|
m_labelStatus->setText(tr("%0 files scanned... (%1 files skipped)").arg(totalFiles).arg(skippedFiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteImportScanPage::logMessage(const QString &message)
|
||||||
|
{
|
||||||
|
m_logView->appendHtml(QString("%0: %1<br/>").arg(QTime::currentTime().toString(), message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteImportScanPage::finished(const ScanResult &result)
|
||||||
|
{
|
||||||
|
m_labelAnimation->setMovie(nullptr);
|
||||||
|
|
||||||
|
const auto success = !scanResultEmpty(result);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
logMessage(tr("Finished"));
|
||||||
|
m_labelAnimation->setPixmap(m_pixmapSucceeded);
|
||||||
|
|
||||||
|
wizard()->setProperty("result", QVariant::fromValue(result));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logMessage(tr("Scan failed."));
|
||||||
|
m_labelAnimation->setPixmap(m_pixmapFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupPage();
|
||||||
|
if (success)
|
||||||
|
emit completeChanged();
|
||||||
|
}
|
45
wizard/remoteimportscanpage.h
Normal file
45
wizard/remoteimportscanpage.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QMovie>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QPlainTextEdit;
|
||||||
|
|
||||||
|
class RemoteScannerThread;
|
||||||
|
|
||||||
|
class RemoteImportScanPage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
const QPixmap m_pixmapSucceeded { ":/loganalyzer/succeeded.png" };
|
||||||
|
const QPixmap m_pixmapFailed { ":/loganalyzer/failed.png" };
|
||||||
|
QMovie m_movieLoading { ":/loganalyzer/loading.gif" };
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RemoteImportScanPage(QWidget *parent = nullptr);
|
||||||
|
~RemoteImportScanPage() override;
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
void initializePage() override;
|
||||||
|
void cleanupPage() override;
|
||||||
|
bool isComplete() const override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void progressUpdate(int totalFiles, int skippedFiles);
|
||||||
|
void logMessage(const QString &message);
|
||||||
|
void finished(const ScanResult &result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<RemoteScannerThread> m_thread;
|
||||||
|
|
||||||
|
QLabel *m_labelAnimation;
|
||||||
|
QLabel *m_labelStatus;
|
||||||
|
QPlainTextEdit *m_logView;
|
||||||
|
};
|
89
wizard/tablespage.cpp
Normal file
89
wizard/tablespage.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include "tablespage.h"
|
||||||
|
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include "importwizard.h"
|
||||||
|
#include "threads/tablecreatorthread.h"
|
||||||
|
|
||||||
|
TablesPage::TablesPage(QWidget *parent) :
|
||||||
|
QWizardPage(parent)
|
||||||
|
{
|
||||||
|
setTitle(tr("Tables"));
|
||||||
|
setSubTitle(tr("TODO..."));
|
||||||
|
|
||||||
|
auto layout = new QGridLayout;
|
||||||
|
|
||||||
|
m_statusLabels.resize(TableCreatorThread::tables().size());
|
||||||
|
|
||||||
|
int index { 0 };
|
||||||
|
for (const QString &tableName : TableCreatorThread::tables())
|
||||||
|
{
|
||||||
|
m_statusLabels[index] = new QLabel;
|
||||||
|
layout->addWidget(m_statusLabels[index], index, 0);
|
||||||
|
|
||||||
|
auto label = new QLabel(tr("Create table %0").arg(tableName));
|
||||||
|
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||||
|
layout->addWidget(label, index++, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
TablesPage::~TablesPage() = default;
|
||||||
|
|
||||||
|
int TablesPage::nextId() const
|
||||||
|
{
|
||||||
|
return int(ImportWizard::Pages::ImportType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TablesPage::initializePage()
|
||||||
|
{
|
||||||
|
auto importWizard = qobject_cast<ImportWizard*>(wizard());
|
||||||
|
Q_ASSERT(importWizard);
|
||||||
|
Q_ASSERT(importWizard->database().isOpen());
|
||||||
|
|
||||||
|
for (auto label : m_statusLabels)
|
||||||
|
{
|
||||||
|
label->setMovie(nullptr);
|
||||||
|
label->setPixmap({});
|
||||||
|
}
|
||||||
|
|
||||||
|
m_thread = std::make_unique<TableCreatorThread>(importWizard->database(), this);
|
||||||
|
connect(m_thread.get(), &TableCreatorThread::someSignal, this, &TablesPage::someSlot);
|
||||||
|
|
||||||
|
m_thread->start();
|
||||||
|
m_statusLabels[0]->setMovie(&m_movieLoading);
|
||||||
|
m_movieLoading.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TablesPage::cleanupPage()
|
||||||
|
{
|
||||||
|
if (m_thread)
|
||||||
|
{
|
||||||
|
m_thread->requestInterruption();
|
||||||
|
m_thread->wait();
|
||||||
|
m_thread = nullptr;
|
||||||
|
}
|
||||||
|
m_movieLoading.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TablesPage::isComplete() const
|
||||||
|
{
|
||||||
|
return m_thread == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TablesPage::someSlot(int index)
|
||||||
|
{
|
||||||
|
Q_ASSERT(index < m_statusLabels.size());
|
||||||
|
|
||||||
|
m_statusLabels[index]->setMovie(nullptr);
|
||||||
|
m_statusLabels[index]->setPixmap(m_pixmapSucceeded);
|
||||||
|
if (index < m_statusLabels.size() - 1)
|
||||||
|
m_statusLabels[index+1]->setMovie(&m_movieLoading);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cleanupPage();
|
||||||
|
emit completeChanged();
|
||||||
|
}
|
||||||
|
}
|
40
wizard/tablespage.h
Normal file
40
wizard/tablespage.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QMovie>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QSqlDatabase;
|
||||||
|
|
||||||
|
class TableCreatorThread;
|
||||||
|
|
||||||
|
class TablesPage : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
const QPixmap m_pixmapSucceeded { ":/loganalyzer/succeeded.png" };
|
||||||
|
const QPixmap m_pixmapFailed { ":/loganalyzer/failed.png" };
|
||||||
|
QMovie m_movieLoading { ":/loganalyzer/loading.gif" };
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TablesPage(QWidget *parent = nullptr);
|
||||||
|
~TablesPage() override;
|
||||||
|
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
void initializePage() override;
|
||||||
|
void cleanupPage() override;
|
||||||
|
bool isComplete() const override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void someSlot(int index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<QLabel*> m_statusLabels;
|
||||||
|
|
||||||
|
std::unique_ptr<TableCreatorThread> m_thread;
|
||||||
|
};
|
Reference in New Issue
Block a user