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