diff --git a/analyzer/CMakeLists.txt b/analyzer/CMakeLists.txt index b1643d5ca8abb8c867854c5b04fad1b2fc4b9670..a1cc62e6026a5132bc2536770f8f98227219077d 100644 --- a/analyzer/CMakeLists.txt +++ b/analyzer/CMakeLists.txt @@ -6,6 +6,7 @@ set(analyzer_srcs datamodel.cpp main.cpp mainwindow.cpp + numericaggregationmodel.cpp product.cpp productmodel.cpp productschemaentry.cpp diff --git a/analyzer/categoryaggregationmodel.cpp b/analyzer/categoryaggregationmodel.cpp index 02df904184605b378c0474225243019bc7fb5f01..31437c7778d192d0f07c07e43db5d921048a1a7c 100644 --- a/analyzer/categoryaggregationmodel.cpp +++ b/analyzer/categoryaggregationmodel.cpp @@ -69,14 +69,15 @@ QVariant CategoryAggregationModel::data(const QModelIndex& index, int role) cons if (!index.isValid() || !m_sourceModel) return {}; + if (role == TimeAggregationModel::MaximumValueRole) + return m_maxValue; + if (index.column() == 0) { const auto srcIdx = m_sourceModel->index(index.row(), 0); return m_sourceModel->data(srcIdx, role); } if (role == Qt::DisplayRole) { return m_data[index.row() * m_categories.size() + index.column() - 1]; - } else if (role == TimeAggregationModel::MaximumValueRole) { - return m_maxValue; } return {}; diff --git a/analyzer/mainwindow.cpp b/analyzer/mainwindow.cpp index 1a846dcdf4a77870e3b858086cc4fef05033829f..197bedca9916d004c2e314e88502c7333808074b 100644 --- a/analyzer/mainwindow.cpp +++ b/analyzer/mainwindow.cpp @@ -24,6 +24,7 @@ #include "chart.h" #include "connectdialog.h" #include "datamodel.h" +#include "numericaggregationmodel.h" #include "productmodel.h" #include "restclient.h" #include "serverinfo.h" @@ -281,6 +282,16 @@ void MainWindow::productSelected() ui->chartType->addItem(schemaEntry.name(), QVariant::fromValue(model)); break; } + case ProductSchemaEntry::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; + } } } } diff --git a/analyzer/numericaggregationmodel.cpp b/analyzer/numericaggregationmodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7debc791239464679125058b131fb4d4e7788af --- /dev/null +++ b/analyzer/numericaggregationmodel.cpp @@ -0,0 +1,145 @@ +/* + 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 "numericaggregationmodel.h" +#include "sample.h" +#include "timeaggregationmodel.h" + +using namespace UserFeedback::Analyzer; + +NumericAggregationModel::NumericAggregationModel(QObject *parent) : + QAbstractTableModel(parent) +{ +} + +NumericAggregationModel::~NumericAggregationModel() = default; + +void NumericAggregationModel::setSourceModel(QAbstractItemModel* model) +{ + Q_ASSERT(model); + m_sourceModel = model; + connect(model, &QAbstractItemModel::modelReset, this, &NumericAggregationModel::recompute); + recompute(); +} + +void NumericAggregationModel::setAggregationValue(const QString& aggrValue) +{ + m_aggrValue = aggrValue; + recompute(); +} + +int NumericAggregationModel::columnCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return 6; +} + +int NumericAggregationModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid() || !m_sourceModel) + return 0; + return m_sourceModel->rowCount(); +} + +QVariant NumericAggregationModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid() || !m_sourceModel) + return {}; + + if (role == TimeAggregationModel::MaximumValueRole) + return m_maxValue; + + if (index.column() == 0) { + const auto srcIdx = m_sourceModel->index(index.row(), 0); + return m_sourceModel->data(srcIdx, role); + } + if (role == Qt::DisplayRole) { + const auto d = m_data.at(index.row()); + switch (index.column()) { + case 1: return d.lowerExtreme; + case 2: return d.lowerQuartile; + case 3: return d.median; + case 4: return d.upperQuartile; + case 5: return d.upperExtreme; + } + } + + return {}; +} + +QVariant NumericAggregationModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_sourceModel) { + switch (section) { + case 0: return m_sourceModel->headerData(section, orientation, role); + case 1: return tr("Lower Extreme"); + case 2: return tr("Lower Quartile"); + case 3: return tr("Median"); + case 4: return tr("Upper Quartile"); + case 5: return tr("Upper Extreme"); + } + } + + return QAbstractTableModel::headerData(section, orientation, role); +} + +void NumericAggregationModel::recompute() +{ + if (!m_sourceModel) + return; + + const auto rowCount = m_sourceModel->rowCount(); + beginResetModel(); + + m_data.clear(); + m_maxValue = 0; + + if (rowCount <= 0 || m_aggrValue.isEmpty()) { + endResetModel(); + return; + } + + m_data.reserve(rowCount); + QVector<double> values; + + for (int row = 0; row < rowCount; ++row) { + const auto samples = m_sourceModel->index(row, 0).data(TimeAggregationModel::SamplesRole).value<QVector<Sample>>(); + + values.clear(); + values.reserve(samples.size()); + + foreach (const auto &sample, samples) { + const auto v = sample.value(m_aggrValue).toDouble(); + values.push_back(v); + } + + std::sort(values.begin(), values.end()); + + Data d; + if (values.size() > 0) { + d.lowerExtreme = values.at(0); + d.lowerQuartile = values.at(values.size() / 4); + d.median = values.at(values.size() / 2); + d.upperQuartile = values.at(values.size() * 3 / 4); + d.upperExtreme = values.last(); + m_maxValue = std::max(m_maxValue, d.upperExtreme); + } + m_data.push_back(d); + } + + endResetModel(); +} diff --git a/analyzer/numericaggregationmodel.h b/analyzer/numericaggregationmodel.h new file mode 100644 index 0000000000000000000000000000000000000000..e0aa756e783c704aec8b3cca8ce013a38e6b4b70 --- /dev/null +++ b/analyzer/numericaggregationmodel.h @@ -0,0 +1,61 @@ +/* + 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_NUMERICAGGREGATIONMODEL_H +#define USERFEEDBACK_ANALYZER_NUMERICAGGREGATIONMODEL_H + +#include <QAbstractTableModel> + +namespace UserFeedback { +namespace Analyzer { + +class NumericAggregationModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit NumericAggregationModel(QObject *parent = nullptr); + ~NumericAggregationModel(); + + void setSourceModel(QAbstractItemModel *model); + void setAggregationValue(const QString &aggrValue); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + +private: + void recompute(); + + QAbstractItemModel *m_sourceModel = nullptr; + QString m_aggrValue; + struct Data { + double lowerExtreme = 0.0; + double lowerQuartile = 0.0; + double median = 0.0; + double upperQuartile = 0.0; + double upperExtreme = 0.0; + }; + QVector<Data> m_data; + + double m_maxValue = 0.0; +}; + +} +} + +#endif // USERFEEDBACK_ANALYZER_NUMERICAGGREGATIONMODEL_H