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"