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

Break out analytics view from main window too

Also, add three different view modes, for analytics, surveys and schema
editing.
parent d69a4ba9
......@@ -31,6 +31,8 @@ set(analyzer_srcs
sample.cpp
timeaggregationmodel.cpp
analytics/analyticsview.cpp
schemaeditor/schemaeditwidget.cpp
schemaeditor/schemaentryitemeditorfactory.cpp
schemaeditor/schemaentrytypecombobox.cpp
......
/*
Copyright (C) 2016 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "analyticsview.h"
#include "ui_analyticsview.h"
#include "aggregateddatamodel.h"
#include "categoryaggregationmodel.h"
#include "chart.h"
#include "datamodel.h"
#include "numericaggregationmodel.h"
#include "ratiosetaggregationmodel.h"
#include "timeaggregationmodel.h"
#include <QMenu>
#include <QSettings>
using namespace UserFeedback::Analyzer;
AnalyticsView::AnalyticsView(QWidget* parent) :
QWidget(parent),
ui(new Ui::AnalyticsView),
m_dataModel(new DataModel(this)),
m_timeAggregationModel(new TimeAggregationModel(this)),
m_aggregatedDataModel(new AggregatedDataModel(this)),
m_chart(new Chart(this))
{
ui->setupUi(this);
ui->dataView->setModel(m_dataModel);
ui->aggregatedDataView->setModel(m_aggregatedDataModel);
m_timeAggregationModel->setSourceModel(m_dataModel);
m_chart->setModel(m_timeAggregationModel);
ui->actionAggregateYear->setData(TimeAggregationModel::AggregateYear);
ui->actionAggregateMonth->setData(TimeAggregationModel::AggregateMonth);
ui->actionAggregateWeek->setData(TimeAggregationModel::AggregateWeek);
ui->actionAggregateDay->setData(TimeAggregationModel::AggregateDay);
auto aggrGroup = new QActionGroup(this);
aggrGroup->addAction(ui->actionAggregateYear);
aggrGroup->addAction(ui->actionAggregateMonth);
aggrGroup->addAction(ui->actionAggregateWeek);
aggrGroup->addAction(ui->actionAggregateDay);
aggrGroup->setExclusive(true);
connect(aggrGroup, &QActionGroup::triggered, this, [this, aggrGroup]() {
m_timeAggregationModel->setAggregationMode(static_cast<TimeAggregationModel::AggregationMode>(aggrGroup->checkedAction()->data().toInt()));
});
auto timeAggrMenu = new QMenu(tr("&Time interval"), this);
timeAggrMenu->addAction(ui->actionAggregateDay);
timeAggrMenu->addAction(ui->actionAggregateWeek);
timeAggrMenu->addAction(ui->actionAggregateMonth);
timeAggrMenu->addAction(ui->actionAggregateYear);
addAction(timeAggrMenu->menuAction());
QSettings settings;
settings.beginGroup(QStringLiteral("Analytics"));
const auto aggrSetting = settings.value(QStringLiteral("TimeAggregationMode"), TimeAggregationModel::AggregateMonth).toInt();
foreach (auto act, aggrGroup->actions())
act->setChecked(act->data().toInt() == aggrSetting);
m_timeAggregationModel->setAggregationMode(static_cast<TimeAggregationModel::AggregationMode>(aggrSetting));
settings.endGroup();
ui->chartView->setChart(m_chart->chart());
connect(ui->chartType, &QComboBox::currentTextChanged, this, [this]() {
const auto model = ui->chartType->currentData().value<QAbstractItemModel*>();
m_chart->setModel(model);
});
}
AnalyticsView::~AnalyticsView()
{
QSettings settings;
settings.beginGroup(QStringLiteral("Analytics"));
settings.setValue(QStringLiteral("TimeAggregationMode"), m_timeAggregationModel->aggregationMode());
settings.endGroup();
}
void AnalyticsView::setRESTClient(RESTClient* client)
{
m_dataModel->setRESTClient(client);
}
void AnalyticsView::setProduct(const Product& product)
{
m_dataModel->setProduct(product);
m_chart->setModel(nullptr);
ui->chartType->clear();
m_aggregatedDataModel->clear();
qDeleteAll(m_aggregationModels);
m_aggregationModels.clear();
m_aggregatedDataModel->addSourceModel(m_timeAggregationModel);
ui->chartType->addItem(tr("Samples"), QVariant::fromValue(m_timeAggregationModel));
foreach (const auto &schemaEntry, product.schema()) {
switch (schemaEntry.type()) {
case SchemaEntry::InvalidType:
case SchemaEntry::StringListType:
break;
case SchemaEntry::StringType:
{
auto model = new CategoryAggregationModel(this);
model->setSourceModel(m_timeAggregationModel);
model->setAggregationValue(schemaEntry.name());
m_aggregationModels.push_back(model);
m_aggregatedDataModel->addSourceModel(model, schemaEntry.name());
ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
break;
}
case SchemaEntry::IntegerType:
{
auto model = new NumericAggregationModel(this);
model->setSourceModel(m_timeAggregationModel);
model->setAggregationValue(schemaEntry.name());
m_aggregationModels.push_back(model);
m_aggregatedDataModel->addSourceModel(model, schemaEntry.name());
ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
break;
}
case SchemaEntry::RatioSetType:
{
auto model = new RatioSetAggregationModel(this);
model->setSourceModel(m_timeAggregationModel);
model->setAggregationValue(schemaEntry.name());
m_aggregationModels.push_back(model);
m_aggregatedDataModel->addSourceModel(model, schemaEntry.name());
ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
break;
}
}
}
}
/*
Copyright (C) 2016 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef USERFEEDBACK_ANALYZER_ANALYTICSVIEW_H
#define USERFEEDBACK_ANALYZER_ANALYTICSVIEW_H
#include <core/product.h>
#include <QVector>
#include <QWidget>
#include <memory>
class QAbstractItemModel;
namespace UserFeedback {
namespace Analyzer {
class AggregatedDataModel;
class Chart;
class DataModel;
class RESTClient;
class TimeAggregationModel;
namespace Ui
{
class AnalyticsView;
}
class AnalyticsView : public QWidget
{
Q_OBJECT
public:
explicit AnalyticsView(QWidget *parent = nullptr);
~AnalyticsView();
void setProduct(const Product &product);
void setRESTClient(RESTClient *client);
signals:
void logMessage(const QString &msg);
private:
std::unique_ptr<Ui::AnalyticsView> ui;
DataModel *m_dataModel;
TimeAggregationModel *m_timeAggregationModel;
QVector<QAbstractItemModel*> m_aggregationModels;
AggregatedDataModel *m_aggregatedDataModel;
Chart *m_chart;
};
}
}
#endif // USERFEEDBACK_ANALYZER_ANALYTICSVIEW_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UserFeedback::Analyzer::AnalyticsView</class>
<widget class="QWidget" name="UserFeedback::Analyzer::AnalyticsView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>587</width>
<height>353</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="chartTab">
<attribute name="title">
<string>Charts</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Chart type:</string>
</property>
<property name="buddy">
<cstring>chartType</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="chartType"/>
</item>
</layout>
</item>
<item>
<widget class="QtCharts::QChartView" name="chartView">
<property name="renderHints">
<set>QPainter::Antialiasing|QPainter::TextAntialiasing</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="aggregationTab">
<attribute name="title">
<string>Aggregated Data</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QTableView" name="aggregatedDataView"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="dataTab">
<attribute name="title">
<string>Raw Data</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTableView" name="dataView"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
<action name="actionAggregateYear">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Year</string>
</property>
<property name="toolTip">
<string>Aggregate data by year.</string>
</property>
</action>
<action name="actionAggregateMonth">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Month</string>
</property>
<property name="toolTip">
<string>Aggregate data by month.</string>
</property>
</action>
<action name="actionAggregateWeek">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Week</string>
</property>
<property name="toolTip">
<string>Aggregate data by week.</string>
</property>
</action>
<action name="actionAggregateDay">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Day</string>
</property>
<property name="toolTip">
<string>Aggregate data by day.</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>QtCharts::QChartView</class>
<extends>QGraphicsView</extends>
<header location="global">QChartView</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
......@@ -19,14 +19,7 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "aggregateddatamodel.h"
#include "categoryaggregationmodel.h"
#include "chart.h"
#include "connectdialog.h"
#include "datamodel.h"
#include "numericaggregationmodel.h"
#include "ratiosetaggregationmodel.h"
#include "timeaggregationmodel.h"
#include <model/productmodel.h>
......@@ -57,27 +50,35 @@ MainWindow::MainWindow() :
ui(new Ui::MainWindow),
m_restClient(new RESTClient(this)),
m_productModel(new ProductModel(this)),
m_dataModel(new DataModel(this)),
m_timeAggregationModel(new TimeAggregationModel(this)),
m_aggregatedDataModel(new AggregatedDataModel(this)),
m_chart(new Chart(this)),
m_feedbackProvider(new UserFeedback::Provider(this))
{
ui->setupUi(this);
setWindowIcon(QIcon::fromTheme(QStringLiteral("search")));
addView(ui->surveyEditor, ui->menuSurvery);
addView(ui->schemaEdit, ui->menuSchema);
addView(ui->analyticsView, ui->menuAnalytics);
ui->productListView->setModel(m_productModel);
ui->dataView->setModel(m_dataModel);
ui->aggregatedDataView->setModel(m_aggregatedDataModel);
connect(m_restClient, &RESTClient::errorMessage, this, &MainWindow::logError);
m_productModel->setRESTClient(m_restClient);
m_dataModel->setRESTClient(m_restClient);
ui->surveyEditor->setRESTClient(m_restClient);
m_timeAggregationModel->setSourceModel(m_dataModel);
m_chart->setModel(m_timeAggregationModel);
ui->analyticsView->setRESTClient(m_restClient);
ui->actionViewAnalytics->setData(QVariant::fromValue(ui->analyticsView));
ui->actionViewSurveys->setData(QVariant::fromValue(ui->surveyEditor));
ui->actionViewSchema->setData(QVariant::fromValue(ui->schemaEdit));
auto viewGroup = new QActionGroup(this);
viewGroup->setExclusive(true);
viewGroup->addAction(ui->actionViewAnalytics);
viewGroup->addAction(ui->actionViewSurveys);
viewGroup->addAction(ui->actionViewSchema);
connect(viewGroup, &QActionGroup::triggered, this, [this](QAction *action) {
auto view = action->data().value<QWidget*>();
ui->viewStack->setCurrentWidget(view);
});
ui->actionViewAnalytics->setChecked(true); // TODO save/restore from settings
ui->actionConnectToServer->setIcon(QIcon::fromTheme(QStringLiteral("network-connect")));
connect(ui->actionConnectToServer, &QAction::triggered, this, [this]() {
......@@ -98,34 +99,6 @@ MainWindow::MainWindow() :
ui->actionDeleteProduct->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
connect(ui->actionDeleteProduct, &QAction::triggered, this, &MainWindow::deleteProduct);
ui->actionAggregateYear->setData(TimeAggregationModel::AggregateYear);
ui->actionAggregateMonth->setData(TimeAggregationModel::AggregateMonth);
ui->actionAggregateWeek->setData(TimeAggregationModel::AggregateWeek);
ui->actionAggregateDay->setData(TimeAggregationModel::AggregateDay);
auto aggrGroup = new QActionGroup(this);
aggrGroup->addAction(ui->actionAggregateYear);
aggrGroup->addAction(ui->actionAggregateMonth);
aggrGroup->addAction(ui->actionAggregateWeek);
aggrGroup->addAction(ui->actionAggregateDay);
aggrGroup->setExclusive(true);
connect(aggrGroup, &QActionGroup::triggered, this, [this, aggrGroup]() {
m_timeAggregationModel->setAggregationMode(static_cast<TimeAggregationModel::AggregationMode>(aggrGroup->checkedAction()->data().toInt()));
});
QSettings settings;
settings.beginGroup(QStringLiteral("Analytics"));
const auto aggrSetting = settings.value(QStringLiteral("TimeAggregationMode"), TimeAggregationModel::AggregateMonth).toInt();
foreach (auto act, aggrGroup->actions())
act->setChecked(act->data().toInt() == aggrSetting);
m_timeAggregationModel->setAggregationMode(static_cast<TimeAggregationModel::AggregationMode>(aggrSetting));
settings.endGroup();
ui->chartView->setChart(m_chart->chart());
connect(ui->chartType, &QComboBox::currentTextChanged, this, [this]() {
const auto model = ui->chartType->currentData().value<QAbstractItemModel*>();
m_chart->setModel(model);
});
connect(ui->schemaEdit, &SchemaEditWidget::productChanged, this, [this](const Product &p) {
auto reply = RESTApi::updateProduct(m_restClient, p);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
......@@ -154,6 +127,7 @@ MainWindow::MainWindow() :
connect(ui->productListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::productSelected);
QSettings settings;
settings.beginGroup(QStringLiteral("MainWindow"));
restoreGeometry(settings.value(QStringLiteral("Geometry")).toByteArray());
restoreState(settings.value(QStringLiteral("State")).toByteArray());
......@@ -163,22 +137,16 @@ MainWindow::MainWindow() :
m_feedbackProvider->setFeedbackServer(QUrl(QStringLiteral("https://feedback.volkerkrause.eu")));
m_feedbackProvider->setSubmissionInterval(1);
m_feedbackProvider->setApplicationUsageTimeUntilEncouragement(10);
auto viewModeSource = new UserFeedback::PropertyRatioSource(ui->tabWidget, "currentIndex", QStringLiteral("viewRatio"));
viewModeSource->addValueMapping(0, QStringLiteral("chart"));
viewModeSource->addValueMapping(1, QStringLiteral("aggregated_data"));
viewModeSource->addValueMapping(2, QStringLiteral("raw_data"));
viewModeSource->addValueMapping(3, QStringLiteral("surveys"));
viewModeSource->addValueMapping(4, QStringLiteral("schema"));
auto viewModeSource = new UserFeedback::PropertyRatioSource(ui->viewStack, "currentIndex", QStringLiteral("viewRatio"));
viewModeSource->addValueMapping(0, QStringLiteral("analytics"));
viewModeSource->addValueMapping(1, QStringLiteral("surveyEditor"));
viewModeSource->addValueMapping(2, QStringLiteral("schemaEditor"));
m_feedbackProvider->addDataSource(viewModeSource, Provider::AllStatistics);
}
MainWindow::~MainWindow()
{
QSettings settings;
settings.beginGroup(QStringLiteral("Analytics"));
settings.setValue(QStringLiteral("TimeAggregationMode"), m_timeAggregationModel->aggregationMode());
settings.endGroup();
settings.beginGroup(QStringLiteral("MainWindow"));
settings.setValue(QStringLiteral("State"), saveState());
settings.setValue(QStringLiteral("Geometry"), saveGeometry());
......@@ -261,56 +229,9 @@ void MainWindow::productSelected()
const auto product = selectedProduct();
if (!product.isValid())
return;
m_dataModel->setProduct(product);
ui->surveyEditor->setProduct(product);
ui->schemaEdit->setProduct(product);
m_chart->setModel(nullptr);
ui->chartType->clear();
m_aggregatedDataModel->clear();
qDeleteAll(m_aggregationModels);
m_aggregationModels.clear();
m_aggregatedDataModel->addSourceModel(m_timeAggregationModel);
ui->chartType->addItem(tr("Samples"), QVariant::fromValue(m_timeAggregationModel));
foreach (const auto &schemaEntry, product.schema()) {
switch (schemaEntry.type()) {
case SchemaEntry::InvalidType:
case SchemaEntry::StringListType:
break;
case SchemaEntry::StringType:
{
auto model = new CategoryAggregationModel(this);
model->setSourceModel(m_timeAggregationModel);
model->setAggregationValue(schemaEntry.name());
m_aggregationModels.push_back(model);
m_aggregatedDataModel->addSourceModel(model, schemaEntry.name());
ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
break;
}
case SchemaEntry::IntegerType:
{
auto model = new NumericAggregationModel(this);
model->setSourceModel(m_timeAggregationModel);
model->setAggregationValue(schemaEntry.name());
m_aggregationModels.push_back(model);
m_aggregatedDataModel->addSourceModel(model, schemaEntry.name());
ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
break;
}
case SchemaEntry::RatioSetType:
{
auto model = new RatioSetAggregationModel(this);
model->setSourceModel(m_timeAggregationModel);
model->setAggregationValue(schemaEntry.name());
m_aggregationModels.push_back(model);
m_aggregatedDataModel->addSourceModel(model, schemaEntry.name());
ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model));
break;
}
}
}
ui->analyticsView->setProduct(product);
}
void MainWindow::logMessage(const QString& msg)
......
......@@ -37,14 +37,10 @@ namespace Ui
class MainWindow;
}
class AggregatedDataModel;
class Chart;
class DataModel;
class Product;
class ProductModel;
class ServerInfo;
class RESTClient;
class TimeAggregationModel;
class MainWindow : public QMainWindow
{
......@@ -70,11 +66,6 @@ private:
std::unique_ptr<Ui::MainWindow> ui;
RESTClient *m_restClient;
ProductModel *m_productModel;
DataModel *m_dataModel;
TimeAggregationModel *m_timeAggregationModel;
QVector<QAbstractItemModel*> m_aggregationModels;
AggregatedDataModel *m_aggregatedDataModel;
Chart *m_chart;
UserFeedback::Provider *m_feedbackProvider;
};
......
......@@ -28,81 +28,10 @@
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="chartTab">
<attribute name="title">
<string>Charts</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Chart type:</string>
</property>
<property name="buddy">
<cstring>chartType</cstring>