summaryrefslogtreecommitdiffstats
path: root/weather/networkforecastsource.cpp
blob: 98456c8a28f9e6dae2e432ac6028e8eb4ab27868 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include "networkforecastsource.h"
#include "yahooweatherresponse.h"
#include "xoapweatherresponse.h"

#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>
#include <QBuffer>
#include <QDomDocument>
#include <QDateTime>
#include <QTime>

#define CITY_NAME_HOST                  "xoap.weather.com"
#define CITY_NAME_TO_LOCATION_ID_URL    "http://" CITY_NAME_HOST "/search/search"
#define CITY_NAME_TO_LOCATION_ID_PARAM  "where"

#define FORECAST_HOST                   "weather.yahooapis.com"
#define FORECAST_URL                    "http://" FORECAST_HOST "/forecastrss"
#define FORECAST_LOCATION_PARAM         "p"
#define FORECAST_UNIT_PARAM             "u"
#define FORECAST_CELSIUS_UNIT           "c"

#define YWEATHER_NS_URI                 "http://xml.weather.yahoo.com/ns/rss/1.0"
#define YWEATHER_NS_NAME                "yweather"

static int getId()
{
    static int id(0);
    return ++id;
}

// LocationRequestManager

LocationRequestManager::LocationRequestManager(QObject *parent)
    : QObject(parent)
{
    connect(&m_network, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(receiveResponse(QNetworkReply*)));
}

int LocationRequestManager::addRequest(const QString &query)
{
    if (m_requests.contains(query))
        return m_requests[query];
    QUrl url(CITY_NAME_TO_LOCATION_ID_URL);
    url.addQueryItem(CITY_NAME_TO_LOCATION_ID_PARAM, query);
    m_network.get(QNetworkRequest(url));

    int result = getId();
    m_requests[query] = result;
    return result;
}

QString LocationRequestManager::readResponse(const QString &query, QNetworkReply *reply)
{

    if (reply->error() != QNetworkReply::NoError) {
        qWarning() << "location id query error: " << query;
        return QString();
    }

     QDomDocument doc;

    if (!doc.setContent(reply)) {
        qWarning() << "location id parse error: " << query;
        return QString();
    }
    XoapWeatherResponse reader;
    reader.read(doc.documentElement());

    if (reader.items().count() == 0) {
        qWarning() << "empty location id response: " << query;
        return QString();
    }

    return reader.items()[0].id();

}

void LocationRequestManager::receiveResponse(QNetworkReply *reply)
{
    const QString query = reply->request().url().queryItemValue(CITY_NAME_TO_LOCATION_ID_PARAM);

    if (query.isNull() || !m_requests.contains(query)) {
        qWarning() << "Unexpected location id query response. (query = " << query << ")";
        return;
    }
    const int reqId = m_requests[query];
    m_requests.remove(query);

    QString locId = readResponse(query, reply);

    if (locId.isEmpty())
        emit locationIdQueryError(reqId, query);
    else
        emit newLocationId(reqId, locId);
}

// ForecastRequestmanager

ForecastRequestmanager::ForecastRequestmanager(QObject *parent)
    : QObject(parent)
{
    connect(&m_network, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(receiveResponse(QNetworkReply*)));
}

int ForecastRequestmanager::addRequest(const QString &locId)
{
    if (m_requests.contains(locId))
        return m_requests[locId][0];
    int result = getId();
    doAddRequest(result, locId);
    return result;
}

void ForecastRequestmanager::addRequest(int reqId, const QString &locId)
{
    if (m_requests.contains(locId)) {
        m_requests[locId].append(reqId);
        return;
    }
    doAddRequest(reqId, locId);
}

void ForecastRequestmanager::doAddRequest(int reqId, const QString &locId)
{
    QUrl url(FORECAST_URL);
    url.addQueryItem(FORECAST_LOCATION_PARAM, locId);
    url.addQueryItem(FORECAST_UNIT_PARAM, FORECAST_CELSIUS_UNIT);
    m_network.get(QNetworkRequest (url));
    QList<int> list;
    list.append(reqId);
    m_requests[locId] = list;
}

YahooWeatherResponse *ForecastRequestmanager::readResponse(const QString &locId,
                                                           QNetworkReply *reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        qWarning() << "forecast query error: " << locId;
        return 0;
    }

     QDomDocument doc;

    if (!doc.setContent(reply)) {
        qWarning() << "forecast parse error: " << locId;
        return 0;
    }

    bool ok = doc.documentElement().childNodes().count() > 0
            && doc.documentElement().childNodes().at(0).isElement();
    if (ok) {
        QDomElement element = doc.documentElement().childNodes().at(0).toElement();
        ok = element.elementsByTagName("lastBuildDate").count() == 1;
    }

    if (!ok) {
        qWarning() << "invalid forecast response: " << locId;
        return 0;
    }

    return new YahooWeatherResponse(locId, doc.documentElement());
}

void ForecastRequestmanager::receiveResponse(QNetworkReply *reply)
{
    const QString locId = reply->request().url().queryItemValue(FORECAST_LOCATION_PARAM);

    if (locId.isNull() || !m_requests.contains(locId)) {
        qWarning() << "Unexpected forecast query response. (locId = " << locId << ")";
        return;
    }

    const QList<int> list = m_requests[locId];
    m_requests.remove(locId);

    YahooWeatherResponse *result = readResponse(locId, reply);
    if (result)
        foreach (int reqId, list) {
            emit newForecastResponse(reqId, result);
        }
    else
        foreach (int reqId, list)
            emit forecastResponseError(reqId);
}

// NetworkForecastSource

NetworkForecastSource::NetworkForecastSource(QObject *parent)
    : ForecastSource(parent)
{
    connect(&m_locationManager, SIGNAL(newLocationId(int, QString)),
            &m_forecastManager, SLOT(addRequest(int, QString)));

    connect(&m_locationManager, SIGNAL(locationIdQueryError(int, QString)),
            &m_forecastManager, SLOT(addRequest(int, QString)));

    connect(&m_forecastManager, SIGNAL(forecastResponseError(int)),
            this, SLOT(forecastResponseError(int)));

    connect(&m_forecastManager, SIGNAL(newForecastResponse(int, YahooWeatherResponse*)),
            this, SLOT(newForecastResponse(int, YahooWeatherResponse*)));
}

NetworkForecastSource::~NetworkForecastSource()
{
}

int NetworkForecastSource::getForecast(const QString &key, bool locationId)
{
    if (locationId)
        return m_forecastManager.addRequest(key);
    else
        return m_locationManager.addRequest(key);
}

void NetworkForecastSource::newForecastResponse(int reqId, YahooWeatherResponse *forecast)
{
    forecast->print();
    emit forecastReceived(reqId, ForecastData(forecast));
}

void NetworkForecastSource::forecastResponseError(int reqId)
{
    emit forecastReceived(reqId, ForecastData(0));
}