3 Copyright (C) 2011 Luciano Montanaro <mikelima@cirulla.net>
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.
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.
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.
22 #include "stationschedulemodel.h"
24 #include "dataprovider.h"
28 #include <QWebElement>
32 StationScheduleModel::StationScheduleModel(const QString &name, QObject *parent) :
33 QAbstractListModel(parent),
38 DataProvider *provider = DataProvider::instance();
39 QHash<int, QByteArray> roles;
40 roles[TrainRole] = "train";
41 roles[DepartureStationRole] = "departureStation";
42 roles[DepartureTimeRole] = "departureTime";
43 roles[ArrivalStationRole] = "arrivalStation";
44 roles[ArrivalTimeRole] = "arrivalTime";
45 roles[DetailsUrlRole] = "detailsUrl";
46 roles[DelayRole] = "delay";
47 roles[DelayClassRole] = "delayClass";
48 roles[ExpectedPlatformRole] = "expectedPlatform";
49 roles[ActualPlatformRole] = "actualPlatform";
52 connect(provider, SIGNAL(stationScheduleReady(QByteArray,QUrl)),
53 this, SLOT(parse(QByteArray,QUrl)));
54 connect(provider, SIGNAL(error()),
55 this, SLOT(onNetworkError()));
56 Settings *settings = Settings::instance();
57 m_scheduleType = settings->showArrivalsPreferred() ? ArrivalSchedule : DepartureSchedule;
60 const QString &StationScheduleModel::name()
65 void StationScheduleModel::setName(const QString &name)
73 const QString &StationScheduleModel::code()
78 void StationScheduleModel::setCode(const QString &code)
86 const QString &StationScheduleModel::error()
91 void StationScheduleModel::setError(const QString &error)
93 if (error != m_error) {
99 StationScheduleModel::ScheduleType StationScheduleModel::type()
101 return m_scheduleType;
104 void StationScheduleModel::setType(StationScheduleModel::ScheduleType type)
106 if (type != m_scheduleType) {
107 emit layoutAboutToBeChanged();
109 m_scheduleType = type;
112 emit layoutChanged();
113 Settings *settings = Settings::instance();
114 settings->setShowArrivalsPreferred(m_scheduleType == ArrivalSchedule ? true : false);
118 static void parseDelayClass(const QWebElement &element, StationScheduleItem &item)
120 if (!element.isNull()) {
121 QWebElement image = element.findFirst("img");
122 if (!image.isNull()) {
124 QString imageName = image.attribute("src");
125 if (!imageName.isEmpty()) {
126 QRegExp delayClassRegexp("pallinoRit([0-9])\\.png");
127 int pos = delayClassRegexp.indexIn(imageName);
128 qDebug() << "regexp matched at pos:" << pos << "match:" << delayClassRegexp.cap(0);
129 delayClass = (pos >= 0) ? delayClassRegexp.cap(1).toInt() : 0;
131 item.setDelayClass(delayClass);
133 qDebug() << "img not found";
136 qDebug() << "div.bloccotreno not found";
140 static void parseDetailsUrl(const QWebElement &element, StationScheduleItem &item)
142 if (!element.isNull()) {
143 QWebElement link = element.findFirst("a");
144 QString url = link.attribute("href");
145 item.setDetailsUrl(url);
147 qDebug() << "link not found";
151 static void parseTrain(const QString &text, StationScheduleItem &item)
153 QRegExp filter("^(Per|Da) (.*)\\n"
155 "Binario Previsto: (.*)\n"
156 "Binario Reale: (.*)\n"
158 int pos = filter.indexIn(text);
160 if (filter.cap(1) == "Per") {
161 item.setDepartureStation(filter.cap(2));
162 item.setDepartureTime(filter.cap(3));
164 item.setArrivalStation(filter.cap(2));
165 item.setArrivalTime(filter.cap(3));
167 item.setDelay(filter.cap(6));
168 item.setExpectedPlatform(filter.cap(4));
169 item.setActualPlatform(filter.cap(5));
171 qDebug() << "could not parse" << text;
175 StationScheduleItem parseResult(const QWebElement &result)
177 StationScheduleItem item;
179 QWebElement current = result.findFirst("h2");
180 if (!current.isNull()) {
181 item.setTrain(current.toPlainText());
183 parseDetailsUrl(result, item);
184 current = result.findFirst("div.bloccotreno");
185 parseDelayClass(current, item);
186 QString rawText = current.toPlainText();
187 parseTrain(rawText, item);
189 qDebug() << "train:" << item.train();
190 qDebug() << "delayClass:" << item.delayClass();
191 qDebug() << "detailsUrl:" << item.detailsUrl();
192 qDebug() << "departureStation:" << item.departureStation();
193 qDebug() << "departureTime:" << item.departureTime();
194 qDebug() << "arrivalStation:" << item.arrivalStation();
195 qDebug() << "arrivalTime:" << item.arrivalTime();
196 qDebug() << "expectedPlatform:" << item.expectedPlatform();
197 qDebug() << "actualPlatform:" << item.actualPlatform();
198 qDebug() << "delay:" << item.delay();
202 void StationScheduleModel::parse(const QByteArray &htmlReply, const QUrl &baseUrl)
205 qDebug() << "--- start of query result --- cut here ------";
206 qDebug() << QString::fromUtf8(htmlReply.constData());
207 qDebug() << "--- end of query result ----- cut here ------";
209 emit layoutAboutToBeChanged();
212 page.mainFrame()->setContent(htmlReply, "text/html", baseUrl);
213 QWebElement doc = page.mainFrame()->documentElement();
215 // Find the first div
216 QWebElement current = doc.findFirst("div");
218 QStringList departures, arrivals;
219 qDebug() << "skipping to the departures";
220 // Skip to the first div of class corpocentrale, which contains the first
221 // departure-related contents
222 while (!current.classes().contains("corpocentrale")) {
223 current = current.nextSibling();
224 qDebug() << "skipping to the next element";
225 if (current.isNull())
228 // Mark every div as a departure class element; the next corpocentrale
229 // marks the start of the arrivals section
230 qDebug() << "marking departures";
232 if (current.classes().contains("bloccorisultato")) {
233 departures << current.toPlainText();
234 StationScheduleItem schedule = parseResult(current);
235 if (schedule.isValid()) {
236 m_departureSchedules << schedule;
239 current = current.nextSibling();
240 qDebug() << "marking as departures";
241 if (current.isNull())
243 } while (!current.classes().contains("corpocentrale"));
245 // Mark everything as an arrival, until reaching the footer
246 while (!current.classes().contains("footer")) {
247 if (current.classes().contains("bloccorisultato")) {
248 arrivals << current.toPlainText();
249 StationScheduleItem schedule = parseResult(current);
250 if (schedule.isValid()) {
251 m_arrivalSchedules << schedule;
254 current = current.nextSibling();
255 qDebug() << "marking as arrival";
256 if (current.isNull())
260 emit layoutChanged();
263 void StationScheduleModel::onNetworkError()
265 qDebug()<< "Station Schedule Model got a Network Error";
266 m_error = tr("Network error");
270 void StationScheduleModel::fetch(const QString &name, const QString &code)
272 DataProvider *provider = DataProvider::instance();
274 if (!error().isEmpty())
276 m_departureSchedules.clear();
277 m_arrivalSchedules.clear();
278 provider->fetchStationSchedule(name, code);
283 int StationScheduleModel::rowCount(const QModelIndex &parent) const
286 if (m_scheduleType == DepartureSchedule) {
287 return m_departureSchedules.count();
289 return m_arrivalSchedules.count();
293 QVariant StationScheduleModel::data(const QModelIndex &index, int role) const
295 if (!index.isValid()) {
298 const QList<StationScheduleItem> &schedules =
299 (m_scheduleType == DepartureSchedule) ? m_departureSchedules : m_arrivalSchedules;
300 if (index.row() < 0 || index.row() >= schedules.count()) {
303 StationScheduleItem item = schedules[index.row()];
305 case Qt::DisplayRole:
307 return QVariant::fromValue(item.train());
308 case DepartureStationRole:
309 return QVariant::fromValue(item.departureStation());
310 case DepartureTimeRole:
311 return QVariant::fromValue(item.departureTime());
312 case ArrivalStationRole:
313 return QVariant::fromValue(item.arrivalStation());
314 case ArrivalTimeRole:
315 return QVariant::fromValue(item.arrivalTime());
317 return QVariant::fromValue(item.detailsUrl());
319 return QVariant::fromValue(item.delay());
321 return QVariant::fromValue(item.delayClass());
322 case ExpectedPlatformRole:
323 return QVariant::fromValue(item.expectedPlatform());
324 case ActualPlatformRole:
325 return QVariant::fromValue(item.actualPlatform());
327 return QVariant::fromValue(QString("Unknown role requested"));