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