Merge branch 'master' into QJson_Experiments
[buliscores] / src / src / backendkicker.cpp
1 #include <QtNetwork/QNetworkAccessManager>
2 #include <QtNetwork/QNetworkRequest>
3 #include <QUrl>
4 #include <QRegExp>
5 #include <QDebug>
6 #include <QStringList>
7 #include <QDateTime>
8 #include <QSettings>
9 #include <QApplication>
10
11 #include <qjson/qobjecthelper.h>
12 #include <qjson/serializer.h>
13
14 #include "backendkicker.h"
15
16 const int BackendKicker::INTERVAL_FAST = 180;
17
18 BackendKicker::BackendKicker(QObject *parent) :
19     MatchDayBackend(parent),
20     m_updateTimer(this),
21     m_netaccmgr(new QNetworkAccessManager(this)),
22     m_initialUpdateDone(false)
23 {
24     QSettings settings(qApp->organizationName(), qApp->applicationName());
25
26     connect(m_netaccmgr, SIGNAL(finished(QNetworkReply*)),
27             this, SLOT(dlndFinished(QNetworkReply*)));
28
29     connect(&m_updateTimer, SIGNAL(timeout()),
30             this, SLOT(update()));
31
32     m_updateTimer.setSingleShot(true);
33
34     this->selectLeague(settings.value("League", "1. Bundesliga").toString());
35 }
36
37 Match* BackendKicker::getOrAddMatch(QString hometeam, QString awayteam, QDateTime date)
38 {
39     QListIterator<Match*> iter(m_matchlist);
40     Match*        match;
41
42     while (iter.hasNext()) {
43         match = iter.next();
44         if (match->awayTeam() == awayteam &&
45             match->homeTeam() == hometeam) {
46             return match;
47         }
48     }
49
50     match = new Match(hometeam, awayteam, date, this);
51     m_matchlist.append(match);
52     emit matchAdded(match);
53
54     return match;
55 }
56
57 int BackendKicker::matchCount()
58 {
59     return m_matchlist.count();
60 }
61
62 Match* BackendKicker::getMatch(int index)
63 {
64     Match* match;
65
66     match = m_matchlist.at(index);
67     if (match == NULL) {
68         qWarning() << "No match at index " << index;
69     }
70
71     return match;
72 }
73
74 QVariant BackendKicker::serializableData()
75 {
76     QVariantMap             matchvariantmap;
77     QList<QVariant>         list;
78     QListIterator<Match*>   iter(m_matchlist);
79     Match*                  match;
80     QJson::Serializer       serializer;
81
82
83     while (iter.hasNext()) {
84         match = iter.next();
85
86         matchvariantmap = QJson::QObjectHelper::qobject2qvariant(match);
87         qDebug() << serializer.serialize(matchvariantmap);
88         list.append(QVariant(matchvariantmap));
89     }
90
91     return matchvariantmap;
92 }
93
94 static QDateTime parseDate(QString datehtml)
95 {
96     static QDateTime    lastParsedDate;
97     QStringList         tokens;
98     QDate               date;
99
100     int month, day, hour, minute;
101
102     //qDebug() << "parseDate in: " << datehtml;
103
104     tokens = datehtml.split(QRegExp("[>.&;:<\"]"), QString::SkipEmptyParts);
105     date = QDate::currentDate();
106
107     //qDebug() << tokens;
108     if (tokens.count() < 6) {
109         return lastParsedDate;
110     }
111
112     month  = (tokens.at(2)).toInt();
113     day    = (tokens.at(1)).toInt();
114     hour   = (tokens.at(4)).toInt();
115     minute = (tokens.at(5)).toInt();
116
117     lastParsedDate =  QDateTime(QDate(date.year(), month, day),
118                                 QTime(hour, minute));
119
120     return lastParsedDate;
121 }
122
123 static QString parseTeam(QString teamhtml)
124 {
125     QString team;
126
127     //qDebug() << "parseTeam in: " << teamhtml;
128
129     teamhtml.truncate(teamhtml.indexOf("</a>"));
130     team = teamhtml.mid(teamhtml.lastIndexOf(">") + 1);
131
132     //qDebug() << "parseTeam out: " << team;
133     return team;
134 }
135
136 void BackendKicker::parseScore(Match* match, QString scorehtml)
137 {
138     QStringList tokens;
139
140     //qDebug() << "parseScore in: " << scorehtml;
141     tokens = scorehtml.split(QRegExp("[>&();:<]"), QString::SkipEmptyParts);
142     //qDebug() << tokens;
143
144     if (tokens.count() == 7) {
145         // no extra color tag -> either not started, halftime or finished
146         if (tokens.at(4) == "-") {
147             // no first half results -> match not started yet
148             match->setState(Match::NotStarted, m_initialUpdateDone);
149         } else if (tokens.at(1) == "-") {
150             // second half has not been started but there are first
151             // half results -> currently half time
152             match->setScore(tokens.at(4).toInt(), tokens.at(5).toInt(),
153                             m_initialUpdateDone);
154             match->setState(Match::HalfTime, m_initialUpdateDone);
155         } else {
156             // no color tag and no "-" -> game is finished
157             match->setScore(tokens.at(1).toInt(), tokens.at(2).toInt(),
158                             m_initialUpdateDone);
159             match->setState(Match::Finished, m_initialUpdateDone);
160         }
161     } else {
162         // there is a color tag which means that either first
163         // half or second half are currently running
164         if (tokens.at(4).contains("color")) {
165             // first half score marked red -> first half running
166             match->setScore(tokens.at(5).toInt(), tokens.at(6).toInt(),
167                             m_initialUpdateDone);
168             match->setState(Match::FirstHalf, m_initialUpdateDone);
169         } else if (tokens.at(1).contains("color")) {
170             // second half score marked res -> second half running
171             match->setState(Match::SecondHalf, m_initialUpdateDone);
172             match->setScore(tokens.at(2).toInt(), tokens.at(3).toInt(),
173                             m_initialUpdateDone);
174         }
175
176     }
177     qDebug() << "match state: " << match->state();
178     qDebug() << "match home: " << match->homeScore();
179     qDebug() << "match away: " << match->awayScore();
180     qDebug() << "notify: " << m_initialUpdateDone;
181 }
182
183 void BackendKicker::parsePage (QString htmlstr)
184 {
185     QStringList     rawmatches;
186     QString         hometeam, awayteam, tmp;
187     QRegExp         rx;
188     QDateTime       date;
189     Match*          match;
190
191     int             pos     = 0;
192     int             count   = 0;
193
194     //qDebug() << "parsePage in: " << htmlstr;
195
196     rx.setPattern("<td class=\"first\">(.*)<td class=\"aligncenter last\">");
197     rx.setMinimal(true);
198     while ((pos = rx.indexIn(htmlstr, pos)) != -1) {
199          ++count;
200          rawmatches.append(htmlstr.mid(pos, rx.matchedLength()));
201          //qDebug() << "MATCH " << count << ":" << htmlstr.mid(pos, rx.matchedLength()) << "\n\n";
202          pos += rx.matchedLength();
203      }
204
205     rx.setPattern("<td.*>(.*)</td>");
206
207     QStringList::iterator i;
208     for (i = rawmatches.begin(); i != rawmatches.end(); ++i) {
209         pos = 0;
210         count = 0;
211         while ((pos = rx.indexIn(*i, pos)) != -1) {
212              ++count;
213              tmp = (*i).mid(pos, rx.matchedLength());
214              pos += rx.matchedLength();
215              switch (count) {
216              case 2: // date
217                  date = parseDate(tmp);
218                  break;
219              case 3: // hometeam
220                  hometeam = parseTeam(tmp);
221                  break;
222              case 5: // awayteam
223                  awayteam = parseTeam(tmp);
224                  match = getOrAddMatch(hometeam, awayteam, date);
225                  break;
226              case 6: // scores
227                  parseScore(match, tmp);
228                  break;
229              default:
230                 ;;
231              }
232         }
233     }
234 }
235
236 bool BackendKicker::selectLeague(QString league)
237 {
238     bool leagueIsSupported = true;
239
240     if (league == "1. Bundesliga") {
241         m_URL = "http://www.kicker.de/news/fussball/bundesliga/spieltag/1-bundesliga/2010-11/spieltag.html";
242     } else if (league == "2. Bundesliga") {
243         m_URL = "http://www.kicker.de/news/fussball/bundesliga/spieltag/2-bundesliga/2010-11/spieltag.html"; 
244     } else if (league == "3. Liga") {
245         m_URL = "http://www.kicker.de/news/fussball/3liga/spieltag/3-liga/2010-11/spieltag.html";
246     } else if (league == "tipp3 Bundesliga") {
247         m_URL = "http://www.kicker.de/news/fussball/intligen/oesterreich/tipp3-bundesliga/2010-11/spieltag.html";
248     } else {
249         leagueIsSupported = false;
250     }
251
252     // delete last data
253     m_initialUpdateDone = false;
254     this->m_matchlist.clear();
255     this->update();
256
257     return leagueIsSupported;
258 }
259
260 void BackendKicker::update()
261 {
262     emit updateStarted();
263
264     qDebug() << "Start Update with URL: " << m_URL;
265     m_netaccmgr->get(QNetworkRequest(QUrl(m_URL)));
266 }
267
268 void BackendKicker::dlndFinished(QNetworkReply *reply)
269 {
270     QString         rawdata;
271     int             secstonextupdate;
272
273     if (reply->error() != QNetworkReply::NoError) {
274         qWarning() << "Download failed with error: " << reply->error();
275     } else {
276         rawdata = reply->readAll();
277         parsePage(rawdata);
278     }
279
280     secstonextupdate = secsToNextGame();
281     if ((secstonextupdate == -1) ||
282         (secstonextupdate > 8 * 3600)) {
283         // all games finished for this matchday
284         secstonextupdate = 8 * 3600;
285     } else if (secstonextupdate < INTERVAL_FAST) {
286         secstonextupdate = INTERVAL_FAST;
287     }
288     m_updateTimer.start(secstonextupdate * 1000);
289
290     emit updateFinished(reply->error());
291     m_initialUpdateDone = true;
292     qDebug() << "Update finished, next update in: " << secstonextupdate << "seconds.";
293 }
294
295 int BackendKicker::secsToNextGame()
296 {
297     QListIterator<Match*> iter(m_matchlist);
298     Match*        match;
299     int           secstogame;
300     int           secstonextgame = -1;
301
302     while (iter.hasNext()) {
303         match = iter.next();
304         if (match->state() == Match::FirstHalf ||
305             match->state() == Match::SecondHalf ||
306             match->state() == Match::HalfTime) {
307
308             secstonextgame = 0;
309             return secstonextgame;
310             break;
311         } else if (match->state() == Match::NotStarted) {
312             secstogame = QDateTime::currentDateTime().secsTo(match->date());
313             if (secstonextgame == -1) {
314                 secstonextgame = secstogame;
315             } else if (secstogame < secstonextgame) {
316                 secstonextgame = secstogame;
317             }
318         }
319     }
320
321     return secstonextgame;
322 }