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