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