Added platform getters setters to the scheduleItem class
[quandoparte] / application / stationschedulemodel.cpp
1 /*
2
3 Copyright (C) 2011 Luciano Montanaro <mikelima@cirulla.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING.  If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19
20 */
21
22 #include "stationschedulemodel.h"
23
24 #include "dataprovider.h"
25 #include "settings.h"
26
27 #include <QDebug>
28 #include <QWebElement>
29 #include <QWebFrame>
30 #include <QWebPage>
31
32 StationScheduleModel::StationScheduleModel(const QString &name, QObject *parent) :
33     QAbstractListModel(parent),
34     m_name(name)
35
36 {
37     DataProvider *provider = DataProvider::instance();
38     QHash<int, QByteArray> roles;
39     roles[TrainRole] = "train";
40     roles[DepartureStationRole] = "departureStation";
41     roles[DepartureTimeRole] = "departureTime";
42     roles[ArrivalStationRole] = "arrivalStation";
43     roles[ArrivalTimeRole] = "arrivalTime";
44     roles[DetailsUrlRole] = "detailsUrl";
45     roles[DelayRole] = "delay";
46     roles[DelayClassRole] = "delayClass";
47     roles[ExpectedPlatformRole] = "expectedPlatform";
48     roles[ActualPlatformRole] = "actualPlatform";
49     setRoleNames(roles);
50
51     connect(provider, SIGNAL(stationScheduleReady(QByteArray,QUrl)),
52             this, SLOT(parse(QByteArray,QUrl)));
53 }
54
55 QString & StationScheduleModel::name()
56 {
57     return m_name;
58 }
59
60 void StationScheduleModel::setName(const QString &name)
61 {
62     if (name != m_name) {
63         m_name = name;
64         emit nameChanged();
65     }
66 }
67
68 StationScheduleModel::ScheduleType StationScheduleModel::type()
69 {
70     return m_scheduleType;
71 }
72
73 void StationScheduleModel::setType(StationScheduleModel::ScheduleType type)
74 {
75     if (type != m_scheduleType) {
76         emit layoutAboutToBeChanged();
77         beginResetModel();
78         m_scheduleType = type;
79         emit typeChanged();
80         endResetModel();
81         emit layoutChanged();
82         Settings *settings = Settings::instance();
83         settings->setShowArrivalsPreferred(m_scheduleType == ArrivalSchedule ? true : false);
84     }
85 }
86
87 static void parseDelayClass(const QWebElement &element, StationScheduleItem &item)
88 {
89     if (!element.isNull()) {
90         QWebElement image = element.findFirst("img");
91         if (!image.isNull()) {
92             int delayClass = 42;
93             QString imageName = image.attribute("src");
94             if (!imageName.isEmpty()) {
95                 QRegExp delayClassRegexp("pallinoRit([0-9])\\.png");
96                 int pos = delayClassRegexp.indexIn(imageName);
97                 qDebug() << "regexp matched at pos:" << pos << "match:" << delayClassRegexp.cap(0);
98                 delayClass =  (pos >= 0) ? delayClassRegexp.cap(1).toInt() : 0;
99             }
100             item.setDelayClass(delayClass);
101         } else {
102             qDebug() << "img not found";
103         }
104     } else {
105         qDebug() << "div.bloccotreno not found";
106     }
107 }
108
109 static void parseDetailsUrl(const QWebElement &element, StationScheduleItem &item)
110 {
111     if (!element.isNull()) {
112         QWebElement link = element.findFirst("a");
113         QString url = link.attribute("href");
114         item.setDetailsUrl(url);
115     } else {
116         qDebug() << "link not found";
117     }
118 }
119
120 static void parseTrain(const QString &text, StationScheduleItem &item)
121 {
122     QRegExp filter("^(Per|Da) (.*)\\n"
123                    "Delle ore (.*)\n"
124                    "Binario Previsto: (.*)\n"
125                    "Binario Reale: (.*)\n"
126                    "(.*)$");
127     int pos = filter.indexIn(text);
128     if (pos >= 0) {
129         if (filter.cap(1) == "Per") {
130             item.setDepartureStation(filter.cap(2));
131             item.setDepartureTime(filter.cap(3));
132         } else {
133             item.setArrivalStation(filter.cap(2));
134             item.setArrivalTime(filter.cap(3));
135         }
136         item.setDelay(filter.cap(6));
137         item.setExpectedPlatform(filter.cap(4));
138         item.setActualPlatform(filter.cap(5));
139     } else {
140         qDebug() << "could not parse" << text;
141     }
142 }
143
144 StationScheduleItem parseResult(const QWebElement &result)
145 {
146     StationScheduleItem item;
147
148     QWebElement current = result.findFirst("h2");
149     if (!current.isNull()) {
150         item.setTrain(current.toPlainText());
151     }
152     parseDetailsUrl(result, item);
153     current = result.findFirst("div.bloccotreno");
154     parseDelayClass(current, item);
155     QString rawText = current.toPlainText();
156     parseTrain(rawText, item);
157
158     qDebug() << "train:" << item.train();
159     qDebug() << "delayClass:" << item.delayClass();
160     qDebug() << "detailsUrl:" << item.detailsUrl();
161     qDebug() << "departureStation:" << item.departureStation();
162     qDebug() << "departureTime:" << item.departureTime();
163     qDebug() << "arrivalStation:" << item.arrivalStation();
164     qDebug() << "arrivalTime:" << item.arrivalTime();
165     qDebug() << "expectedPlatform:" << item.expectedPlatform();
166     qDebug() << "actualPlatform:" << item.actualPlatform();
167     qDebug() << "delay:" << item.delay();
168     return item;
169 }
170
171 void StationScheduleModel::parse(const QByteArray &htmlReply, const QUrl &baseUrl)
172 {
173     Q_UNUSED(baseUrl);
174     qDebug() << "--- start of query result --- cut here ------";
175     qDebug() << QString::fromUtf8(htmlReply.constData());
176     qDebug() << "--- end of query result ----- cut here ------";
177
178     emit layoutAboutToBeChanged();
179     beginResetModel();
180     QWebPage page;
181     page.mainFrame()->setContent(htmlReply, "text/html", baseUrl);
182     QWebElement doc = page.mainFrame()->documentElement();
183
184     // Find the first div
185     QWebElement current = doc.findFirst("div");
186
187     QStringList departures, arrivals;
188     qDebug() << "skipping to the departures";
189     // Skip to the first div of class corpocentrale, which contains the first
190     // departure-related contents
191     while (!current.classes().contains("corpocentrale")) {
192         current = current.nextSibling();
193         qDebug() << "skipping to the next element";
194         if (current.isNull())
195             break;
196     }
197     // Mark every div as a departure class element; the next corpocentrale
198     // marks the start of the arrivals section
199     qDebug() << "marking departures";
200     do {
201         if (current.classes().contains("bloccorisultato")) {
202             departures << current.toPlainText();
203             StationScheduleItem schedule = parseResult(current);
204             if (schedule.isValid()) {
205                 m_departureSchedules << schedule;
206             }
207         }
208         current = current.nextSibling();
209         qDebug() << "marking as departures";
210         if (current.isNull())
211             break;
212     } while (!current.classes().contains("corpocentrale"));
213
214     // Mark everything as an arrival, until reaching the footer
215     while (!current.classes().contains("footer")) {
216         if (current.classes().contains("bloccorisultato")) {
217             arrivals << current.toPlainText();
218             StationScheduleItem schedule = parseResult(current);
219             if (schedule.isValid()) {
220                 m_arrivalSchedules << schedule;
221             }
222         }
223         current = current.nextSibling();
224         qDebug() << "marking as arrival";
225         if (current.isNull())
226             break;
227     }
228     endResetModel();
229     emit layoutChanged();
230 }
231
232 void StationScheduleModel::fetch(const QString &name)
233 {
234     DataProvider *provider = DataProvider::instance();
235
236     provider->fetchStationSchedule(name);
237     setName(name);
238 }
239
240 int StationScheduleModel::rowCount(const QModelIndex &parent) const
241 {
242     Q_UNUSED(parent);
243     if (m_scheduleType == DepartureSchedule) {
244         qDebug() << "schedule.count" << m_departureSchedules.count();
245         return m_departureSchedules.count();
246     } else {
247         qDebug() << "schedule.count" << m_arrivalSchedules.count();
248         return m_arrivalSchedules.count();
249     }
250 }
251
252 QVariant StationScheduleModel::data(const QModelIndex &index, int role) const
253 {
254     qDebug() << "getting data for role" << role;
255     if (!index.isValid()) {
256         return QVariant();
257     }
258     const QList<StationScheduleItem> &schedules =
259             (m_scheduleType == DepartureSchedule) ? m_departureSchedules : m_arrivalSchedules;
260     if (index.row() < 0 || index.row() >= schedules.count()) {
261         return QVariant();
262     }
263     StationScheduleItem item = schedules[index.row()];
264     switch (role) {
265     case Qt::DisplayRole:
266     case TrainRole:
267         return QVariant::fromValue(item.train());
268     case DepartureStationRole:
269         return QVariant::fromValue(item.departureStation());
270     case DepartureTimeRole:
271         return QVariant::fromValue(item.departureTime());
272     case ArrivalStationRole:
273         return QVariant::fromValue(item.arrivalStation());
274     case ArrivalTimeRole:
275         return QVariant::fromValue(item.arrivalTime());
276     case DetailsUrlRole:
277         return QVariant::fromValue(item.detailsUrl());
278     case DelayRole:
279         return QVariant::fromValue(item.delay());
280     case DelayClassRole:
281         return QVariant::fromValue(item.delayClass());
282     default:
283         return QVariant::fromValue(QString("Unknown role requested"));
284     }
285 }