From 620c4bd1e3a98c008b269cfde68dd0e0363a6092 Mon Sep 17 00:00:00 2001 From: arcan1s Date: Thu, 2 Jun 2016 11:09:02 +0300 Subject: [PATCH] Return to owm weather provider Since yahoo support already exists, so I've added new option X-AW-Provider (apiver 3). Added tests for these cases --- sources/awesomewidgets/abstractextitem.cpp | 4 +- .../awesomewidgets/abstractweatherprovider.h | 47 ++++++ .../awesomewidgets-extweather-ids.json | 131 +++++++++++++++- sources/awesomewidgets/extscript.h | 7 +- sources/awesomewidgets/extweather.cpp | 144 +++++++++--------- sources/awesomewidgets/extweather.h | 19 ++- sources/awesomewidgets/extweather.ui | 42 ++++- sources/awesomewidgets/graphicalitem.cpp | 3 +- sources/awesomewidgets/graphicalitem.h | 8 +- sources/awesomewidgets/owmweatherprovider.cpp | 112 ++++++++++++++ sources/awesomewidgets/owmweatherprovider.h | 43 ++++++ sources/awesomewidgets/weather/01d.png | Bin 0 -> 2859 bytes sources/awesomewidgets/weather/02d.png | Bin 0 -> 2969 bytes sources/awesomewidgets/weather/03d.png | Bin 0 -> 2565 bytes sources/awesomewidgets/weather/04d.png | Bin 0 -> 2773 bytes sources/awesomewidgets/weather/09d.png | Bin 0 -> 3818 bytes sources/awesomewidgets/weather/10d.png | Bin 0 -> 3793 bytes sources/awesomewidgets/weather/11d.png | Bin 0 -> 3777 bytes sources/awesomewidgets/weather/13d.png | Bin 0 -> 3901 bytes sources/awesomewidgets/weather/50d.png | Bin 0 -> 3328 bytes sources/awesomewidgets/weather/london.desktop | 3 +- .../awesomewidgets/yahooweatherprovider.cpp | 126 +++++++++++++++ sources/awesomewidgets/yahooweatherprovider.h | 47 ++++++ sources/test/testextweather.cpp | 90 +++++------ sources/test/testextweather.h | 4 +- sources/version.h.in | 5 +- 26 files changed, 704 insertions(+), 131 deletions(-) create mode 100644 sources/awesomewidgets/abstractweatherprovider.h create mode 100644 sources/awesomewidgets/owmweatherprovider.cpp create mode 100644 sources/awesomewidgets/owmweatherprovider.h create mode 100644 sources/awesomewidgets/weather/01d.png create mode 100644 sources/awesomewidgets/weather/02d.png create mode 100644 sources/awesomewidgets/weather/03d.png create mode 100644 sources/awesomewidgets/weather/04d.png create mode 100644 sources/awesomewidgets/weather/09d.png create mode 100644 sources/awesomewidgets/weather/10d.png create mode 100644 sources/awesomewidgets/weather/11d.png create mode 100644 sources/awesomewidgets/weather/13d.png create mode 100644 sources/awesomewidgets/weather/50d.png create mode 100644 sources/awesomewidgets/yahooweatherprovider.cpp create mode 100644 sources/awesomewidgets/yahooweatherprovider.h diff --git a/sources/awesomewidgets/abstractextitem.cpp b/sources/awesomewidgets/abstractextitem.cpp index 62e0e09..787acc0 100644 --- a/sources/awesomewidgets/abstractextitem.cpp +++ b/sources/awesomewidgets/abstractextitem.cpp @@ -178,7 +178,7 @@ void AbstractExtItem::setName(const QString _name) void AbstractExtItem::setNumber(int _number) { qCDebug(LOG_LIB) << "Number" << _number; - if (_number == -1) + if (_number == -1) { _number = []() { qCWarning(LOG_LIB) << "Number is empty, generate new one"; qsrand(QTime::currentTime().msec()); @@ -186,6 +186,8 @@ void AbstractExtItem::setNumber(int _number) qCInfo(LOG_LIB) << "Generated number is" << n; return n; }(); + writeConfiguration(); + } m_number = _number; } diff --git a/sources/awesomewidgets/abstractweatherprovider.h b/sources/awesomewidgets/abstractweatherprovider.h new file mode 100644 index 0000000..9582cac --- /dev/null +++ b/sources/awesomewidgets/abstractweatherprovider.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * This file is part of awesome-widgets * + * * + * awesome-widgets is free software: you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * awesome-widgets 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with awesome-widgets. If not, see http://www.gnu.org/licenses/ * + ***************************************************************************/ + +#ifndef ABSTRACTWEATHERPROVIDER_H +#define ABSTRACTWEATHERPROVIDER_H + +#include +#include + + +class AbstractWeatherProvider : public QObject +{ + Q_OBJECT + Q_PROPERTY(int number READ number) + +public: + explicit AbstractWeatherProvider(QObject *parent, const int number) + : QObject(parent) + , m_number(number){}; + virtual ~AbstractWeatherProvider(){}; + virtual void initUrl(const QString city, const QString country, + const int ts) + = 0; + virtual QVariantHash parse(const QVariantMap &json) const = 0; + virtual QUrl url() const = 0; + int number() const { return m_number; }; + +private: + int m_number; +}; + + +#endif /* ABSTRACTWEATHERPROVIDER_H */ diff --git a/sources/awesomewidgets/awesomewidgets-extweather-ids.json b/sources/awesomewidgets/awesomewidgets-extweather-ids.json index 69f9085..e8ec649 100644 --- a/sources/awesomewidgets/awesomewidgets-extweather-ids.json +++ b/sources/awesomewidgets/awesomewidgets-extweather-ids.json @@ -1,11 +1,75 @@ { - "__url": "https://developer.yahoo.com/weather/documentation.html", + "__url-yahoo": "https://developer.yahoo.com/weather/documentation.html", + "__url-owm": "http://openweathermap.org/weather-conditions", "image": { "__comment": "should be described as html image with full path inside", "default": "", + "800": "", + + "801": "", + + "802": "", + "803": "", + + "804": "", + + "300": "", + "301": "", + "302": "", + "310": "", + "311": "", + "312": "", + "313": "", + "314": "", + "321": "", + "520": "", + "521": "", + "522": "", + "531": "", + + "500": "", + "501": "", + "502": "", + "503": "", + "504": "", + + "200": "", + "201": "", + "202": "", + "210": "", + "211": "", + "212": "", + "221": "", + "230": "", + "231": "", + "232": "", + + "511": "", + "600": "", + "601": "", + "602": "", + "611": "", + "612": "", + "615": "", + "616": "", + "620": "", + "621": "", + "622": "", + + "701": "", + "711": "", + "721": "", + "731": "", + "741": "", + "751": "", + "761": "", + "762": "", + "771": "", + "781": "", + "0": "", "1": "", "2": "", @@ -59,8 +123,71 @@ "text": { "default": "\u2604", - "3200": "\u2604", + "800": "\u2600", + + "801": "\u26C5", + + "802": "\u2601", + "803": "\u2601", + + "804": "\u2601", + + "300": "\u2602", + "301": "\u2602", + "302": "\u2602", + "310": "\u2602", + "311": "\u2602", + "312": "\u2602", + "313": "\u2602", + "314": "\u2602", + "321": "\u2602", + "520": "\u2602", + "521": "\u2602", + "522": "\u2602", + "531": "\u2602", + + "500": "\u2614", + "501": "\u2614", + "502": "\u2614", + "503": "\u2614", + "504": "\u2614", + + "200": "\u2608", + "201": "\u2608", + "202": "\u2608", + "210": "\u2608", + "211": "\u2608", + "212": "\u2608", + "221": "\u2608", + "230": "\u2608", + "231": "\u2608", + "232": "\u2608", + + "511": "\u2603", + "600": "\u2603", + "601": "\u2603", + "602": "\u2603", + "611": "\u2603", + "612": "\u2603", + "615": "\u2603", + "616": "\u2603", + "620": "\u2603", + "621": "\u2603", + "622": "\u2603", + + "701": "\u26C5", + "711": "\u26C5", + "721": "\u26C5", + "731": "\u26C5", + "741": "\u26C5", + "751": "\u26C5", + "761": "\u26C5", + "762": "\u26C5", + "771": "\u26C5", + "781": "\u26C5", + + "3200": "\u2604", "0": "\u2604", "1": "\u2604", "2": "\u2604", diff --git a/sources/awesomewidgets/extscript.h b/sources/awesomewidgets/extscript.h index 1dca548..5189a63 100644 --- a/sources/awesomewidgets/extscript.h +++ b/sources/awesomewidgets/extscript.h @@ -37,7 +37,12 @@ class ExtScript : public AbstractExtItem Q_PROPERTY(Redirect redirect READ redirect WRITE setRedirect) public: - enum class Redirect { stdout2stderr = 0, nothing = 1, stderr2stdout = 2, swap = 3 }; + enum class Redirect { + stdout2stderr = 0, + nothing = 1, + stderr2stdout = 2, + swap = 3 + }; explicit ExtScript(QWidget *parent, const QString filePath = QString()); virtual ~ExtScript(); diff --git a/sources/awesomewidgets/extweather.cpp b/sources/awesomewidgets/extweather.cpp index 0aabd44..456d893 100644 --- a/sources/awesomewidgets/extweather.cpp +++ b/sources/awesomewidgets/extweather.cpp @@ -27,11 +27,12 @@ #include #include #include -#include #include #include "awdebug.h" +#include "owmweatherprovider.h" +#include "yahooweatherprovider.h" ExtWeather::ExtWeather(QWidget *parent, const QString filePath) @@ -68,6 +69,7 @@ ExtWeather::~ExtWeather() SLOT(weatherReplyReceived(QNetworkReply *))); m_manager->deleteLater(); + delete m_providerObject; delete ui; } @@ -83,6 +85,7 @@ ExtWeather *ExtWeather::copy(const QString _fileName, const int _number) item->setCountry(country()); item->setImage(image()); item->setNumber(_number); + item->setProvider(provider()); item->setTs(ts()); return item; @@ -117,6 +120,28 @@ bool ExtWeather::image() const } +ExtWeather::Provider ExtWeather::provider() const +{ + return m_provider; +} + + +QString ExtWeather::strProvider() const +{ + QString value; + switch (m_provider) { + case Provider::OWM: + value = QString("OWM"); + break; + case Provider::Yahoo: + value = QString("Yahoo"); + break; + } + + return value; +} + + int ExtWeather::ts() const { return m_ts; @@ -134,7 +159,7 @@ void ExtWeather::setCity(const QString _city) qCDebug(LOG_LIB) << "City" << _city; m_city = _city; - initUrl(); + initProvider(); } @@ -143,7 +168,7 @@ void ExtWeather::setCountry(const QString _country) qCDebug(LOG_LIB) << "Country" << _country; m_country = _country; - initUrl(); + initProvider(); } @@ -155,11 +180,32 @@ void ExtWeather::setImage(const bool _image) } +void ExtWeather::setProvider(const Provider _provider) +{ + qCDebug(LOG_LIB) << "Provider" << static_cast(_provider); + + m_provider = _provider; + initProvider(); +} + + +void ExtWeather::setStrProvider(const QString _provider) +{ + qCDebug(LOG_LIB) << "Provider" << _provider; + + if (_provider == QString("Yahoo")) + setProvider(Provider::Yahoo); + else + setProvider(Provider::OWM); +} + + void ExtWeather::setTs(const int _ts) { qCDebug(LOG_LIB) << "Timestamp" << _ts; m_ts = _ts; + initProvider(); } @@ -176,16 +222,11 @@ void ExtWeather::readConfiguration() // api == 2 setImage(settings.value(QString("X-AW-Image"), QVariant(m_image)).toString() == QString("true")); + // api == 3 + setStrProvider( + settings.value(QString("X-AW-Provider"), strProvider()).toString()); settings.endGroup(); - // update for current API - if ((apiVersion() > 0) && (apiVersion() < AWEWAPI)) { - qCWarning(LOG_LIB) << "Bump API version from" << apiVersion() << "to" - << AWEWAPI; - setApiVersion(AWEWAPI); - writeConfiguration(); - } - bumpApi(AWEWAPI); } @@ -224,7 +265,8 @@ QVariantHash ExtWeather::run() if (times == 1) { qCInfo(LOG_LIB) << "Send request"; isRunning = true; - QNetworkReply *reply = m_manager->get(QNetworkRequest(m_url)); + QNetworkReply *reply + = m_manager->get(QNetworkRequest(m_providerObject->url())); new QReplyTimeout(reply, REQUEST_TIMEOUT); } @@ -244,6 +286,7 @@ int ExtWeather::showConfiguration(const QVariant args) ui->lineEdit_name->setText(name()); ui->lineEdit_comment->setText(comment()); ui->label_numberValue->setText(QString("%1").arg(number())); + ui->comboBox_provider->setCurrentIndex(static_cast(m_provider)); ui->lineEdit_city->setText(m_city); ui->lineEdit_country->setText(m_country); ui->spinBox_timestamp->setValue(m_ts); @@ -261,6 +304,7 @@ int ExtWeather::showConfiguration(const QVariant args) setApiVersion(AWEWAPI); setCity(ui->lineEdit_city->text()); setCountry(ui->lineEdit_country->text()); + setProvider(static_cast(ui->comboBox_provider->currentIndex())); setTs(ui->spinBox_timestamp->value()); setImage(ui->checkBox_image->checkState() == Qt::Checked); setActive(ui->checkBox_active->checkState() == Qt::Checked); @@ -282,6 +326,7 @@ void ExtWeather::writeConfiguration() const settings.setValue(QString("X-AW-City"), m_city); settings.setValue(QString("X-AW-Country"), m_country); settings.setValue(QString("X-AW-Image"), m_image); + settings.setValue(QString("X-AW-Provider"), strProvider()); settings.setValue(QString("X-AW-TS"), m_ts); settings.endGroup(); @@ -304,69 +349,31 @@ void ExtWeather::weatherReplyReceived(QNetworkReply *reply) return; } - // convert to map - QVariantMap json = jsonDoc.toVariant().toMap()[QString("query")].toMap(); - if (json[QString("count")].toInt() != 1) { - qCWarning(LOG_LIB) << "Found data count" - << json[QString("count")].toInt() << "is not 1"; + QVariantHash data = m_providerObject->parse(jsonDoc.toVariant().toMap()); + if (data.isEmpty()) return; - } - QVariantMap results - = json[QString("results")].toMap()[QString("channel")].toMap(); - QVariantMap item = results[QString("item")].toMap(); - - if (m_ts == 0) { - // current weather - int id = item[QString("condition")].toMap()[QString("code")].toInt(); - values[tag(QString("weatherId"))] = id; - values[tag(QString("weather"))] = weatherFromInt(id); - values[tag(QString("temperature"))] - = item[QString("condition")].toMap()[QString("temp")].toInt(); - values[tag(QString("timestamp"))] - = item[QString("condition")].toMap()[QString("date")].toString(); - values[tag(QString("humidity"))] = results[QString("atmosphere")] - .toMap()[QString("humidity")] - .toInt(); - values[tag(QString("pressure"))] - = static_cast(results[QString("atmosphere")] - .toMap()[QString("pressure")] - .toFloat()); - } else { - // forecast weather - QVariantList weatherList = item[QString("forecast")].toList(); - QVariantMap weatherMap = weatherList.count() < m_ts - ? weatherList.last().toMap() - : weatherList.at(m_ts).toMap(); - int id = weatherMap[QString("code")].toInt(); - values[tag(QString("weatherId"))] = id; - values[tag(QString("weather"))] = weatherFromInt(id); - values[tag(QString("timestamp"))] - = weatherMap[QString("date")].toString(); - // yahoo provides high and low temperatures. Lets calculate average one - values[tag(QString("temperature"))] - = (weatherMap[QString("high")].toFloat() - + weatherMap[QString("low")].toFloat()) - / 2.0; - // ... and no forecast data for humidity and pressure - values[tag(QString("humidity"))] = 0; - values[tag(QString("pressure"))] = 0.0; - } + values = data; + values[tag(QString("weather"))] + = weatherFromInt(values[tag(QString("weatherId"))].toInt()); emit(dataReceived(values)); } -void ExtWeather::initUrl() +void ExtWeather::initProvider() { - // init query - m_url = QUrl(YAHOO_WEATHER_URL); - QUrlQuery params; - params.addQueryItem(QString("format"), QString("json")); - params.addQueryItem(QString("env"), - QString("store://datatables.org/alltableswithkeys")); - params.addQueryItem(QString("q"), - QString(YAHOO_WEATHER_QUERY).arg(m_city, m_country)); - m_url.setQuery(params); + delete m_providerObject; + + switch (m_provider) { + case Provider::OWM: + m_providerObject = new OWMWeatherProvider(this, number()); + break; + case Provider::Yahoo: + m_providerObject = new YahooWeatherProvider(this, number()); + break; + } + + return m_providerObject->initUrl(m_city, m_country, m_ts); } @@ -375,6 +382,7 @@ void ExtWeather::translate() ui->label_name->setText(i18n("Name")); ui->label_comment->setText(i18n("Comment")); ui->label_number->setText(i18n("Tag")); + ui->label_provider->setText(i18n("Provider")); ui->label_city->setText(i18n("City")); ui->label_country->setText(i18n("Country")); ui->label_timestamp->setText(i18n("Timestamp")); diff --git a/sources/awesomewidgets/extweather.h b/sources/awesomewidgets/extweather.h index 6b60583..8ee174c 100644 --- a/sources/awesomewidgets/extweather.h +++ b/sources/awesomewidgets/extweather.h @@ -22,12 +22,8 @@ #include "abstractextitem.h" -#define YAHOO_WEATHER_URL "https://query.yahooapis.com/v1/public/yql" -#define YAHOO_WEATHER_QUERY \ - "select * from weather.forecast where u='c' and woeid in (select woeid " \ - "from geo.places(1) where text='%1, %2')" - +class AbstractWeatherProvider; namespace Ui { class ExtWeather; @@ -39,9 +35,13 @@ class ExtWeather : public AbstractExtItem Q_PROPERTY(QString city READ city WRITE setCity) Q_PROPERTY(QString country READ country WRITE setCountry) Q_PROPERTY(bool image READ image WRITE setImage) + Q_PROPERTY(Provider povider READ provider WRITE setProvider) + Q_PROPERTY(QString strPovider READ strProvider WRITE setStrProvider) Q_PROPERTY(int ts READ ts WRITE setTs) public: + enum class Provider { OWM = 0, Yahoo = 1 }; + explicit ExtWeather(QWidget *parent, const QString filePath = QString()); virtual ~ExtWeather(); ExtWeather *copy(const QString _fileName, const int _number); @@ -50,12 +50,16 @@ public: QString city() const; QString country() const; bool image() const; + Provider provider() const; + QString strProvider() const; int ts() const; QString uniq() const; // set methods void setCity(const QString _city = QString("London")); void setCountry(const QString _country = QString("uk")); void setImage(const bool _image = false); + void setProvider(const Provider _provider = Provider::OWM); + void setStrProvider(const QString _provider = QString("OWM")); void setTs(const int _ts = 0); public slots: @@ -70,15 +74,16 @@ private slots: private: QNetworkAccessManager *m_manager = nullptr; - QUrl m_url; + AbstractWeatherProvider *m_providerObject = nullptr; bool isRunning = false; Ui::ExtWeather *ui = nullptr; - void initUrl(); + void initProvider(); void translate(); // properties QString m_city = QString("London"); QString m_country = QString("uk"); bool m_image = false; + Provider m_provider = Provider::OWM; int m_ts = 0; QVariantMap m_jsonMap = QVariantMap(); // values diff --git a/sources/awesomewidgets/extweather.ui b/sources/awesomewidgets/extweather.ui index 5015643..0b5bb43 100644 --- a/sources/awesomewidgets/extweather.ui +++ b/sources/awesomewidgets/extweather.ui @@ -7,7 +7,7 @@ 0 0 420 - 301 + 333 @@ -81,6 +81,46 @@ + + + + + + + 0 + 0 + + + + Provider + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + OWM + + + + + Yahoo + + + + + + diff --git a/sources/awesomewidgets/graphicalitem.cpp b/sources/awesomewidgets/graphicalitem.cpp index 313f022..d236cdc 100644 --- a/sources/awesomewidgets/graphicalitem.cpp +++ b/sources/awesomewidgets/graphicalitem.cpp @@ -514,7 +514,8 @@ int GraphicalItem::showConfiguration(const QVariant args) setActiveColor(ui->lineEdit_activeColor->text()); setInactiveColor(ui->lineEdit_inactiveColor->text()); setType(static_cast(ui->comboBox_type->currentIndex())); - setDirection(static_cast(ui->comboBox_direction->currentIndex())); + setDirection( + static_cast(ui->comboBox_direction->currentIndex())); setItemHeight(ui->spinBox_height->value()); setItemWidth(ui->spinBox_width->value()); diff --git a/sources/awesomewidgets/graphicalitem.h b/sources/awesomewidgets/graphicalitem.h index b7bc656..cbfe3a2 100644 --- a/sources/awesomewidgets/graphicalitem.h +++ b/sources/awesomewidgets/graphicalitem.h @@ -50,7 +50,13 @@ class GraphicalItem : public AbstractExtItem public: enum class Direction { LeftToRight = 0, RightToLeft = 1 }; - enum class Type { Horizontal = 0, Vertical = 1, Circle = 2, Graph = 3, Bars = 4 }; + enum class Type { + Horizontal = 0, + Vertical = 1, + Circle = 2, + Graph = 3, + Bars = 4 + }; explicit GraphicalItem(QWidget *parent, const QString filePath = QString()); virtual ~GraphicalItem(); diff --git a/sources/awesomewidgets/owmweatherprovider.cpp b/sources/awesomewidgets/owmweatherprovider.cpp new file mode 100644 index 0000000..f3057b9 --- /dev/null +++ b/sources/awesomewidgets/owmweatherprovider.cpp @@ -0,0 +1,112 @@ +/*************************************************************************** + * This file is part of awesome-widgets * + * * + * awesome-widgets is free software: you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * awesome-widgets 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with awesome-widgets. If not, see http://www.gnu.org/licenses/ * + ***************************************************************************/ + +#include "owmweatherprovider.h" + +#include +#include + +#include "awdebug.h" + + +OWMWeatherProvider::OWMWeatherProvider(QObject *parent, const int number) + : AbstractWeatherProvider(parent, number) +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; +} + + +OWMWeatherProvider::~OWMWeatherProvider() +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; +} + + +void OWMWeatherProvider::initUrl(const QString city, const QString country, + const int ts) +{ + qCDebug(LOG_LIB) << "Init query for" << city << country << "with ts" << ts; + + m_ts = ts; + + if (m_ts == 0) + m_url = QUrl(OWM_WEATHER_URL); + else + m_url = QUrl(OWM_FORECAST_URL); + QUrlQuery params; + params.addQueryItem(QString("q"), QString("%1,%2").arg(city, country)); + params.addQueryItem(QString("units"), QString("metric")); + m_url.setQuery(params); +} + + +QVariantHash OWMWeatherProvider::parse(const QVariantMap &json) const +{ + qCDebug(LOG_LIB) << "Parse json" << json; + + if (json[QString("cod")].toInt() != 200) { + qCWarning(LOG_LIB) << "Invalid OpenWeatherMap return code" + << json[QString("cod")].toInt(); + return QVariantHash(); + } + + if (m_ts == 0) { + return parseSingleJson(json); + } else { + QVariantList list = json[QString("list")].toList(); + return parseSingleJson(list.count() <= m_ts ? list.at(m_ts - 1).toMap() + : list.last().toMap()); + } +} + + +QUrl OWMWeatherProvider::url() const +{ + return m_url; +} + + +QVariantHash OWMWeatherProvider::parseSingleJson(const QVariantMap &json) const +{ + qCDebug(LOG_LIB) << "Single json data" << json; + + QVariantHash output; + + // weather status + QVariantList weather = json[QString("weather")].toList(); + if (!weather.isEmpty()) { + int id = weather.first().toMap()[QString("id")].toInt(); + output[QString("weatherId%1").arg(number())] = id; + } + + // main data + QVariantMap mainWeather = json[QString("main")].toMap(); + if (!weather.isEmpty()) { + output[QString("humidity%1").arg(number())] + = mainWeather[QString("humidity")].toFloat(); + output[QString("pressure%1").arg(number())] + = mainWeather[QString("pressure")].toFloat(); + output[QString("temperature%1").arg(number())] + = mainWeather[QString("temp")].toFloat(); + } + + // timestamp + output[QString("timestamp%1").arg(number())] + = QDateTime::fromTime_t(json[QString("dt")].toUInt()).toUTC(); + + return output; +} diff --git a/sources/awesomewidgets/owmweatherprovider.h b/sources/awesomewidgets/owmweatherprovider.h new file mode 100644 index 0000000..6f8b639 --- /dev/null +++ b/sources/awesomewidgets/owmweatherprovider.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * This file is part of awesome-widgets * + * * + * awesome-widgets is free software: you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * awesome-widgets 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with awesome-widgets. If not, see http://www.gnu.org/licenses/ * + ***************************************************************************/ + +#ifndef OWMWEATHERPROVIDER_H +#define OWMWEATHERPROVIDER_H + +#include "abstractweatherprovider.h" + +#define OWM_WEATHER_URL "http://arcanis.me/weather" +#define OWM_FORECAST_URL "http://arcanis.me/forecast" + + +class OWMWeatherProvider : public AbstractWeatherProvider +{ +public: + explicit OWMWeatherProvider(QObject *parent, const int number); + virtual ~OWMWeatherProvider(); + void initUrl(const QString city, const QString country, const int); + QVariantHash parse(const QVariantMap &json) const; + QUrl url() const; + +private: + QVariantHash parseSingleJson(const QVariantMap &json) const; + int m_ts = 0; + QUrl m_url; +}; + + +#endif /* OWMWEATHERPROVIDER_H */ diff --git a/sources/awesomewidgets/weather/01d.png b/sources/awesomewidgets/weather/01d.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2f792c324408eaccb8a13e0cd16fd502dd51cf GIT binary patch literal 2859 zcmeAS@N?(olHy`uVBq!ia0y~yU@!t<4mJh`208nVjSLJ7oCO|{#S9GG!XV7ZFl&wk z0|Tp1W=KRygs+cPa(=E}VoH8es$NBI0RsrwR9IEy7UZUuBq~(o=HwMyRoE(lRaoT} zTY-f2l@!2AO0sR0B76fBob!uP6-@OE^^BD4xD*r=Y>HCStb$zJpxTR4(rlG7N=gc> z^!3Zj%k|2Q_413-^$jg8E%gnI^o@*ki&D~bi!1X=5-W7`ij^UTz|3(;Elw`VEGWs$ z&r<-Io0ybeT4JlD1hPm02B7ZDEr45;R}A%4a(=FUMPh-zp`L+0++>j96}bggzWFJc zX_YRCC8>5s28Kqu2Ijg(<{^g0RtDx)7N$1(Xlf94+JI!7i&7Iy@{2<9^Ks9t0hP|Y^_AYUN)3LIo02cd}*@@+|G8X{a#ybF@S^e;%%%D*TxHLoNQ z6qv; z5>VHILLDscoS&0l6kL#)oN8xgYHDl>)rKUDt^;gUBrdCvB%m6p7BDHv@GMtQT9gCM z*(u5TsX3{+sd**E`i6RjsM#(h*{UcrJ)@);mhK^W7+hvJ=9Hj{fTzEgNs2>k!Z)|EBLmGfq}Kv)5S3)gY|7hwNG%k z#Q(X|oU$9eG&{J01Ll0TeSOU6PD8_|h(&H!e3H)0nZ5pH+PC@jI?^Za+;Kj8=1kaT z-Qws&YWrqNupGU9Pj8FCAWD9BtFQ+NPd0dm(spS#YPpi-~p8-@ZKR zd?)<#{MYKeXId4FUjA}D#bGxccF7ElY-~OcCZtuduC> z?e7~G>xoOrE-<|@aoal;H@%Mb$NeH3!X~N4-aB)k;iGur=1=~036WaDx7r;<4jg{$ zzq$46%Pk9L?|QRLqC+LpX2y1x;NZk&0k)KUCboXoSGf((%>dBvTQ(!;Q{We&3C`bEda>z+ zFy}Qc`4JXy>z9~16;lrw_oJnUdrb^}B zke;#WWYbpT#-8W{;!D0P4SFxuf-T%s5tF`9q6#0Ja>VfH%OB6D9&K23ae$}1B52`0!ih5;T7O+Ef z;`Fj6>9c%>S}R?A=KPxZ?&eo8mCB@EiP-eN$y_;yJ2(99RO2GO&!-o9m3)}%H^u11((m`Q?jG`&eO|L+ z-scT=4jHC%rvE*)H@Cg+-O_NYQ`~FWW$xCzT}MKti)~4 zqBi?LnfMYb@A=ESR_?JCQ`l%4t-d+$H^-Y=-ioPyuYY?rOwrt;{Bz^BpRNphH_?5@x#Em}-HN8awRdcHKi;-H zMfv-Qh3-1xUry;ui+<$v_%m(g3m$Wq>4wcu&7b5=2!Fv;R96vnPw;7?9b81tlA;xQcjyd+9 tFqxB;;@H0;Eox>@v)sf?i>oL8v6&yX>ttr_{SRu+db;|#taD0e0su!iWjp`? literal 0 HcmV?d00001 diff --git a/sources/awesomewidgets/weather/02d.png b/sources/awesomewidgets/weather/02d.png new file mode 100644 index 0000000000000000000000000000000000000000..d86a99c86ac05f59a25f5c330752ba4681e51179 GIT binary patch literal 2969 zcmeAS@N?(olHy`uVBq!ia0y~yU@!t<4mJh`208nVjSLJ7oCO|{#S9GG!XV7ZFl&wk z0|Tp1W=KRygs+cPa(=E}VoH8es$NBI0RsrwR9IEy7UZUuBq~(o=HwMyRoE(lRaoT} zTY-f2l@!2AO0sR0B76fBob!uP6-@OE^^BD4xD*r=Y>HCStb$zJpxTR4(rlG7N=gc> z^!3Zj%k|2Q_413-^$jg8E%gnI^o@*ki&D~bi!1X=5-W7`ij^UTz|3(;Elw`VEGWs$ z&r<-Io0ybeT4JlD1hPm02B7ZDEr45;R}A%4a(=FUMPh-zp`L+0++>j96}bggzWFJc zX_YRCC8>5s28Kqu2Ijg(<{^g0RtDx)=H@o~Xlf94+JI!7i&7Iy@{2<9^KQ;1$<6;RDK`XFB*`3f9lAP1p|6Y_0IW*Q<~QM?P1!SpXk)XKjoGc~Uy z5fqwshBo>bVo*ndoCB7N3`#A|&nX3~H#dT6Ko&&T;GCaZP?TC+oSC0zX9`u1EQYQg zVSZ>{W(nL)Xu{~aA`tR+aAzS4LN!n=uv05CaRfF<1~afhqE_HE5#f@Vo06|3e|=rjIIN0RU|H}kR+fQsTMFP$?z;!P+F7& z&eYQcg@^{ZxY=>p=!1(vP?2cIwSrfWoq>V%v!{z=NCxZMuxg*# zp(6J87wdbSPTs&!*3;#YW5#YLaN~x&0gsPi;jF)hdoG@pF!3|@HJP6EOEo;L5vw(oV{ z?-jp$|L2_LccT`89l|Ypg&hKT1QoVRPZo4j>S;O2Jn;h8#doO-yT14 zQ(R8umRYL4$opK@6nEhjp7F2QYvPZu`&zmnQb6KY29IlYcDC`B?c423-rdI4XU=@ddnJ6IS&aR&{pVMHdHHD3)32vkk}vn|o4zi0 z^K`k!*UtS+&8r{t9G@|*ZR-*h^IMkFc6gZ&Fczw#{5oB`VuW9ESy{PIO*)9qH~5Z%z1nVcI*=G zvU|?)oCbjiSP#`Zj7k9llRiov0#>o)Y+DQ8`G438&}QV!d%lXtvA##JBvnPTRXCE2Lb0aLh5w>cG6aEPkPX)K)FFmyO)E zPEoP=dFf9he~lBTBO2`Njf_6Mxp8Ag!ggy8H6uY+!R=>6Yz%C_mdv(%o$eAEvd@j9 z{dnLn6V^WAFD-NITuvNRar(B&; zv)YWvQ!m?oGCbT8^|pFfM(i?g0fv(^)_H8!y>~~v$Y09gVwQsTH0ESk)@7X9H?Hny zyjLNpa(sSojN@O%%Q9+AYQJB+c)`5myUM*vDJicwwu~DsAuH@o8%(=h!LxW}`m~=$ z1>*mi?AEa^SiMX7RQ<{0XXHPomzOpCUUO)UMp*L9MG6Lu2j7-e|I=JB!R(6mF*Sx- znGRv$$+|Zg)-4he(EZDB?O^p>buIaCmiN9GCCd?auC_RHM7&fIhSo|k0Y-5_>P?yyC}!$}hE z_Zru(T9;d4{cC!IrIWUpQDM~j>&GANWqkbjv0T}o)!cu>r8p%-^Exj-U8Z(?&dk$J z4sJYbhmXE{_fG0eZ}^HyoIUsdduLQ0x9)EHk~(ecSI43=bCxkP9h`e4$1MN2fbqgZ zN{rqHrUIWGBg^f3x<#S<gTe~DWM4fdI^0U literal 0 HcmV?d00001 diff --git a/sources/awesomewidgets/weather/03d.png b/sources/awesomewidgets/weather/03d.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4a8e9fa75b5b860ff33f84cd6bf65a9ad04e54 GIT binary patch literal 2565 zcmeAS@N?(olHy`uVBq!ia0y~yU@!t<4mJh`208nVjSLJ7oCO|{#S9GG!XV7ZFl&wk z0|Tp1W=KRygs+cPa(=E}VoH8es$NBI0RsrwR9IEy7UZUuBq~(o=HwMyRoE(lRaoT} zTY-f2l@!2AO0sR0B76fBob!uP6-@OE^^BD4xD*r=Y>HCStb$zJpxTR4(rlG7N=gc> z^!3Zj%k|2Q_413-^$jg8E%gnI^o@*ki&D~bi!1X=5-W7`ij^UTz|3(;Elw`VEGWs$ z&r<-Io0ybeT4JlD1hPm02B7ZDEr45;R}A%4a(=FUMPh-zp`L+0++>j96}bggzWFJc zX_YRCC8>5s28Kqu2Ijg(<{^g0RtDx)W|lVkXlf94+JI!7i&7Iy@{2<9^KQ;1$<6;RDK`XFB*`3f9lAP1p|6Y_0IW*Q<~QM?P1!SpXk)XKjoGc~Uy z5fqwshBo>bVo*ndoCB7N3`#A|&nX3~H#dT6Ko&&T;GCaZP?TC+oSC0zX9`u1EQYQg zVSZ>{W(nL)Xu{~aA`tR+aAzS4LN!n=uv05CaRfF<1~afhqE_HE5#f@Vo06|3e|=rjIIN0RU|H}kR+fQsTMFP$?z;!P+F7& z&eYQcg@^{ZxY=>p=!1(vP?2cIb-?|?F9rsdcuyC{kPOzhVZIq5 z;UfFq=chgLNxOF@#i>nV*`x_BM_jXmG!D8xRw`Az%UZayTWQM$*YHIwE*yEH7h^R& z7Oqp7-Ozo>EAy?B$R$2Eb=9LsQWl;~ne#pE-u~w{dp*S-SLeSwb6DtJ>&)_<_V;b? z|2${;y{PZ;gufSl{B>ZmbaDBY@8#Cz($(^$QDci!k&bcB5{}KUoo?;O7ZKe3Kq0TR z^lNa%=D8bnt8bNch;(=EmuxGZE%3R2;zYr<-044@3g+5K&A0jaJVWVNyqL?wB`ThU zr?+)JzR~iWHEGter#H^kCRvJmA6|ODR`+EAqw&P4Q>VV1A+mq&xi4ZD+4>&eo5j?h zDSaYhnrlU$`l?lV=3AeIA3l88zBFU?^D;Fh0WbN^JwLkVO!Yp{(6Q)&kfYHGgMT>z z#|?PO_bfE~XRQ1A$b$zDlKr!-W~7{*nE!N6jMHJoB^?THgdC$Mu>32KQR;c}X=lvd zxc}=mMOdoKiP^Jl^>PSmSo-?$ZN_j%Wr zUkDLhHDTTB^`*Z5xjAZ-x{_pyIyAbHBDa;MW@o>?_T)Q+wFX z7{8-Q8`odn%YJUTv83SPWmAQ!`Hx;$?AkEN>C=shMs=nyroQU6wLg>BpIBXNP;+%| z$`9@(CoWwIGP^nXkK?&i|F2(EVnj3Iwp6RBwuJIN;yP{OV6kLj=K0rKZ;6C#fBrV< z(JzOt6PZ7Q_UYGiy}IbqD(YtzLmaT&YH zJ}=F!w>LHWw(IJ@y~+DrR$aYi{&7bMufBN>n@Z;PjsWKg9C3>#H0LP>NNWkS7;^02 zwJ0QO>)V^1ot^F_wX4^^Uo3df*WG|;!<9#gb)5HJ3G6rQdma;WMw};JrDeL#wh%4T zB87=N0wZI3svrJ%#-Z{wnB_qCd4m#j$Gr^Ja~D5sJIe6eYD%`I*#Df}{~vfB*!SD6 zCSK`k)>pZfAltcq%Qu7^QhiqSzP4d*-({so`}j7jVpM$NJ?X-iL*G=5OiV)J;^Ou( zZ<_C_P{Fcm-Y2HF$7F;K&1cZhJZy43^lId%Ltj;yX20FKbt|_+0#C#84~iappRwNQ zwLIf~Mx5hywz0AC>$n;FLc8M?9|Xui;o6ajvIhLxt`c+lfkvRnu9mgIbPeC%)?x;k>+C=f*@OojC4d|0NzX u?xh~=6G{9SHKSU|_0ShR?r-}7pYTdf`2I@YXM!52q2}r8=d#Wzp$Pz9Ce(TW literal 0 HcmV?d00001 diff --git a/sources/awesomewidgets/weather/04d.png b/sources/awesomewidgets/weather/04d.png new file mode 100644 index 0000000000000000000000000000000000000000..4677b8596acb4bed4fc8c62633e673567e8af6db GIT binary patch literal 2773 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 U#Z#X5_ken%p00i_>zopr0I^Y5cmMzZ literal 0 HcmV?d00001 diff --git a/sources/awesomewidgets/weather/10d.png b/sources/awesomewidgets/weather/10d.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e1f8305ef7558bad0ab12e5298db59229d69a3 GIT binary patch literal 3793 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 a{AV->naA+G(0>uAlj-T|=d#Wzp$P!AUNc$% literal 0 HcmV?d00001 diff --git a/sources/awesomewidgets/weather/13d.png b/sources/awesomewidgets/weather/13d.png new file mode 100644 index 0000000000000000000000000000000000000000..7b64a6cfd080465634c92f56e1eba8a5a3166c4f GIT binary patch literal 3901 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 + +#include "awdebug.h" + + +YahooWeatherProvider::YahooWeatherProvider(QObject *parent, const int number) + : AbstractWeatherProvider(parent, number) +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; +} + + +YahooWeatherProvider::~YahooWeatherProvider() +{ + qCDebug(LOG_LIB) << __PRETTY_FUNCTION__; +} + + +void YahooWeatherProvider::initUrl(const QString city, const QString country, + const int ts) +{ + qCDebug(LOG_LIB) << "Init query for" << city << country << "with ts" << ts; + + m_ts = ts; + + m_url = QUrl(YAHOO_WEATHER_URL); + QUrlQuery params; + params.addQueryItem(QString("format"), QString("json")); + params.addQueryItem(QString("env"), + QString("store://datatables.org/alltableswithkeys")); + params.addQueryItem(QString("q"), + QString(YAHOO_WEATHER_QUERY).arg(city, country)); + m_url.setQuery(params); +} + + +QVariantHash YahooWeatherProvider::parse(const QVariantMap &json) const +{ + qCDebug(LOG_LIB) << "Parse json" << json; + + QVariantMap jsonMap = json[QString("query")].toMap(); + if (jsonMap[QString("count")].toInt() != 1) { + qCWarning(LOG_LIB) << "Found data count" + << json[QString("count")].toInt() << "is not 1"; + return QVariantHash(); + } + QVariantMap results + = jsonMap[QString("results")].toMap()[QString("channel")].toMap(); + QVariantMap item = results[QString("item")].toMap(); + QVariantMap atmosphere = results[QString("atmosphere")].toMap(); + + return m_ts == 0 ? parseCurrent(item, atmosphere) : parseForecast(item); +} + + +QUrl YahooWeatherProvider::url() const +{ + return m_url; +} + + +QVariantHash +YahooWeatherProvider::parseCurrent(const QVariantMap &json, + const QVariantMap &atmosphere) const +{ + qCDebug(LOG_LIB) << "Parse current weather from" << json; + + QVariantHash values; + int id = json[QString("condition")].toMap()[QString("code")].toInt(); + values[QString("weatherId%1").arg(number())] = id; + values[QString("temperature%1").arg(number())] + = json[QString("condition")].toMap()[QString("temp")].toInt(); + values[QString("timestamp%1").arg(number())] + = json[QString("condition")].toMap()[QString("date")].toString(); + values[QString("humidity%1").arg(number())] + = atmosphere[QString("humidity")].toInt(); + values[QString("pressure%1").arg(number())] + = static_cast(atmosphere[QString("pressure")].toFloat()); + + return values; +} + + +QVariantHash YahooWeatherProvider::parseForecast(const QVariantMap &json) const +{ + qCDebug(LOG_LIB) << "Parse forecast from" << json; + + QVariantHash values; + QVariantList weatherList = json[QString("forecast")].toList(); + QVariantMap weatherMap = weatherList.count() < m_ts + ? weatherList.last().toMap() + : weatherList.at(m_ts).toMap(); + int id = weatherMap[QString("code")].toInt(); + values[QString("weatherId%1").arg(number())] = id; + values[QString("timestamp%1").arg(number())] + = weatherMap[QString("date")].toString(); + // yahoo provides high and low temperatures. Lets calculate average one + values[QString("temperature%1").arg(number())] + = (weatherMap[QString("high")].toFloat() + + weatherMap[QString("low")].toFloat()) + / 2.0; + // ... and no forecast data for humidity and pressure + values[QString("humidity%1").arg(number())] = 0; + values[QString("pressure%1").arg(number())] = 0.0; + + return values; +} diff --git a/sources/awesomewidgets/yahooweatherprovider.h b/sources/awesomewidgets/yahooweatherprovider.h new file mode 100644 index 0000000..d5c997f --- /dev/null +++ b/sources/awesomewidgets/yahooweatherprovider.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * This file is part of awesome-widgets * + * * + * awesome-widgets is free software: you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * awesome-widgets 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with awesome-widgets. If not, see http://www.gnu.org/licenses/ * + ***************************************************************************/ + +#ifndef YAHOOWEATHERPROVIDER_H +#define YAHOOWEATHERPROVIDER_H + +#include "abstractweatherprovider.h" + +#define YAHOO_WEATHER_URL "https://query.yahooapis.com/v1/public/yql" +#define YAHOO_WEATHER_QUERY \ + "select * from weather.forecast where u='c' and woeid in (select woeid " \ + "from geo.places(1) where text='%1, %2')" + + +class YahooWeatherProvider : public AbstractWeatherProvider +{ +public: + explicit YahooWeatherProvider(QObject *parent, const int number); + virtual ~YahooWeatherProvider(); + void initUrl(const QString city, const QString country, const int); + QVariantHash parse(const QVariantMap &json) const; + QUrl url() const; + +private: + QVariantHash parseCurrent(const QVariantMap &json, + const QVariantMap &atmosphere) const; + QVariantHash parseForecast(const QVariantMap &json) const; + int m_ts = 0; + QUrl m_url; +}; + + +#endif /* YAHOOWEATHERPROVIDER_H */ diff --git a/sources/test/testextweather.cpp b/sources/test/testextweather.cpp index bf41746..d1d38d9 100644 --- a/sources/test/testextweather.cpp +++ b/sources/test/testextweather.cpp @@ -30,6 +30,7 @@ void TestExtWeather::initTestCase() extWeather->setCity(city); extWeather->setCountry(country); extWeather->setNumber(0); + extWeather->setProvider(ExtWeather::Provider::OWM); extWeather->run(); } @@ -47,62 +48,28 @@ void TestExtWeather::test_values() QCOMPARE(extWeather->number(), 0); QCOMPARE(extWeather->city(), city); QCOMPARE(extWeather->country(), country); + QCOMPARE(extWeather->provider(), ExtWeather::Provider::OWM); } -void TestExtWeather::test_run() +void TestExtWeather::test_runOWM() { - // init spy - QSignalSpy spy(extWeather, SIGNAL(dataReceived(const QVariantHash &))); - QVariantHash firstValue = extWeather->run(); + run(); +} - // check values - QVERIFY(spy.wait(5000)); - QVariantHash arguments = spy.takeFirst().at(0).toHash(); - QEXPECT_FAIL("", "WeatherID should not be 0", Continue); - QCOMPARE(arguments[extWeather->tag(QString("weatherId"))].toInt(), 0); - QVERIFY((arguments[extWeather->tag(QString("humidity"))].toInt() - > humidity.first) - && (arguments[extWeather->tag(QString("humidity"))].toInt() - < humidity.second)); - QEXPECT_FAIL("", "https://yahoo.uservoice.com/forums/207813-us-weather/" - "suggestions/14209233-invalid-pressure-calculation", - Continue); - QVERIFY((arguments[extWeather->tag(QString("pressure"))].toFloat() - > pressure.first) - && (arguments[extWeather->tag(QString("pressure"))].toInt() - < pressure.second)); - QVERIFY((arguments[extWeather->tag(QString("temperature"))].toFloat() - > temp.first) - && (arguments[extWeather->tag(QString("temperature"))].toInt() - < temp.second)); - // image should be only one symbol here - QCOMPARE(arguments[extWeather->tag(QString("weather"))].toString().count(), - 1); + +void TestExtWeather::test_runYahoo() +{ + extWeather->setProvider(ExtWeather::Provider::Yahoo); + run(); + extWeather->setProvider(ExtWeather::Provider::OWM); } void TestExtWeather::test_ts() { extWeather->setTs(1); - // init spy - QSignalSpy spy(extWeather, SIGNAL(dataReceived(const QVariantHash &))); - QVariantHash firstValue = extWeather->run(); - - // check values - QVERIFY(spy.wait(5000)); - QVariantHash arguments = spy.takeFirst().at(0).toHash(); - QEXPECT_FAIL("", "WeatherID should not be 0", Continue); - QCOMPARE(arguments[extWeather->tag(QString("weatherId"))].toInt(), 0); - QCOMPARE(arguments[extWeather->tag(QString("humidity"))].toInt(), 0); - QCOMPARE(arguments[extWeather->tag(QString("pressure"))].toFloat(), 0.0f); - QVERIFY((arguments[extWeather->tag(QString("temperature"))].toFloat() - > temp.first) - && (arguments[extWeather->tag(QString("temperature"))].toInt() - < temp.second)); - // image should be only one symbol here - QCOMPARE(arguments[extWeather->tag(QString("weather"))].toString().count(), - 1); + run(); } @@ -131,10 +98,43 @@ void TestExtWeather::test_copy() QCOMPARE(newExtWeather->country(), extWeather->country()); QCOMPARE(newExtWeather->ts(), extWeather->ts()); QCOMPARE(newExtWeather->image(), extWeather->image()); + QCOMPARE(newExtWeather->provider(), extWeather->provider()); QCOMPARE(newExtWeather->number(), 1); delete newExtWeather; } +void TestExtWeather::run() +{ + // init spy + QSignalSpy spy(extWeather, SIGNAL(dataReceived(const QVariantHash &))); + QVariantHash firstValue = extWeather->run(); + + // check values + QVERIFY(spy.wait(5000)); + QVariantHash arguments = spy.takeFirst().at(0).toHash(); + QEXPECT_FAIL("", "WeatherID should not be 0", Continue); + QCOMPARE(arguments[extWeather->tag(QString("weatherId"))].toInt(), 0); + QVERIFY((arguments[extWeather->tag(QString("humidity"))].toInt() + >= humidity.first) + && (arguments[extWeather->tag(QString("humidity"))].toInt() + <= humidity.second)); + QWARN("May fail here for Yahoo! Weather, see " + "https://yahoo.uservoice.com/forums/207813-us-weather/suggestions/" + "14209233-invalid-pressure-calculation"); + QVERIFY((arguments[extWeather->tag(QString("pressure"))].toFloat() + > pressure.first) + && (arguments[extWeather->tag(QString("pressure"))].toInt() + < pressure.second)); + QVERIFY((arguments[extWeather->tag(QString("temperature"))].toFloat() + > temp.first) + && (arguments[extWeather->tag(QString("temperature"))].toInt() + < temp.second)); + // image should be only one symbol here + QCOMPARE(arguments[extWeather->tag(QString("weather"))].toString().count(), + 1); +} + + QTEST_MAIN(TestExtWeather); diff --git a/sources/test/testextweather.h b/sources/test/testextweather.h index 8e28e05..e06cae2 100644 --- a/sources/test/testextweather.h +++ b/sources/test/testextweather.h @@ -34,12 +34,14 @@ private slots: void cleanupTestCase(); // test void test_values(); - void test_run(); + void test_runOWM(); + void test_runYahoo(); void test_ts(); void test_image(); void test_copy(); private: + void run(); ExtWeather *extWeather = nullptr; QString city = QString("London"); QString country = QString("uk"); diff --git a/sources/version.h.in b/sources/version.h.in index 5803bec..4c89f7b 100644 --- a/sources/version.h.in +++ b/sources/version.h.in @@ -19,7 +19,8 @@ "qnetworkreply-network-reply-timeout-helper" #define SPECIAL_THANKS \ "Yahoo! Finance,https://finance.yahoo.com/;Yahoo! " \ - "Weather,https://weather.yahoo.com/;JetBrains,https://www.jetbrains.com/" + "Weather,https://weather.yahoo.com/;JetBrains,https://www.jetbrains.com/" \ + ";OpenWeatherMap,http://openweathermap.org/" #define CHANGELOG "@PROJECT_CHANGELOG@" // configuraion @@ -32,7 +33,7 @@ // extupgrade api version #define AWEUAPI 3 // extweather api version -#define AWEWAPI 2 +#define AWEWAPI 3 // formatter api version #define AWEFAPI 1 // network requests timeout, ms