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 "datamodel.h"
0008 
0009 #include <rest/restapi.h>
0010 #include <rest/restclient.h>
0011 #include <core/sample.h>
0012 
0013 #include <QDebug>
0014 #include <QNetworkReply>
0015 
0016 #include <algorithm>
0017 
0018 using namespace KUserFeedback::Console;
0019 
0020 static QString mapToString(const QVariantMap &map)
0021 {
0022     QStringList l;
0023     l.reserve(map.size());
0024     for (auto it = map.begin(); it != map.end(); ++it) {
0025         if (it.value().type() == QVariant::Map)
0026             l.push_back(it.key() + QLatin1String(": {") + mapToString(it.value().toMap()) + QLatin1Char('}'));
0027         else
0028             l.push_back(it.key() + QLatin1String(": ") + it.value().toString());
0029     }
0030     return l.join(QLatin1String(", "));
0031 }
0032 
0033 static QString listToString(const QVariantList &list)
0034 {
0035     QStringList l;
0036     l.reserve(list.size());
0037     for (const auto &v : list)
0038         l.push_back(mapToString(v.toMap()));
0039 
0040     return QLatin1String("[{") + l.join(QLatin1String("}, {")) + QLatin1String("}]");
0041 }
0042 
0043 QString DataModel::Column::name() const
0044 {
0045     if (entry.dataType() == SchemaEntry::Scalar)
0046         return entry.name() + QLatin1Char('.') + element.name();
0047     return entry.name();
0048 }
0049 
0050 DataModel::DataModel(QObject *parent) : QAbstractTableModel(parent)
0051 {
0052 }
0053 
0054 DataModel::~DataModel() = default;
0055 
0056 void DataModel::setRESTClient(RESTClient* client)
0057 {
0058     Q_ASSERT(client);
0059     m_restClient = client;
0060     connect(client, &RESTClient::clientConnected, this, &DataModel::reload);
0061     if (client->isConnected())
0062         reload();
0063 }
0064 
0065 Product DataModel::product() const
0066 {
0067     return m_product;
0068 }
0069 
0070 void DataModel::setProduct(const Product& product)
0071 {
0072     beginResetModel();
0073     m_product = product;
0074     m_columns.clear();
0075     for (const auto &entry : product.schema()) {
0076         if (entry.dataType() == SchemaEntry::Scalar) {
0077             for (const auto &elem : entry.elements())
0078                 m_columns.push_back({entry, elem});
0079         } else {
0080             m_columns.push_back({entry, {}});
0081         }
0082     }
0083     m_data.clear();
0084     reload();
0085     endResetModel();
0086 }
0087 
0088 void DataModel::setSamples(const QVector<Sample> &samples)
0089 {
0090     beginResetModel();
0091     m_data = samples;
0092     std::sort(m_data.begin(), m_data.end(), [](const Sample &lhs, const Sample &rhs) {
0093         return lhs.timestamp() < rhs.timestamp();
0094     });
0095     endResetModel();
0096 }
0097 
0098 void DataModel::reload()
0099 {
0100     if (!m_restClient || !m_restClient->isConnected() || !m_product.isValid())
0101         return;
0102 
0103     auto reply = RESTApi::listSamples(m_restClient, m_product);
0104     connect(reply, &QNetworkReply::finished, this, [this, reply]() {
0105         if (reply->error() == QNetworkReply::NoError) {
0106             const auto samples = Sample::fromJson(reply->readAll(), m_product);
0107             setSamples(samples);
0108         }
0109         reply->deleteLater();
0110     });
0111 }
0112 
0113 int DataModel::columnCount(const QModelIndex& parent) const
0114 {
0115     Q_UNUSED(parent);
0116     return m_columns.size() + 1;
0117 }
0118 
0119 int DataModel::rowCount(const QModelIndex& parent) const
0120 {
0121     if (parent.isValid())
0122         return 0;
0123     return m_data.size();
0124 }
0125 
0126 QVariant DataModel::data(const QModelIndex &index, int role) const
0127 {
0128     if (!index.isValid())
0129         return {};
0130 
0131     if (role == Qt::DisplayRole) {
0132         const auto sample = m_data.at(index.row());
0133         if (index.column() == 0)
0134             return sample.timestamp();
0135         const auto col = m_columns.at(index.column() - 1);
0136         const auto v = sample.value(col.name());
0137         switch (col.entry.dataType()) {
0138             case SchemaEntry::Scalar:
0139                 return v;
0140             case SchemaEntry::List:
0141                 return listToString(v.toList());
0142             case SchemaEntry::Map:
0143                 return mapToString(v.toMap());
0144         }
0145     } else if (role == SampleRole) {
0146         return QVariant::fromValue(m_data.at(index.row()));
0147     } else if (role == AllSamplesRole) {
0148         return QVariant::fromValue(m_data);
0149     }
0150 
0151     return {};
0152 }
0153 
0154 QVariant DataModel::headerData(int section, Qt::Orientation orientation, int role) const
0155 {
0156     if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_product.isValid()) {
0157         if (section == 0)
0158             return tr("Timestamp");
0159         const auto col = m_columns.at(section - 1);
0160         return QString(col.name());
0161     }
0162     return QAbstractTableModel::headerData(section, orientation, role);
0163 }
0164 
0165 #include "moc_datamodel.cpp"