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"