Removed debug messages.
[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 <QTimer>
21 #include <QImage>
22 #include <QDir>
23 #include <QFile>
24
25 #include "medarddownloader.h"
26
27 #define DOWNLOAD_CACHE_DIR ".cache/medard-downloader"
28
29 #define MEDARD_URL "http://www.medard-online.cz"
30 #define MEDARD_IMAGE_URL "http://www.medard-online.cz/scripts/getimage.php?initDate=%1&domain=%2&variable=%3&offset=%4"
31
32 #define FC_SEA_LEVEL_PRESSURE "slp"
33 #define FC_PRECIPITATION "precip"
34 #define FC_WIND_VELOCITY "wv"
35 #define FC_CLOUDINESS "cloud"
36 #define FC_TEMPERATURE "temp"
37
38 #define FD_EUROPE "1"
39 #define FD_CZECH_REPUBLIC "2"
40
41 #define MIN_OFFSET 0
42 #define MAX_OFFSET 72
43
44 #define IMAGE_WIDTH 556
45 #define IMAGE_HEIGHT 408
46
47 #define MAX_DOWNLOAD_RETRIES 3
48 #define RETRY_TIMEOUT 5000
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     connect(m_retryTimer, SIGNAL(timeout()), this, SLOT(retryTimerEvent()));
63
64     m_cachePath = QString("%1/%2")
65                   .arg(QDir().homePath())
66                   .arg(DOWNLOAD_CACHE_DIR);
67
68     createCacheDirectory();
69 }
70
71 MedardDownloader::~MedardDownloader()
72 {
73     if (m_retryTimer->isActive())
74         m_retryTimer->stop();
75
76     if (m_reply) {
77         m_reply->abort();
78         delete m_reply;
79     }
80 }
81
82 QSize MedardDownloader::imageSize()
83 {
84     return QSize(IMAGE_WIDTH, IMAGE_HEIGHT);
85 }
86
87 void MedardDownloader::setForecastType(ForecastType type)
88 {
89     switch (type) {
90         case SeaLevelPressure:
91             m_forecastType = FC_SEA_LEVEL_PRESSURE;
92             break;
93
94         case Precipitation:
95             m_forecastType = FC_PRECIPITATION;
96             break;
97
98         case WindVelocity:
99             m_forecastType = FC_WIND_VELOCITY;
100             break;
101
102         case Cloudiness:
103             m_forecastType = FC_CLOUDINESS;
104             break;
105
106         case Temperature:
107             m_forecastType = FC_TEMPERATURE;
108             break;
109     }
110 }
111
112 void MedardDownloader::setForecastDomain(ForecastDomain domain)
113 {
114     switch (domain) {
115         case Europe:
116             m_forecastDomain = FD_EUROPE;
117             break;
118
119         case CzechRepublic:
120             m_forecastDomain = FD_CZECH_REPUBLIC;
121             break;
122     }
123 }
124
125 void MedardDownloader::setForecastInitialDate(QDateTime date)
126 {
127     QString offset;
128
129     m_forecastInitialDate = date.toUTC();
130
131     if (date.toUTC().time().hour() >= 18) {
132         m_forecastInitialDate.setTime(QTime(18, 0, 0));
133         offset = "12";
134     } else if (date.toUTC().time().hour() >= 12) {
135         m_forecastInitialDate.setTime(QTime(12, 0, 0));
136         offset = "06";
137     } else if (date.toUTC().time().hour() >= 6) {
138         m_forecastInitialDate.setTime(QTime(6, 0, 0));
139         offset = "00";
140     } else {
141         m_forecastInitialDate.setTime(QTime(0, 0, 0));
142         offset = "18";
143     }
144
145     if (offset == "18") {
146         /* use previous day */
147         m_forecastInitialDateCode = QString("%1_%2")
148                                     .arg(date.addDays(-1).toUTC().toString("yyMMdd"))
149                                     .arg(offset);
150     } else {
151         /* use current day */
152         m_forecastInitialDateCode = QString("%1_%2")
153                                     .arg(date.toUTC().toString("yyMMdd"))
154                                     .arg(offset);
155     }
156
157     cleanCacheDirectory();
158 }
159
160 QDateTime MedardDownloader::forecastInitialDate()
161 {
162     return m_forecastInitialDate.toLocalTime();
163 }
164
165 QDateTime MedardDownloader::forecastDate()
166 {
167     return m_forecastInitialDate.addSecs(3600 * m_forecastDateOffset).toLocalTime();
168 }
169
170 void MedardDownloader::setForecastDateOffset(int offset)
171 {
172     m_forecastDateOffset = offset;
173     if (m_forecastDateOffset > MAX_OFFSET)
174         m_forecastDateOffset = MAX_OFFSET;
175
176     if (m_forecastDateOffset < MIN_OFFSET)
177         m_forecastDateOffset = MIN_OFFSET;
178 }
179
180 void MedardDownloader::retryTimerEvent()
181 {
182     if (m_retryTimer->isActive())
183         m_retryTimer->stop();
184
185     downloadImage();
186 }
187
188 void MedardDownloader::tryDownloadImageAgain()
189 {
190     m_retryCounter++;
191
192     if (m_retryCounter < MAX_DOWNLOAD_RETRIES) {
193         m_retryTimer->setInterval(RETRY_TIMEOUT * (m_retryCounter + 1));
194         m_retryTimer->start();
195     } else {
196         m_retryCounter = 0;
197         emit downloadFailed();
198     }
199 }
200
201 void MedardDownloader::clearDownloadRequest()
202 {
203     delete m_reply;
204     m_reply = 0;
205 }
206
207 void MedardDownloader::downloadImageFinished()
208
209     QByteArray picture = m_reply->readAll();
210
211     if (picture.isNull() || picture.size() <= 0)
212         return;
213
214     m_retryCounter = 0;
215
216     QImage image;
217     if (!image.loadFromData(picture, "png"))
218         return;
219
220     QString filename = QString("%1/%2_%3_%4_%5.png")
221                        .arg(m_cachePath)
222                        .arg(m_forecastType)
223                        .arg(m_forecastDomain)
224                        .arg(m_forecastInitialDateCode)
225                        .arg(QString().number(m_forecastDateOffset));
226
227     if ((image.width() == 512) && (image.height() == 512)) {
228         QImage croped(512, 400, QImage::Format_ARGB32_Premultiplied);
229         croped = image.copy(0, 52, 512, IMAGE_HEIGHT);
230         croped.save(filename, "png");
231     } else {
232         QImage croped(560, 400, QImage::Format_ARGB32_Premultiplied);
233         croped = image.copy(10, 96, IMAGE_WIDTH, IMAGE_HEIGHT);
234         croped.save(filename, "png");
235     }
236
237     emit downloadFinished(filename, forecastDate());
238
239     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
240 }
241
242 void MedardDownloader::downloadImageError(QNetworkReply::NetworkError /* code */)
243 {
244     tryDownloadImageAgain();
245     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
246 }
247
248 void MedardDownloader::downloadImage()
249 {
250     if (m_forecastInitialDateCode.isNull()) {
251         retrieveForecastInitialDate();
252         tryDownloadImageAgain();
253         return;
254     }
255
256     QString filename = QString("%1/%2_%3_%4_%5.png")
257                        .arg(m_cachePath)
258                        .arg(m_forecastType)
259                        .arg(m_forecastDomain)
260                        .arg(m_forecastInitialDateCode)
261                        .arg(QString().number(m_forecastDateOffset));
262
263     if (isDownloaded(filename)) {
264         emit downloadFinished(filename, forecastDate());
265         return;
266     }
267
268     QString imageUrl = QString(MEDARD_IMAGE_URL)
269                                .arg(m_forecastInitialDateCode)
270                                .arg(m_forecastDomain)
271                                .arg(m_forecastType)
272                                .arg(QString().number(m_forecastDateOffset));
273
274     QUrl url(imageUrl);
275     QNetworkRequest request(url);
276
277     if (m_reply)
278         clearDownloadRequest();
279     m_reply = m_network->get(request);
280
281     connect(m_reply, SIGNAL(finished()), this, SLOT(downloadImageFinished()));
282     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
283             SLOT(downloadImageError(QNetworkReply::NetworkError)));
284 }
285
286 bool MedardDownloader::isDownloaded(const QString filename)
287 {
288     return QFile(filename).exists();
289 }
290
291 int MedardDownloader::forecastDateOffset()
292 {
293     return m_forecastDateOffset;
294 }
295
296 int MedardDownloader::minForecastDateOffset()
297 {
298     return MIN_OFFSET;
299 }
300
301 int MedardDownloader::maxForecastDateOffset()
302 {
303     return MAX_OFFSET;
304 }
305
306 void MedardDownloader::retrieveForecastInitialDateFinished()
307 {
308     QByteArray data = m_reply->readAll();
309
310     int index1 = data.indexOf("var fcst_initDatestamp=\"", 0);
311     int index2 = data.indexOf("\";", index1);
312     if (index1 != -1) {
313         QString temp;
314         for (int i = index1 + 24; i < index2; i++) {
315             temp.append(data.at(i));
316         }
317         QDateTime date = QDateTime::fromTime_t(temp.toULong() + 6 * 3600);
318         if (!date.isNull()) {
319             setForecastInitialDate(date.toLocalTime());
320
321             int forecastDateOffset = date.toLocalTime().secsTo(QDateTime().currentDateTime()) / 3600;
322             setForecastDateOffset(forecastDateOffset);
323         }
324         m_retryCounter = 0;
325     }
326
327     QTimer::singleShot(0, this, SLOT(clearDownloadRequest()));
328 }
329
330 void MedardDownloader::retrieveForecastInitialDateError(QNetworkReply::NetworkError /* code */)
331 {
332 }
333
334 void MedardDownloader::retrieveForecastInitialDate()
335 {
336     QString serverUrl = QString(MEDARD_URL);
337
338     QUrl url(serverUrl);
339     QNetworkRequest request(url);
340
341     if (m_reply)
342         clearDownloadRequest();
343     m_reply = m_network->get(request);
344
345     connect(m_reply, SIGNAL(finished()), this, SLOT(retrieveForecastInitialDateFinished()));
346     connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
347             SLOT(retrieveForecastInitialDateError(QNetworkReply::NetworkError)));
348 }
349
350 void MedardDownloader::createCacheDirectory()
351 {
352     QDir cacheDir(m_cachePath);
353     if (!cacheDir.exists())
354         cacheDir.mkpath(cacheDir.path());
355 }
356
357 void MedardDownloader::cleanCacheDirectory()
358 {
359     QDir cacheDir(m_cachePath);
360     QStringList list = cacheDir.entryList();
361     for (int i = 0; i < list.size(); i++) {
362         if (!list.at(i).contains(m_forecastInitialDateCode)) {
363             QFile(m_cachePath + "/" + list.at(i)).remove();
364         }
365     }
366 }