File indexing completed on 2024-04-28 04:42:43

0001 /*
0002  * SPDX-FileCopyrightText: 2020-2021 Han Young <hanyoung@protonmail.com>
0003  * SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "pendingweatherforecast.h"
0009 #include "geotimezone.h"
0010 #include "kweathercore_p.h"
0011 #include "kweathercore_version.h"
0012 #include "pendingweatherforecast_p.h"
0013 
0014 #include <QDir>
0015 #include <QFile>
0016 #include <QJsonArray>
0017 #include <QJsonDocument>
0018 #include <QJsonObject>
0019 #include <QNetworkAccessManager>
0020 #include <QNetworkReply>
0021 #include <QStandardPaths>
0022 #include <QTimeZone>
0023 #include <QUrlQuery>
0024 
0025 namespace KWeatherCore
0026 {
0027 
0028 PendingWeatherForecastPrivate::PendingWeatherForecastPrivate(PendingWeatherForecast *qq)
0029     : q(qq)
0030 {
0031 }
0032 
0033 void PendingWeatherForecastPrivate::getTimezone(double latitude, double longitude)
0034 {
0035     auto timezoneSource = new GeoTimezone(m_manager, latitude, longitude, q);
0036     QObject::connect(timezoneSource, &GeoTimezone::finished, q, [this, timezoneSource]() {
0037         timezoneSource->deleteLater();
0038         parseTimezoneResult(timezoneSource->timezone());
0039     });
0040 }
0041 void PendingWeatherForecastPrivate::parseTimezoneResult(const QString &result)
0042 {
0043     hasTimezone = true;
0044     parser.forecast.setTimezone(result);
0045     m_timezone = result;
0046     if (parser.hasData()) {
0047         parser.applySunriseToForecast(QTimeZone(m_timezone.toUtf8()));
0048         Q_EMIT q->finished();
0049     }
0050 }
0051 
0052 void PendingWeatherForecastPrivate::parseWeatherForecastResults(QNetworkReply *reply)
0053 {
0054     reply->deleteLater();
0055     if (reply->error() != QNetworkReply::NoError) {
0056         qWarning() << "network error when fetching forecast:" << reply->errorString();
0057         setError(PendingWeatherForecast::NetworkError, reply->errorString());
0058         Q_EMIT q->finished();
0059         return;
0060     }
0061 
0062     parser.parseLocationForecast(reply->readAll());
0063     if (hasTimezone) {
0064         parser.applySunriseToForecast(QTimeZone(m_timezone.toUtf8()));
0065         Q_EMIT q->finished();
0066     }
0067 }
0068 
0069 PendingWeatherForecast::PendingWeatherForecast(double latitude,
0070                                                double longitude,
0071                                                const QString &timezone,
0072                                                QNetworkAccessManager *nam,
0073                                                QObject *parent)
0074     : Reply(new PendingWeatherForecastPrivate(this), parent)
0075 {
0076     Q_D(PendingWeatherForecast);
0077     d->m_manager = nam;
0078 
0079     // query weather api
0080     QUrl url(QStringLiteral("https://api.met.no/weatherapi/locationforecast/2.0/complete"));
0081     QUrlQuery query;
0082     query.addQueryItem(QStringLiteral("lat"), KWeatherCorePrivate::toFixedString(latitude));
0083     query.addQueryItem(QStringLiteral("lon"), KWeatherCorePrivate::toFixedString(longitude));
0084     url.setQuery(query);
0085     QNetworkRequest req(url);
0086     req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
0087 
0088     // see §Identification on https://api.met.no/conditions_service.html
0089     req.setHeader(QNetworkRequest::UserAgentHeader, QStringLiteral("KWeatherCore/" KWEATHERCORE_VERSION_STRING " kde-frameworks-devel@kde.org"));
0090     auto reply = d->m_manager->get(req);
0091     connect(reply, &QNetworkReply::finished, this, [reply, this]() {
0092         Q_D(PendingWeatherForecast);
0093         d->parseWeatherForecastResults(reply);
0094     });
0095 
0096     d->parser.forecast.setCoordinate(latitude, longitude);
0097 
0098     if (timezone.isEmpty()) {
0099         d->hasTimezone = false;
0100         d->getTimezone(latitude, longitude);
0101     } else {
0102         d->hasTimezone = true;
0103         d->parser.forecast.setTimezone(timezone);
0104         d->m_timezone = timezone;
0105     }
0106 }
0107 PendingWeatherForecast::PendingWeatherForecast(WeatherForecast data, QObject *parent)
0108     : Reply(new PendingWeatherForecastPrivate(this), parent)
0109 {
0110     Q_D(PendingWeatherForecast);
0111     d->parser.forecast = data;
0112     QMetaObject::invokeMethod(this, &PendingWeatherForecast::finished, Qt::QueuedConnection);
0113 }
0114 
0115 PendingWeatherForecast::~PendingWeatherForecast() = default;
0116 
0117 WeatherForecast PendingWeatherForecast::value() const
0118 {
0119     Q_D(const PendingWeatherForecast);
0120     return d->parser.forecast;
0121 }
0122 }
0123 
0124 #include "moc_pendingweatherforecast.cpp"