File indexing completed on 2024-12-15 03:44:59

0001 /*
0002     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "sample.h"
0008 #include <core/product.h>
0009 #include <core/schemaentry.h>
0010 #include <core/schemaentryelement.h>
0011 
0012 #include <QDateTime>
0013 #include <QDebug>
0014 #include <QHash>
0015 #include <QJsonArray>
0016 #include <QJsonDocument>
0017 #include <QJsonObject>
0018 #include <QSharedData>
0019 #include <QVariant>
0020 
0021 using namespace KUserFeedback::Console;
0022 
0023 class KUserFeedback::Console::SampleData : public QSharedData
0024 {
0025 public:
0026     QDateTime timestamp;
0027     QHash<QString, QVariant> data;
0028 };
0029 
0030 Sample::Sample() : d(new SampleData) {}
0031 Sample::Sample(const Sample&) = default;
0032 Sample::~Sample() = default;
0033 Sample& Sample::operator=(const Sample&) = default;
0034 
0035 QDateTime Sample::timestamp() const
0036 {
0037     return d->timestamp;
0038 }
0039 
0040 QVariant Sample::value(const QString &name) const
0041 {
0042     return d->data.value(name);
0043 }
0044 
0045 QVector<Sample> Sample::fromJson(const QByteArray &json, const Product &product)
0046 {
0047     const auto array = QJsonDocument::fromJson(json).array();
0048     QVector<Sample> samples;
0049     samples.reserve(array.size());
0050     for (auto it = array.begin(); it != array.end(); ++it) {
0051         const auto obj = it->toObject();
0052         Sample s;
0053         s.d->timestamp = QDateTime::fromString(obj.value(QStringLiteral("timestamp")).toString(), Qt::ISODate);
0054         foreach (const auto &entry, product.schema()) {
0055             if (!obj.contains(entry.name()))
0056                 continue;
0057             switch (entry.dataType()) {
0058                 case SchemaEntry::Scalar:
0059                 {
0060                     const auto entryData = obj.value(entry.name()).toObject();
0061                     foreach (const auto &elem, entry.elements()) {
0062                         if (!entryData.contains(elem.name()))
0063                             continue;
0064                         // TODO schema-dependent type conversion
0065                         s.d->data.insert(entry.name() + QLatin1Char('.') + elem.name(), entryData.value(elem.name()).toVariant());
0066                     }
0067                     break;
0068                 }
0069                 case SchemaEntry::List:
0070                 {
0071                     const auto entryArray = obj.value(entry.name()).toArray();
0072                     QVariantList l;
0073                     l.reserve(entryArray.size());
0074                     foreach (const auto &entryDataValue, entryArray) {
0075                         const auto entryData = entryDataValue.toObject();
0076                         QVariantMap m;
0077                         foreach (const auto &elem, entry.elements()) {
0078                             if (!entryData.contains(elem.name()))
0079                                 continue;
0080                             // TODO schema-dependent type conversion
0081                             m.insert(elem.name(), entryData.value(elem.name()).toVariant());
0082                         }
0083                         l.push_back(m);
0084                     }
0085                     s.d->data.insert(entry.name(), l);
0086                     break;
0087                 }
0088                 case SchemaEntry::Map:
0089                 {
0090                     const auto entryMap = obj.value(entry.name()).toObject();
0091                     QVariantMap m;
0092                     for (auto it = entryMap.begin(); it != entryMap.end(); ++it) {
0093                         const auto entryData = it.value().toObject();
0094                         QVariantMap m2;
0095                         foreach (const auto &elem, entry.elements()) {
0096                             if (!entryData.contains(elem.name()))
0097                                 continue;
0098                             // TODO schema-dependent type conversion
0099                             m2.insert(elem.name(), entryData.value(elem.name()).toVariant());
0100                         }
0101                         m.insert(it.key(), m2);
0102                     }
0103                     s.d->data.insert(entry.name(), m);
0104                     break;
0105                 }
0106             }
0107         }
0108         samples.push_back(s);
0109     }
0110     return samples;
0111 }
0112 
0113 QByteArray Sample::toJson(const QVector<Sample> &samples, const Product &product)
0114 {
0115     QJsonArray array;
0116     for (const auto &s : samples) {
0117         QJsonObject obj;
0118         obj[QLatin1String("timestamp")] = s.timestamp().toString(Qt::ISODate);
0119         foreach (const auto &entry, product.schema()) {
0120             switch (entry.dataType()) {
0121                 case SchemaEntry::Scalar:
0122                 {
0123                     QJsonObject subObj;
0124                     const auto entryData = obj.value(entry.name()).toObject();
0125                     foreach (const auto &elem, entry.elements()) {
0126                         const auto it = s.d->data.constFind(entry.name() + QLatin1Char('.') + elem.name());
0127                         if (it == s.d->data.constEnd())
0128                             continue;
0129                         subObj[elem.name()] = QJsonValue::fromVariant(it.value());
0130                     }
0131                     if (!subObj.isEmpty())
0132                         obj[entry.name()] = subObj;
0133                     break;
0134                 }
0135                 case SchemaEntry::List:
0136                 {
0137                     QJsonArray a;
0138                     const auto it = s.d->data.constFind(entry.name());
0139                     if (it == s.d->data.constEnd())
0140                         continue;
0141                     const auto l = it.value().toList();
0142                     for (const auto &v : l) {
0143                         QJsonObject subObj;
0144                         const auto m = v.toMap();
0145                         for (auto it = m.begin(); it != m.end(); ++it) {
0146                             subObj[it.key()] = QJsonValue::fromVariant(it.value());
0147                         }
0148                         a.push_back(subObj);
0149                     }
0150                     obj[entry.name()] = a;
0151                     break;
0152                 }
0153                 case SchemaEntry::Map:
0154                 {
0155                     QJsonObject map;
0156                     const auto it = s.d->data.constFind(entry.name());
0157                     if (it == s.d->data.constEnd())
0158                         continue;
0159                     const auto m = it.value().toMap();
0160                     for (auto it = m.begin(); it != m.end(); ++it) {
0161                         QJsonObject subObj;
0162                         const auto m = it.value().toMap();
0163                         for (auto it = m.begin(); it != m.end(); ++it) {
0164                             subObj[it.key()] = QJsonValue::fromVariant(it.value());
0165                         }
0166                         map[it.key()] = subObj;
0167                     }
0168                     obj[entry.name()] = map;
0169                     break;
0170                 }
0171             }
0172         }
0173         array.push_back(obj);
0174     }
0175 
0176     QJsonDocument doc;
0177     doc.setArray(array);
0178     return doc.toJson();
0179 }