Commit b710408e authored by Volker Krause's avatar Volker Krause
Browse files

Rework data API for sources

This now matches the new two-level structures the server expects.
parent a440cd46
......@@ -5,11 +5,11 @@
"aggregation": "category",
"elements": [
{
"name": "platform.os",
"name": "os",
"type": "string"
},
{
"name": "platform.version",
"name": "version",
"type": "string"
}
]
......
......@@ -5,11 +5,11 @@
"aggregation": "numeric",
"elements": [
{
"name": "screen.width",
"name": "width",
"type": "int"
},
{
"name": "screen.height",
"name": "height",
"type": "int"
}
]
......
......@@ -21,7 +21,6 @@
#include "userfeedbackcore_export.h"
#include "provider.h"
class QJsonObject;
class QSettings;
namespace UserFeedback {
......@@ -37,8 +36,21 @@ public:
/** Returns the name of this data source. */
QString name() const;
/** Override this to serialize the data you collected. */
virtual void toJson(QJsonObject &obj) = 0;
/** Implement this to return the data gathered by this source.
* Depending on the schema entry type for this source, the following
* formats are expected:
* - scalar entries: QAssiciativeIterable
* - list entries: QSequentialIterable containing QAssociativeIterable
* - map entries: QAssiciativeIterable containing QAssociativeIterable
* The innermost QAssiciativeIterable must only contain one of the following
* base types (which has to match the corresponding schema entry element):
* - QString
* - int
* - double
* - bool
* All keys must be strings.
*/
virtual QVariant data() = 0;
/** Load persistent state for this data source. */
virtual void load(QSettings *settings);
......
......@@ -17,8 +17,8 @@
#include "platforminfosource.h"
#include <QJsonObject>
#include <QSysInfo>
#include <QVariant>
using namespace UserFeedback;
......@@ -27,14 +27,16 @@ PlatformInfoSource::PlatformInfoSource() :
{
}
void PlatformInfoSource::toJson(QJsonObject& obj)
QVariant PlatformInfoSource::data()
{
QVariantMap m;
#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID))
// on Linux productType() is the distro name
obj.insert(QStringLiteral("platformOS"), QStringLiteral("linux"));
obj.insert(QStringLiteral("platformVersion"), QSysInfo::productType() + QLatin1Char('-') + QSysInfo::productVersion());
m.insert(QStringLiteral("os"), QStringLiteral("linux"));
m.insert(QStringLiteral("version"), QSysInfo::productType() + QLatin1Char('-') + QSysInfo::productVersion());
#else
obj.insert(QStringLiteral("platformOS"), QSysInfo::productType());
obj.insert(QStringLiteral("platformVersion"), QSysInfo::productVersion());
m.insert(QStringLiteral("os"), QSysInfo::productType());
m.insert(QStringLiteral("version"), QSysInfo::productVersion());
#endif
return m;
}
......@@ -31,7 +31,7 @@ class USERFEEDBACKCORE_EXPORT PlatformInfoSource : public AbstractDataSource
{
public:
PlatformInfoSource();
void toJson(QJsonObject &obj) Q_DECL_OVERRIDE;
QVariant data() Q_DECL_OVERRIDE;
};
}
......
......@@ -20,7 +20,6 @@
#include <QDebug>
#include <QHash>
#include <QJsonObject>
#include <QMap>
#include <QMetaProperty>
#include <QObject>
......@@ -125,22 +124,20 @@ void PropertyRatioSource::addValueMapping(const QVariant &value, const QString &
d->valueMap.insert(value, str);
}
void PropertyRatioSource::toJson(QJsonObject &obj)
QVariant PropertyRatioSource::data()
{
Q_D(PropertyRatioSource);
d->propertyChanged();
QJsonObject set;
QVariantMap m;
int total = 0;
for (auto it = d->ratioSet.constBegin(); it != d->ratioSet.constEnd(); ++it)
total += it.value();
for (auto it = d->ratioSet.constBegin(); it != d->ratioSet.constEnd(); ++it)
set.insert(it.key(), (double)it.value() / (double)(total));
m.insert(it.key(), (double)it.value() / (double)(total));
obj.insert(name(), set);
return m;
}
void PropertyRatioSource::load(QSettings *settings)
......
......@@ -49,7 +49,7 @@ public:
*/
void addValueMapping(const QVariant &value, const QString &str);
void toJson(QJsonObject &obj) Q_DECL_OVERRIDE;
QVariant data() Q_DECL_OVERRIDE;
void load(QSettings *settings) Q_DECL_OVERRIDE;
void store(QSettings *settings) Q_DECL_OVERRIDE;
......
......@@ -24,6 +24,7 @@
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
......@@ -177,15 +178,18 @@ void ProviderPrivate::aboutToQuit()
QByteArray ProviderPrivate::jsonData() const
{
QJsonObject obj;
obj.insert(QStringLiteral("productId"), productId);
if (statisticsMode != Provider::NoStatistics) {
obj.insert(QStringLiteral("startCount"), startCount);
obj.insert(QStringLiteral("usageTime"), currentApplicationTime());
obj.insert(QStringLiteral("version"), QCoreApplication::applicationVersion());
foreach (auto source, dataSources) {
if (statisticsMode >= source->collectionMode())
source->toJson(obj);
if (statisticsMode < source->collectionMode())
continue;
const auto data = source->data();
if (data.canConvert<QVariantMap>())
obj.insert(source->name(), QJsonObject::fromVariantMap(data.toMap()));
else if (data.canConvert<QVariantList>())
obj.insert(source->name(), QJsonArray::fromVariantList(data.value<QVariantList>()));
}
}
......@@ -372,7 +376,7 @@ void Provider::submit()
d->networkAccessManager = new QNetworkAccessManager(this);
auto url = d->serverUrl;
url.setPath(url.path() + QStringLiteral("/receiver/submit"));
url.setPath(url.path() + QStringLiteral("/receiver/submit/") + d->productId);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
request.setHeader(QNetworkRequest::UserAgentHeader, QStringLiteral("UserFeedback/") + QStringLiteral(USERFEEDBACK_VERSION));
......
......@@ -18,8 +18,6 @@
#include "screeninfosource.h"
#include <QGuiApplication>
#include <QJsonArray>
#include <QJsonObject>
#include <QScreen>
using namespace UserFeedback;
......@@ -34,13 +32,14 @@ ScreenInfoSource::ScreenInfoSource() :
{
}
void ScreenInfoSource::toJson(QJsonObject &obj)
QVariant ScreenInfoSource::data()
{
QJsonArray a;
QVariantList l;
foreach (auto screen, QGuiApplication::screens()) {
a.push_back(screenToString(screen));
QVariantMap m;
m.insert(QStringLiteral("width"), screen->size().width());
m.insert(QStringLiteral("height"), screen->size().height());
l.push_back(m);
}
obj.insert(QStringLiteral("screens"), a);
return l;
}
......@@ -28,7 +28,7 @@ class USERFEEDBACKCORE_EXPORT ScreenInfoSource : public AbstractDataSource
public:
ScreenInfoSource();
void toJson(QJsonObject &obj) override;
QVariant data() Q_DECL_OVERRIDE;
};
}
......
......@@ -20,8 +20,6 @@
#include <provider/core/screeninfosource.h>
#include <QDebug>
#include <QJsonArray>
#include <QJsonObject>
#include <QtTest/qtest.h>
#include <QObject>
#include <QStandardPaths>
......@@ -59,17 +57,16 @@ private slots:
void testPlatformInfoSource()
{
PlatformInfoSource src;
QJsonObject obj;
src.toJson(obj);
QVERIFY(obj.contains(QLatin1String("platformOS")));
auto v = obj.value(QLatin1String("platformOS"));
QVERIFY(v.isString());
auto obj = src.data().toMap();
QVERIFY(obj.contains(QLatin1String("os")));
auto v = obj.value(QLatin1String("os"));
QCOMPARE(v.type(), QVariant::String);
auto s = v.toString();
QVERIFY(!s.isEmpty());
QVERIFY(obj.contains(QLatin1String("platformVersion")));
v = obj.value(QLatin1String("platformVersion"));
QVERIFY(v.isString());
QVERIFY(obj.contains(QLatin1String("version")));
v = obj.value(QLatin1String("version"));
QCOMPARE(v.type(), QVariant::String);
s = v.toString();
QVERIFY(!s.isEmpty());
}
......@@ -77,12 +74,9 @@ private slots:
void testScreenInfoSource()
{
ScreenInfoSource src;
QJsonObject obj;
src.toJson(obj);
QVERIFY(obj.contains(QLatin1String("screens")));
auto v = obj.value(QLatin1String("screens"));
QVERIFY(v.isArray());
auto a = v.toArray();
auto v = src.data();
QVERIFY(v.canConvert<QVariantList>());
auto a = v.value<QVariantList>();
QVERIFY(a.size() > 0);
}
......@@ -93,38 +87,30 @@ private slots:
src.addValueMapping(23, QStringLiteral("value2"));
QTest::qWait(1);
QJsonObject obj;
src.toJson(obj);
QVERIFY(obj.contains(QLatin1String("ratioSample")));
auto v = obj.value(QLatin1String("ratioSample"));
QVERIFY(v.isObject());
auto o = v.toObject();
auto v = src.data();
QVERIFY(v.canConvert<QVariantMap>());
auto o = v.toMap();
QCOMPARE(o.size(), 0); // nothing recorded
QTest::qWait(1000);
obj = {};
src.toJson(obj);
v = obj.value(QLatin1String("ratioSample"));
o = v.toObject();
v = src.data();
o = v.toMap();
QCOMPARE(o.size(), 1);
QVERIFY(o.contains(QLatin1String("value1")));
v = o.value(QLatin1String("value1"));
QVERIFY(v.isDouble());
QCOMPARE(v.type(), QVariant::Double);
setProp(23);
QTest::qWait(1000);
obj = {};
src.toJson(obj);
v = obj.value(QLatin1String("ratioSample"));
o = v.toObject();
v = src.data();
o = v.toMap();
QCOMPARE(o.size(), 2);
QVERIFY(o.contains(QLatin1String("value2")));
v = o.value(QLatin1String("value2"));
QVERIFY(v.isDouble());
QCOMPARE(v.type(), QVariant::Double);
}
};
QTEST_MAIN(DataSourceTest)
#include "datasourcetest.moc"
......@@ -21,6 +21,7 @@
#include <rest/restclient.h>
#include <provider/core/provider.h>
#include <provider/core/platforminfosource.h>
#include <provider/core/screeninfosource.h>
#include <core/product.h>
......@@ -93,6 +94,7 @@ private slots:
provider.setProductIdentifier(QStringLiteral("org.kde.UserFeedback.UnitTestProduct"));
provider.setFeedbackServer(m_server.url());
provider.addDataSource(new ScreenInfoSource, Provider::AllStatistics);
provider.addDataSource(new PlatformInfoSource, Provider::AllStatistics);
provider.submit();
QTest::qWait(10); // HACK submit is async
......@@ -108,7 +110,18 @@ private slots:
QVERIFY(!obj.isEmpty());
QVERIFY(obj.contains(QLatin1String("id")));
QVERIFY(obj.contains(QLatin1String("timestamp")));
qDebug() << doc.toJson();
QVERIFY(obj.contains(QLatin1String("platform")));
auto sub = obj.value(QLatin1String("platform")).toObject();
QVERIFY(sub.contains(QLatin1String("os")));
QVERIFY(sub.contains(QLatin1String("version")));
QVERIFY(obj.contains(QLatin1String("screens")));
a = obj.value(QLatin1String("screens")).toArray();
QVERIFY(a.size() > 0);
sub = a.at(0).toObject();
QVERIFY(sub.contains(QLatin1String("height")));
QVERIFY(sub.contains(QLatin1String("width")));
}
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment