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

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "timeaggregationmodel.h"
0008 #include <core/sample.h>
0009 #include <model/datamodel.h>
0010 
0011 #include <QDebug>
0012 
0013 #include <algorithm>
0014 #include <numeric>
0015 
0016 using namespace KUserFeedback::Console;
0017 
0018 TimeAggregationModel::TimeAggregationModel(QObject *parent) :
0019     QAbstractTableModel(parent)
0020 {
0021 }
0022 
0023 TimeAggregationModel::~TimeAggregationModel() = default;
0024 
0025 void TimeAggregationModel::setSourceModel(QAbstractItemModel* model)
0026 {
0027     Q_ASSERT(model);
0028     m_sourceModel = model;
0029     connect(model, &QAbstractItemModel::modelReset, this, [this]() { reload(); });
0030     reload();
0031 }
0032 
0033 TimeAggregationModel::AggregationMode TimeAggregationModel::aggregationMode() const
0034 {
0035     return m_mode;
0036 }
0037 
0038 void TimeAggregationModel::setAggregationMode(AggregationMode mode)
0039 {
0040     m_mode = mode;
0041     reload();
0042 }
0043 
0044 void TimeAggregationModel::reload()
0045 {
0046     if (!m_sourceModel)
0047         return;
0048 
0049     // TODO use the fact that the source model is sorted by time!
0050     QHash<QDateTime, QVector<Sample>> aggregator;
0051     for (int i = 0; i < m_sourceModel->rowCount(); ++i) {
0052         const auto dt = m_sourceModel->index(i, 0).data().toDateTime();
0053         const auto aggr = aggregate(dt);
0054         aggregator[aggr].push_back(m_sourceModel->index(i, 0).data(DataModel::SampleRole).value<Sample>());
0055     }
0056 
0057     beginResetModel();
0058     m_data.clear();
0059     m_maxValue = 0;
0060     for (auto it = aggregator.constBegin(); it != aggregator.constEnd(); ++it) {
0061         m_data.push_back({ it.key(), it.value() });
0062         m_maxValue = std::max(m_maxValue, (int)it.value().size());
0063     }
0064     std::sort(m_data.begin(), m_data.end(), [](const Data &lhs, const Data &rhs) {
0065         return lhs.time < rhs.time;
0066     });
0067     endResetModel();
0068 }
0069 
0070 QDateTime TimeAggregationModel::aggregate(const QDateTime &dt) const
0071 {
0072     switch (m_mode) {
0073         case AggregateYear:
0074             return QDateTime(QDate(dt.date().year(), 1, 1), QTime());
0075         case AggregateMonth:
0076             return QDateTime(QDate(dt.date().year(), dt.date().month(), 1), QTime());
0077         case AggregateWeek:
0078         {
0079             auto d = dt.date();
0080             d = d.addDays(1 - d.dayOfWeek());
0081             return QDateTime(d, QTime());
0082         }
0083         case AggregateDay:
0084             return QDateTime(dt.date(), QTime());
0085     }
0086 
0087     Q_UNREACHABLE();
0088 }
0089 
0090 QString TimeAggregationModel::timeToString(const QDateTime &dt) const
0091 {
0092     switch (m_mode) {
0093         case AggregateYear:
0094             return QString::number(dt.date().year());
0095         case AggregateMonth:
0096             return dt.date().toString(QStringLiteral("yyyy-MM"));
0097         case AggregateWeek:
0098         {
0099             int year = 0;
0100             int week = dt.date().weekNumber(&year);
0101             return QString::number(year) + QStringLiteral("-w") + QString::number(week);
0102         }
0103         case AggregateDay:
0104             return dt.date().toString(Qt::ISODate);
0105     }
0106 
0107     Q_UNREACHABLE();
0108 }
0109 
0110 int TimeAggregationModel::columnCount(const QModelIndex& parent) const
0111 {
0112     Q_UNUSED(parent);
0113     return 2;
0114 }
0115 
0116 int TimeAggregationModel::rowCount(const QModelIndex& parent) const
0117 {
0118     if (parent.isValid())
0119         return 0;
0120     return m_data.size();
0121 }
0122 
0123 QVariant TimeAggregationModel::data(const QModelIndex& index, int role) const
0124 {
0125     if (!index.isValid())
0126         return {};
0127 
0128     if (role == Qt::DisplayRole || role == DataDisplayRole || role == AccumulatedDisplayRole) {
0129         const auto d = m_data.at(index.row());
0130         switch (index.column()) {
0131             case 0: return d.time.toMSecsSinceEpoch(); // this is required by QtCharts...
0132             case 1: return d.samples.size();
0133         }
0134     } else if (role == DateTimeRole) {
0135         return m_data.at(index.row()).time;
0136     } else if (role == TimeDisplayRole) {
0137         return timeToString(m_data.at(index.row()).time);
0138     } else if (role == MaximumValueRole) {
0139         return m_maxValue;
0140     } else if (role == SamplesRole) {
0141         return QVariant::fromValue(m_data.at(index.row()).samples);
0142     } else if (role == AllSamplesRole) {
0143         return m_sourceModel->index(0, 0).data(DataModel::AllSamplesRole);
0144     }
0145 
0146     return {};
0147 }
0148 
0149 QVariant TimeAggregationModel::headerData(int section, Qt::Orientation orientation, int role) const
0150 {
0151     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
0152         switch (section) {
0153             case 0: return tr("Time");
0154             case 1: return tr("Samples");
0155         }
0156     }
0157     return QAbstractTableModel::headerData(section, orientation, role);
0158 }
0159 
0160 #include "moc_timeaggregationmodel.cpp"