Removed prefix get from all methods.
[medard] / src / medarddownloader.cpp
1 /*
2  *  Medard for Maemo.
3  *  Copyright (C) 2011 Roman Moravcik
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <QDebug>
21 #include <QTimer>
22 #include <QImage>
23 #include <QDir>
24 #include <QFile>
25
26 #include "medarddownloader.h"
27
28 #define DOWNLOAD_CACHE_DIR ".cache/medard-downloader"
29
30 #define MEDARD_URL "http://www.medard-online.cz"
31 #define MEDARD_IMAGE_URL "http://www.medard-online.cz/scripts/getimage.php?initDate=%1&domain=%2&variable=%3&offset=%4"
32
33 #define FC_SEA_LEVEL_PRESSURE "slp"
34 #define FC_PRECIPITATION "precip"
35 #define FC_WIND_VELOCITY "wv"
36 #define FC_CLOUDINESS "cloud"
37 #define FC_TEMPERATURE "temp"
38
39 #define FD_EUROPE "1"
40 #define FD_CZECH_REPUBLIC "2"
41
42 #define MIN_OFFSET 0
43 #define MAX_OFFSET 72
44
45 #define IMAGE_WIDTH 556
46 #define IMAGE_HEIGHT 408
47
48 #define MAX_DOWNLOAD_RETRIES 3
49
50 MedardDownloader::MedardDownloader() : QObject()
51 {
52     m_forecastType = FC_SEA_LEVEL_PRESSURE;
53     m_forecastDomain = FD_EUROPE;
54     m_forecastInitialDateCode.clear();
55     m_forecastDateOffset = 0;
56
57     m_network = new QNetworkAccessManager();
58     m_reply = 0;
59
60     m_retryCounter = 0;
61     m_retryTimer = new QTimer();
62     m_retryTimer->setInterval(5000);
63     connect(m_retryTimer, SIGNAL(timeout()), this, SLOT(retryTimerEvent()));
64
65     m_cachePath = QString("%1/%2")
66                   .arg(QDir().homePath())
67                   .arg(DOWNLOAD_CACHE_DIR);
68
69     createCacheDirectory();
70 }
71
72 MedardDownloader::~MedardDownloader()
73 {
74     if (m_retryTimer->isActive())
75         m_retryTimer->stop();
76
77     if (m_reply) {
78         m_reply->abort();
79         delete m_reply;
80     }
81 }
82
83 QSize MedardDownloader::imageSize()
84 {
85     return QSize(IMAGE_WIDTH, IMAGE_HEIGHT);
86 }
87
88 void MedardDownloader::setForecastType(ForecastType type)
89 {
90     switch (type) {
91         case SeaLevelPressure:
92             m_forecastType = FC_SEA_LEVEL_PRESSURE;
93             break;
94
95         case Precipitation:
96             m_forecastType = FC_PRECIPITATION;
97             break;
98
99         case WindVelocity:
100             m_forecastType = FC_WIND_VELOCITY;
101             break;
102
103         case Cloudiness:
104             m_forecastType = FC_CLOUDINESS;
105             break;
106
107         case Temperature:
108             m_forecastType = FC_TEMPERATURE;
109             break;
110     }
111 }
112
113 void MedardDownloader::setForecastDomain(ForecastDomain domain)
114 {
115     switch (domain) {
116         case Europe:
117             m_forecastDomain = FD_EUROPE;
118             break;
119
120         case CzechRepublic:
121             m_forecastDomain = FD_CZECH_REPUBLIC;
122             break;
123     }
124 }
125
126 void MedardDownloader::setForecastInitialDate(QDateTime date)
127 {
128     QString offset;
129
130     m_forecastInitialDate = date.toUTC();
131
132     if (date.toUTC().time().hour() >= 18) {
133         m_forecastInitialDate.setTime(QTime(18, 0, 0));
134         offset = "12";
135     } else if (date.toUTC().time().hour() >= 12) {
136         m_forecastInitialDate.setTime(QTime(12, 0, 0));
137         offset = "06";
138     } else if (date.toUTC().time().hour() >= 6) {
139         m_forecastInitialDate.setTime(QTime(6, 0, 0));
140         offset = "00";
141     } else {
142         m_forecastInitialDate.setTime(QTime(0, 0, 0));
143         offset = "18";
144     }
145
146     if (offset == "18") {
147         /* use previous day */
148         m_forecastInitialDateCode = QString("%1_%2")
149                                     .arg(date.addDays(-1).toUTC().toString("yyMMdd"))
150                                     .arg(offset);
151     } else {
152         /* use current day */
153         m_forecastInitialDateCode = QString("%1_%2")
154                                     .arg(date.toUTC().toString("yyMMdd"))
155                                     .arg(offset);
156     }
157
158     cleanCacheDirectory();
159 }
160
161 QDateTime MedardDownloader::forecastInitialDate()
162 {
163     return m_forecastInitialDate.toLocalTime();
164 }
165
166 QDateTime MedardDownloader::forecastDate()
167 {
168     return m_forecastInitialDate.addSecs(3600 * m_forecastDateOffset).toLocalTime();
169 }
170
171 void MedardDownloader::setForecastDateOffset(int offset)
172 {
173     m_forecastDateOffset = offset;
174     if (m_forecastDateOffset > MAX_OFFSET)
175         m_forecastDateOffset = MAX_OFFSET;
176
177     if (m_forecastDateOffset < MIN_OFFSET)
178         m_forecastDateOffset = MIN_OFFSET;
179 }
180
181 void MedardDownloader::retryTimerEvent()
182 {
183     if (m_retryTimer->isActive())
184         m_retryTimer->stop();
185
186     downloadImage();
187 }
188
189 void MedardDownloader::tryDownloadImageAgain()
190 {
191     m_retryCounter++;
192
193     if (m_retryCounter < MAX_DOWNLOAD_RETRIES) {
194         m_retryTimer->start();
195     } else {
196         m_retryCounter = 0;
197         emit downloadFailed();
198     }
199 }
200
201 void MedardDownloader::clearDownloadRequest()
202 {
203     qDebug() << "clearDownloadRequest: m_reply=" << m_reply;
204
205     delete m_reply;
206     m_reply = 0;
207 }
208
209 void MedardDownloader::downloadImageFinished()
210
211     qDebug() << "downloadImageFinished: m_reply=" << m_reply;
212
213     QByteArray picture = m_reply->readAll();
214
215     if (picture.isNull() || picture.size() <= 0)
216         return;
217
218     m_retryCounter = 0;
219
220     QImage image;
221     if (!image.loadFromData(picture, "png"))
222         return;
223
224     QString filename = QString("%1/%2_%3_%4_%5.png")
225                        .arg(m_cachePath)
226                        .arg(m_forecastType)
227                        .arg(m_forecastDomain)
228                        .arg(m_forecastInitialDateCode)
229                        .arg(QString().number(m_forecastDateOffset));
230
231     if ((image.width() == 512) && (image.height() == 512)) {
232         QImage croped(512, 400, QImage::Format_ARGB32_Premultiplied);
233         croped = image.copy(0, 52, 512, IMAGE_HEIGHT);
234         croped.save(filename, "png");
235     } else {
236         QImage croped(560, 400, QImage::Format_ARGB32_Premultiplied);
237         croped = image.copy(10, 96, IMAGE_WIDTH, IMAGE_HEIGHT);
238         croped.save(filename, "png");
239     }
240
241     qDebug() << "downloadImageFinished: downloadFinished=" << filename;
242     emit downloadFinished(filename, forecastDate());
243
244     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
245 }
246
247 void MedardDownloader::downloadImageError(QNetworkReply::NetworkError /* code */)
248 {
249     tryDownloadImageAgain();
250     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
251 }
252
253 void MedardDownloader::downloadImage()
254 {
255     qDebug() << "downloadImage: retry=" << m_retryCounter << "date:" << m_forecastInitialDate.toString("dd.MM.yyyy hh:mm");
256
257     if (m_forecastInitialDateCode.isNull()) {
258         retrieveForecastInitialDate();
259         tryDownloadImageAgain();
260         return;
261     }
262
263     QString filename = QString("%1/%2_%3_%4_%5.png")
264                        .arg(m_cachePath)
265                        .arg(m_forecastType)
266                        .arg(m_forecastDomain)
267                        .arg(m_forecastInitialDateCode)
268                        .arg(QString().number(m_forecastDateOffset));
269
270     if (isDownloaded(filename)) {
271         qDebug() << "downloadImage: downloadFinished=" << filename;
272
273         emit downloadFinished(filename, forecastDate());
274         return;
275     }
276
277     QString imageUrl = QString(MEDARD_IMAGE_URL)
278                                .arg(m_forecastInitialDateCode)
279                                .arg(m_forecastDomain)
280                                .arg(m_forecastType)
281                                .arg(QString().number(m_forecastDateOffset));
282
283     QUrl url(imageUrl);
284     QNetworkRequest request(url);
285
286     if (m_reply)
287         clearDownloadRequest();
288     m_reply = m_network->get(request);
289
290     connect(m_reply, SIGNAL(finished()), this, SLOT(downloadImageFinished()));
291     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
292             SLOT(downloadImageError(QNetworkReply::NetworkError)));
293 }
294
295 bool MedardDownloader::isDownloaded(const QString filename)
296 {
297     return QFile(filename).exists();
298 }
299
300 int MedardDownloader::forecastDateOffset()
301 {
302     return m_forecastDateOffset;
303 }
304
305 int MedardDownloader::minForecastDateOffset()
306 {
307     return MIN_OFFSET;
308 }
309
310 int MedardDownloader::maxForecastDateOffset()
311 {
312     return MAX_OFFSET;
313 }
314
315 void MedardDownloader::retrieveForecastInitialDateFinished()
316 {
317     qDebug() << "retrieveForecastInitialDateFinished: m_reply=" << m_reply;
318
319     QByteArray data = m_reply->readAll();
320
321     int index1 = data.indexOf("var fcst_initDatestamp=\"", 0);
322     int index2 = data.indexOf("\";", index1);
323     if (index1 != -1) {
324         QString temp;
325         for (int i = index1 + 24; i < index2; i++) {
326             temp.append(data.at(i));
327         }
328         QDateTime date = QDateTime::fromTime_t(temp.toULong() + 6 * 3600);
329         if (!date.isNull()) {
330             setForecastInitialDate(date.toLocalTime());
331
332             int forecastDateOffset = date.toLocalTime().secsTo(QDateTime().currentDateTime()) / 3600;
333             setForecastDateOffset(forecastDateOffset);
334         }
335         m_retryCounter = 0;
336     }
337
338     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
339 }
340
341 void MedardDownloader::retrieveForecastInitialDateError(QNetworkReply::NetworkError /* code */)
342 {
343     qDebug() << "retrieveForecastInitialDateError: m_reply=" << m_reply;
344 }
345
346 void MedardDownloader::retrieveForecastInitialDate()
347 {
348     qDebug() << "retrieveForecastInitialDate: m_reply=" << m_reply;
349
350     QString serverUrl = QString(MEDARD_URL);
351
352     QUrl url(serverUrl);
353     QNetworkRequest request(url);
354
355     if (m_reply)
356         clearDownloadRequest();
357     m_reply = m_network->get(request);
358
359     connect(m_reply, SIGNAL(finished()), this, SLOT(retrieveForecastInitialDateFinished()));
360     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
361             SLOT(retrieveForecastInitialDateError(QNetworkReply::NetworkError)));
362 }
363
364 void MedardDownloader::createCacheDirectory()
365 {
366     QDir cacheDir(m_cachePath);
367     if (!cacheDir.exists())
368         cacheDir.mkpath(cacheDir.path());
369 }
370
371 void MedardDownloader::cleanCacheDirectory()
372 {
373     QDir cacheDir(m_cachePath);
374     QStringList list = cacheDir.entryList();
375     for (int i = 0; i < list.size(); i++) {
376         if (!list.at(i).contains(m_forecastInitialDateCode)) {
377             QFile(m_cachePath + "/" + list.at(i)).remove();
378         }
379     }
380 }