provider.cpp 6.04 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
    Copyright (C) 2016 Volker Krause <vkrause@kde.org>

    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "provider.h"
19
#include "surveyinfo.h"
20
21
22
23
24
25
26
27
28
29

#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QSettings>
30
#include <QStringList>
31
#include <QTime>
Volker Krause's avatar
Volker Krause committed
32
#include <QTimer>
33
34
#include <QUrl>

35
#include <algorithm>
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <numeric>

using namespace UserFeedback;

namespace UserFeedback {
class ProviderPrivate
{
public:
    ProviderPrivate(Provider *qq);

    void reset();
    int currentApplicationTime() const;

    void load();
    void store();

    void aboutToQuit();

    QByteArray jsonData() const;
    void submitFinished();

    Provider *q;

    QString productId;

    QNetworkAccessManager *networkAccessManager;
    QUrl serverUrl;
    QDateTime lastSubmitTime;
Volker Krause's avatar
Volker Krause committed
64
    int submissionInterval;
65

66
67
    QStringList completedSurveys;

68
69
70
71
72
73
74
75
76
    QTime startTime;
    int startCount;
    int usageTime;
};
}

ProviderPrivate::ProviderPrivate(Provider *qq)
    : q(qq)
    , networkAccessManager(Q_NULLPTR)
Volker Krause's avatar
Volker Krause committed
77
    , submissionInterval(-1)
78
79
80
    , startCount(0)
    , usageTime(0)
{
81
82
83
84
    auto domain = QCoreApplication::organizationDomain().split(QLatin1Char('.'));
    std::reverse(domain.begin(), domain.end());
    productId = domain.join(QLatin1Char('.')) + QLatin1Char('.') + QCoreApplication::applicationName();

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    startTime.start();
}

void ProviderPrivate::reset()
{
    startCount = 0;
    usageTime = 0;
    startTime.start();
}

int ProviderPrivate::currentApplicationTime() const
{
    return usageTime + (startTime.elapsed() / 1000);
}

void ProviderPrivate::load()
{
    QSettings settings;
    settings.beginGroup(QStringLiteral("UserFeedback"));
    lastSubmitTime = settings.value(QStringLiteral("LastSubmission")).toDateTime();
    startCount = std::max(settings.value(QStringLiteral("ApplicationStartCount"), 0).toInt() + 1, 1);
    usageTime = std::max(settings.value(QStringLiteral("ApplicationTime"), 0).toInt(), 0);
107
    completedSurveys = settings.value(QStringLiteral("CompletedSurveys"), QStringList()).toStringList();
108
109
110
111
112
113
114
115
116
}

void ProviderPrivate::store()
{
    QSettings settings;
    settings.beginGroup(QStringLiteral("UserFeedback"));
    settings.setValue(QStringLiteral("LastSubmission"), lastSubmitTime);
    settings.setValue(QStringLiteral("ApplicationStartCount"), startCount);
    settings.setValue(QStringLiteral("ApplicationTime"), currentApplicationTime());
117
    settings.setValue(QStringLiteral("CompletedSurveys"), completedSurveys);
118
119
120
121
122
123
124
125
126
127
128
129
130
131
}

void ProviderPrivate::aboutToQuit()
{
    qDebug() << Q_FUNC_INFO;
    store();
}

QByteArray ProviderPrivate::jsonData() const
{
    QJsonObject obj;
    obj.insert(QStringLiteral("productId"), productId);
    obj.insert(QStringLiteral("startCount"), startCount);
    obj.insert(QStringLiteral("usageTime"), currentApplicationTime());
Volker Krause's avatar
Volker Krause committed
132
    obj.insert(QStringLiteral("version"), QCoreApplication::applicationVersion());
133
134
135
136
137
138
139
140
141
142

    QJsonDocument doc(obj);
    return doc.toJson();
}

void ProviderPrivate::submitFinished()
{
    auto reply = qobject_cast<QNetworkReply*>(q->sender());
    Q_ASSERT(reply);

143
144
    if (reply->error() != QNetworkReply::NoError) {
        qWarning() << "failed to submit user feedback:" << reply->errorString();
145
        return;
146
    }
147
148

    lastSubmitTime = QDateTime::currentDateTime();
149
150
151
152

    const auto obj = QJsonDocument::fromJson(reply->readAll()).object();
    if (obj.contains(QStringLiteral("survey"))) {
        const auto surveyObj = obj.value(QStringLiteral("survey")).toObject();
153
154
155
156
157
        const auto survey = SurveyInfo::fromJson(surveyObj);
        qDebug() << Q_FUNC_INFO << "got survey:" << survey.url();
        if (!survey.isValid() || completedSurveys.contains(QString::number(survey.id())))
            return;
        emit q->surveyAvailable(survey);
158
    }
Volker Krause's avatar
Volker Krause committed
159
160
161

    if (submissionInterval > 0)
        QTimer::singleShot(submissionInterval * 24 * 60 * 60 * 1000, q, SLOT(submit()));
162
163
164
165
166
167
168
169
170
171
172
173
174
175
}


Provider::Provider(QObject *parent) :
    QObject(parent),
    d(new ProviderPrivate(this))
{
    qDebug() << Q_FUNC_INFO;

    connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));

    d->load();
}

176
177
178
179
180
Provider::~Provider()
{
    delete d;
}

181
182
183
184
185
186
187
188
189
190
void Provider::setProductIdentifier(const QString &productId)
{
    d->productId = productId;
}

void Provider::setFeedbackServer(const QUrl &url)
{
    d->serverUrl = url;
}

Volker Krause's avatar
Volker Krause committed
191
192
193
194
195
196
197
198
199
200
201
202
203
204
void Provider::setSubmissionInterval(int days)
{
    d->submissionInterval = days;
    if (d->submissionInterval <= 0)
        return;

    const auto nextSubmission = d->lastSubmitTime.addDays(days);
    const auto now = QDateTime::currentDateTime();
    if (nextSubmission <= now)
        submit();
    else
        QTimer::singleShot(now.msecsTo(nextSubmission), this, SLOT(submit()));
}

205
206
207
208
209
210
void Provider::setSurveyCompleted(const SurveyInfo &info)
{
    d->completedSurveys.push_back(QString::number(info.id()));
    d->store();
}

211
212
213
214
215
216
217
218
219
220
221
222
223
224
void Provider::submit()
{
    if (!d->networkAccessManager)
        d->networkAccessManager = new QNetworkAccessManager(this);

    auto url = d->serverUrl;
    url.setPath(url.path() + QStringLiteral("/receiver/submit"));
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
    auto reply = d->networkAccessManager->post(request, d->jsonData());
    connect(reply, SIGNAL(finished()), this, SLOT(submitFinished()));
}

#include "moc_provider.cpp"