Commit b8d00fdb authored by Volker Krause's avatar Volker Krause
Browse files

Add data export action

parent cd05ed73
......@@ -21,12 +21,17 @@
#include "aggregateddatamodel.h"
#include "categoryaggregationmodel.h"
#include "chart.h"
#include <model/datamodel.h>
#include "numericaggregationmodel.h"
#include "ratiosetaggregationmodel.h"
#include "timeaggregationmodel.h"
#include <model/datamodel.h>
#include <core/sample.h>
#include <QFile>
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include <QSettings>
using namespace UserFeedback::Analyzer;
......@@ -66,7 +71,11 @@ AnalyticsView::AnalyticsView(QWidget* parent) :
timeAggrMenu->addAction(ui->actionAggregateWeek);
timeAggrMenu->addAction(ui->actionAggregateMonth);
timeAggrMenu->addAction(ui->actionAggregateYear);
addAction(timeAggrMenu->menuAction());
connect(ui->actionExportData, &QAction::triggered, this, &AnalyticsView::exportData);
connect(ui->actionImportData, &QAction::triggered, this, &AnalyticsView::importData);
addActions({timeAggrMenu->menuAction(), ui->actionExportData, ui->actionImportData});
QSettings settings;
settings.beginGroup(QStringLiteral("Analytics"));
......@@ -147,3 +156,25 @@ void AnalyticsView::setProduct(const Product& product)
}
}
}
void AnalyticsView::exportData()
{
const auto fileName = QFileDialog::getSaveFileName(this, tr("Export Data"));
if (fileName.isEmpty())
return;
QFile f(fileName);
if (!f.open(QFile::WriteOnly)) {
QMessageBox::critical(this, tr("Export Failed"), tr("Could not open file: %1").arg(f.errorString()));
return;
}
const auto samples = m_dataModel->index(0, 0).data(DataModel::AllSamplesRole).value<QVector<Sample>>();
f.write(Sample::toJson(samples, m_dataModel->product()));
logMessage(tr("Sample data of %1 exported to %2.").arg(m_dataModel->product().name(), f.fileName()));
}
void AnalyticsView::importData()
{
// TODO
}
......@@ -55,6 +55,9 @@ signals:
void logMessage(const QString &msg);
private:
void exportData();
void importData();
std::unique_ptr<Ui::AnalyticsView> ui;
DataModel *m_dataModel;
......
......@@ -126,6 +126,28 @@
<string>Aggregate data by day.</string>
</property>
</action>
<action name="actionExportData">
<property name="icon">
<iconset theme="document-export"/>
</property>
<property name="text">
<string>&amp;Export Data...</string>
</property>
<property name="toolTip">
<string>Export all recorded data.</string>
</property>
</action>
<action name="actionImportData">
<property name="icon">
<iconset theme="document-import"/>
</property>
<property name="text">
<string>&amp;Import Data...</string>
</property>
<property name="toolTip">
<string>Import sample data.</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
......
......@@ -121,3 +121,71 @@ QVector<Sample> Sample::fromJson(const QByteArray &json, const Product &product)
}
return samples;
}
QByteArray Sample::toJson(const QVector<Sample> &samples, const Product &product)
{
QJsonArray array;
for (const auto &s : samples) {
QJsonObject obj;
obj[QLatin1String("timestamp")] = s.timestamp().toString(Qt::ISODate);
foreach (const auto &entry, product.schema()) {
switch (entry.dataType()) {
case SchemaEntry::Scalar:
{
QJsonObject subObj;
const auto entryData = obj.value(entry.name()).toObject();
foreach (const auto &elem, entry.elements()) {
const auto it = s.d->data.constFind(entry.name() + QLatin1Char('.') + elem.name());
if (it == s.d->data.constEnd())
continue;
subObj[elem.name()] = QJsonValue::fromVariant(it.value());
}
if (!subObj.isEmpty())
obj[entry.name()] = subObj;
break;
}
case SchemaEntry::List:
{
QJsonArray a;
const auto it = s.d->data.constFind(entry.name());
if (it == s.d->data.constEnd())
continue;
const auto l = it.value().toList();
for (const auto &v : l) {
QJsonObject subObj;
const auto m = v.toMap();
for (auto it = m.begin(); it != m.end(); ++it) {
subObj[it.key()] = QJsonValue::fromVariant(it.value());
}
a.push_back(subObj);
}
obj[entry.name()] = a;
break;
}
case SchemaEntry::Map:
{
QJsonObject map;
const auto it = s.d->data.constFind(entry.name());
if (it == s.d->data.constEnd())
continue;
const auto m = it.value().toMap();
for (auto it = m.begin(); it != m.end(); ++it) {
QJsonObject subObj;
const auto m = it.value().toMap();
for (auto it = m.begin(); it != m.end(); ++it) {
subObj[it.key()] = QJsonValue::fromVariant(it.value());
}
map[it.key()] = subObj;
}
obj[entry.name()] = map;
break;
}
}
}
array.push_back(obj);
}
QJsonDocument doc;
doc.setArray(array);
return doc.toJson();
}
......@@ -44,6 +44,7 @@ public:
QVariant value(const QString &name) const;
static QVector<Sample> fromJson(const QByteArray &json, const Product &product);
static QByteArray toJson(const QVector<Sample> &samples, const Product &product);
private:
QSharedDataPointer<SampleData> d;
......
......@@ -73,6 +73,11 @@ void DataModel::setRESTClient(RESTClient* client)
reload();
}
Product DataModel::product() const
{
return m_product;
}
void DataModel::setProduct(const Product& product)
{
beginResetModel();
......
......@@ -44,7 +44,10 @@ public:
~DataModel();
void setRESTClient(RESTClient *client);
Product product() const;
void setProduct(const Product &product);
void setSamples(const QVector<Sample> &samples);
void reload();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment