File indexing completed on 2024-12-15 03:45:01

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "numericaggregationmodel.h"
0008 #include <model/timeaggregationmodel.h>
0009 #include <core/sample.h>
0010 
0011 using namespace KUserFeedback::Console;
0012 
0013 NumericAggregationModel::NumericAggregationModel(QObject *parent) :
0014     QAbstractTableModel(parent)
0015 {
0016 }
0017 
0018 NumericAggregationModel::~NumericAggregationModel() = default;
0019 
0020 void NumericAggregationModel::setSourceModel(QAbstractItemModel* model)
0021 {
0022     Q_ASSERT(model);
0023     m_sourceModel = model;
0024     connect(model, &QAbstractItemModel::modelReset, this, &NumericAggregationModel::recompute);
0025     recompute();
0026 }
0027 
0028 void NumericAggregationModel::setAggregation(const AggregationElement& aggr)
0029 {
0030     m_aggr = aggr;
0031     recompute();
0032 }
0033 
0034 int NumericAggregationModel::columnCount(const QModelIndex& parent) const
0035 {
0036     Q_UNUSED(parent);
0037     return 6;
0038 }
0039 
0040 int NumericAggregationModel::rowCount(const QModelIndex& parent) const
0041 {
0042     if (parent.isValid() || !m_sourceModel)
0043         return 0;
0044     return m_sourceModel->rowCount();
0045 }
0046 
0047 QVariant NumericAggregationModel::data(const QModelIndex& index, int role) const
0048 {
0049     if (!index.isValid() || !m_sourceModel || index.row()>=m_data.count())
0050         return {};
0051 
0052     if (role == TimeAggregationModel::MaximumValueRole)
0053         return m_maxValue;
0054 
0055     if (index.column() == 0) {
0056         const auto srcIdx = m_sourceModel->index(index.row(), 0);
0057         return m_sourceModel->data(srcIdx, role);
0058     }
0059     if (role == Qt::DisplayRole || role == TimeAggregationModel::DataDisplayRole) {
0060         const auto d = m_data.at(index.row());
0061         switch (index.column()) {
0062             case 1: return d.lowerExtreme;
0063             case 2: return d.lowerQuartile;
0064             case 3: return d.median;
0065             case 4: return d.upperQuartile;
0066             case 5: return d.upperExtreme;
0067         }
0068     }
0069 
0070     return {};
0071 }
0072 
0073 QVariant NumericAggregationModel::headerData(int section, Qt::Orientation orientation, int role) const
0074 {
0075     if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_sourceModel) {
0076         switch (section) {
0077             case 0: return m_sourceModel->headerData(section, orientation, role);
0078             case 1: return tr("Lower Extreme");
0079             case 2: return tr("Lower Quartile");
0080             case 3: return tr("Median");
0081             case 4: return tr("Upper Quartile");
0082             case 5: return tr("Upper Extreme");
0083         }
0084     }
0085 
0086     return QAbstractTableModel::headerData(section, orientation, role);
0087 }
0088 
0089 void NumericAggregationModel::recompute()
0090 {
0091     if (!m_sourceModel)
0092         return;
0093 
0094     const auto rowCount = m_sourceModel->rowCount();
0095     beginResetModel();
0096 
0097     m_data.clear();
0098     m_maxValue = 0;
0099 
0100     if (rowCount <= 0 || !m_aggr.isValid()) {
0101         endResetModel();
0102         return;
0103     }
0104 
0105     m_data.reserve(rowCount);
0106     QVector<double> values;
0107 
0108     for (int row = 0; row < rowCount; ++row) {
0109         const auto samples = m_sourceModel->index(row, 0).data(TimeAggregationModel::SamplesRole).value<QVector<Sample>>();
0110 
0111         values.clear();
0112         values.reserve(samples.size());
0113 
0114         foreach (const auto &sample, samples)
0115             values += sampleValues(sample);
0116 
0117         std::sort(values.begin(), values.end());
0118 
0119         Data d;
0120         if (values.size() > 0) {
0121             d.lowerExtreme = values.at(0);
0122             d.lowerQuartile = values.at(values.size() / 4);
0123             d.median = values.at(values.size() / 2);
0124             d.upperQuartile = values.at(values.size() * 3 / 4);
0125             d.upperExtreme = values.last();
0126             m_maxValue = std::max(m_maxValue, d.upperExtreme);
0127         }
0128         m_data.push_back(d);
0129     }
0130 
0131     endResetModel();
0132 }
0133 
0134 QVector<double> NumericAggregationModel::sampleValues(const Sample &s) const
0135 {
0136     switch (m_aggr.schemaEntry().dataType()) {
0137         case SchemaEntry::Scalar:
0138         {
0139             if (m_aggr.type() != AggregationElement::Value)
0140                 return {};
0141             return {s.value(m_aggr.schemaEntry().name() + QLatin1String(".") + m_aggr.schemaEntryElement().name()).toDouble()};
0142         }
0143         case SchemaEntry::List:
0144         {
0145             const auto l = s.value(m_aggr.schemaEntry().name()).value<QVariantList>();
0146             if (m_aggr.type() == AggregationElement::Size)
0147                 return {(double)l.size()};
0148             QVector<double> r;
0149             r.reserve(l.size());
0150             for (const auto &entry : l)
0151                 r.push_back(entry.toMap().value(m_aggr.schemaEntryElement().name()).toDouble());
0152             return r;
0153         }
0154         case SchemaEntry::Map:
0155         {
0156             const auto m = s.value(m_aggr.schemaEntry().name()).toMap();
0157             if (m_aggr.type() == AggregationElement::Size)
0158                 return {(double)m.size()};
0159             QVector<double> r;
0160             r.reserve(m.size());
0161             for (auto it = m.begin(); it != m.end(); ++it)
0162                 r.push_back(it.value().toMap().value(m_aggr.schemaEntryElement().name()).toDouble());
0163             return r;
0164         }
0165     }
0166 
0167     return {};
0168 }
0169 
0170 #include "moc_numericaggregationmodel.cpp"