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"
29 #if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
30 #include <QtWebKitWidgets>
32 #include <QWebElement>
37 StationScheduleModel::StationScheduleModel(const QString &name, QObject *parent) :
38 QAbstractListModel(parent),
43 DataProvider *provider = DataProvider::instance();
44 connect(provider, SIGNAL(stationScheduleReady(QByteArray,QUrl)),
45 this, SLOT(parse(QByteArray,QUrl)));
46 connect(provider, SIGNAL(error()),
47 this, SLOT(onNetworkError()));
48 Settings *settings = Settings::instance();
49 m_scheduleType = settings->showArrivalsPreferred() ? ArrivalSchedule : DepartureSchedule;
50 #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
51 setRoleNames(roleNames());
55 const QString &StationScheduleModel::name()
60 void StationScheduleModel::setName(const QString &name)
68 const QString &StationScheduleModel::code()
73 void StationScheduleModel::setCode(const QString &code)
81 const QString &StationScheduleModel::error()
86 void StationScheduleModel::setError(const QString &error)
88 if (error != m_error) {
94 QHash<int, QByteArray> StationScheduleModel::roleNames() const
96 QHash<int, QByteArray> roles;
97 roles[TrainRole] = "train";
98 roles[DepartureStationRole] = "departureStation";
99 roles[DepartureTimeRole] = "departureTime";
100 roles[ArrivalStationRole] = "arrivalStation";
101 roles[ArrivalTimeRole] = "arrivalTime";
102 roles[DetailsUrlRole] = "detailsUrl";
103 roles[DelayRole] = "delay";
104 roles[DelayClassRole] = "delayClass";
105 roles[ExpectedPlatformRole] = "expectedPlatform";
106 roles[ActualPlatformRole] = "actualPlatform";
111 StationScheduleModel::ScheduleType StationScheduleModel::type()
113 return m_scheduleType;
116 void StationScheduleModel::setType(StationScheduleModel::ScheduleType type)
118 if (type != m_scheduleType) {
119 emit layoutAboutToBeChanged();
121 m_scheduleType = type;
124 emit layoutChanged();
125 Settings *settings = Settings::instance();
126 settings->setShowArrivalsPreferred(m_scheduleType == ArrivalSchedule ? true : false);
130 static void parseDelayClass(const QWebElement &element, StationScheduleItem &item)
132 if (!element.isNull()) {
133 QWebElement image = element.findFirst("img");
134 if (!image.isNull()) {
136 QString imageName = image.attribute("src");
137 if (!imageName.isEmpty()) {
138 QRegExp delayClassRegexp("pallinoRit([0-9])\\.png");
139 int pos = delayClassRegexp.indexIn(imageName);
140 qDebug() << "regexp matched at pos:" << pos << "match:" << delayClassRegexp.cap(0);
141 delayClass = (pos >= 0) ? delayClassRegexp.cap(1).toInt() : 0;
143 item.setDelayClass(delayClass);
145 qDebug() << "img not found";
148 qDebug() << "div.bloccotreno not found";
152 static void parseDetailsUrl(const QWebElement &element, StationScheduleItem &item)
154 if (!element.isNull()) {
155 QWebElement link = element.findFirst("a");
156 QString url = link.attribute("href");
157 item.setDetailsUrl(url);
159 qDebug() << "link not found";
163 static void parseTrain(const QString &text, StationScheduleItem &item)
165 QRegExp filter("^(Per|Da) (.*)\\n"
167 "Binario Previsto: (.*)\n"
168 "Binario Reale: (.*)\n"
170 int pos = filter.indexIn(text);
172 if (filter.cap(1) == "Per") {
173 item.setDepartureStation(filter.cap(2));
174 item.setDepartureTime(filter.cap(3));
176 item.setArrivalStation(filter.cap(2));
177 item.setArrivalTime(filter.cap(3));
179 QString delayDescription = filter.cap(6);
180 if (delayDescription == "in orario") {
181 item.setDelay(QObject::tr("On time"));
183 QRegExp delayRegExp("ritardo ([0-9]+)(| minuti)");
184 int pos = delayRegExp.indexIn(delayDescription);
186 item.setDelay(QString(QObject::tr("Delay: %1 minutes")).arg(delayRegExp.cap(1)));
188 // Does not match, let the user parse it
189 item.setDelay(delayDescription);
192 item.setExpectedPlatform(filter.cap(4));
193 item.setActualPlatform(filter.cap(5));
195 qDebug() << "could not parse" << text;
199 StationScheduleItem parseResult(const QWebElement &result)
201 StationScheduleItem item;
203 QWebElement current = result.findFirst("h2");
204 if (!current.isNull()) {
205 item.setTrain(current.toPlainText());
207 parseDetailsUrl(result, item);
208 current = result.findFirst("div.bloccotreno");
209 parseDelayClass(current, item);
210 QString rawText = current.toPlainText();
211 parseTrain(rawText, item);
213 qDebug() << "train:" << item.train();
214 qDebug() << "delayClass:" << item.delayClass();
215 qDebug() << "detailsUrl:" << item.detailsUrl();
216 qDebug() << "departureStation:" << item.departureStation();
217 qDebug() << "departureTime:" << item.departureTime();
218 qDebug() << "arrivalStation:" << item.arrivalStation();
219 qDebug() << "arrivalTime:" << item.arrivalTime();
220 qDebug() << "expectedPlatform:" << item.expectedPlatform();
221 qDebug() << "actualPlatform:" << item.actualPlatform();
222 qDebug() << "delay:" << item.delay();
226 void StationScheduleModel::parse(const QByteArray &htmlReply, const QUrl &baseUrl)
229 qDebug() << "--- start of query result --- cut here ------";
230 qDebug() << QString::fromUtf8(htmlReply.constData());
231 qDebug() << "--- end of query result ----- cut here ------";
233 emit layoutAboutToBeChanged();
236 page.mainFrame()->setContent(htmlReply, "text/html", baseUrl);
237 QWebElement doc = page.mainFrame()->documentElement();
239 // Check if the page is reporting an error before parsing it
240 QWebElement errorElement = doc.findFirst("span.errore");
241 if (!errorElement.isNull()) {
242 m_departureSchedules.clear();
243 m_arrivalSchedules.clear();
244 QString errorText = errorElement.toPlainText().trimmed();
245 if (errorText == "localita' non trovata") {
246 setError(tr("Unknown station"));
248 setError(tr("Unknown error"));
250 qDebug() << "error:" << error();
253 // Find the first div
254 QWebElement current = doc.findFirst("div");
256 qDebug() << "skipping to the departures";
257 // Skip to the first div of class corpocentrale, which contains the first
258 // departure-related contents
259 while (!current.classes().contains("corpocentrale")) {
260 current = current.nextSibling();
261 qDebug() << "skipping to the next element";
262 if (current.isNull())
265 // Mark every div as a departure class element; the next corpocentrale
266 // marks the start of the arrivals section
267 qDebug() << "marking departures";
269 if (current.classes().contains("bloccorisultato")) {
270 StationScheduleItem schedule = parseResult(current);
271 if (schedule.isValid()) {
272 m_departureSchedules << schedule;
275 current = current.nextSibling();
276 qDebug() << "marking as departures";
277 if (current.isNull())
279 } while (!current.classes().contains("corpocentrale"));
281 // Mark everything as an arrival, until reaching the footer
282 while (!current.classes().contains("footer")) {
283 if (current.classes().contains("bloccorisultato")) {
284 StationScheduleItem schedule = parseResult(current);
285 if (schedule.isValid()) {
286 m_arrivalSchedules << schedule;
289 current = current.nextSibling();
290 qDebug() << "marking as arrival";
291 if (current.isNull())
295 emit layoutChanged();
298 void StationScheduleModel::onNetworkError()
300 qDebug()<< "Station Schedule Model got a Network Error";
301 m_error = tr("Network error");
305 void StationScheduleModel::fetch(const QString &name, const QString &code)
307 DataProvider *provider = DataProvider::instance();
309 if (!error().isEmpty())
311 m_departureSchedules.clear();
312 m_arrivalSchedules.clear();
313 provider->fetchStationSchedule(name, code);
318 int StationScheduleModel::rowCount(const QModelIndex &parent) const
321 if (m_scheduleType == DepartureSchedule) {
322 return m_departureSchedules.count();
324 return m_arrivalSchedules.count();
328 QVariant StationScheduleModel::data(const QModelIndex &index, int role) const
330 if (!index.isValid()) {
333 const QList<StationScheduleItem> &schedules =
334 (m_scheduleType == DepartureSchedule) ? m_departureSchedules : m_arrivalSchedules;
335 if (index.row() < 0 || index.row() >= schedules.count()) {
338 StationScheduleItem item = schedules[index.row()];
340 case Qt::DisplayRole:
342 return QVariant::fromValue(item.train());
343 case DepartureStationRole:
344 return QVariant::fromValue(item.departureStation());
345 case DepartureTimeRole:
346 return QVariant::fromValue(item.departureTime());
347 case ArrivalStationRole:
348 return QVariant::fromValue(item.arrivalStation());
349 case ArrivalTimeRole:
350 return QVariant::fromValue(item.arrivalTime());
352 return QVariant::fromValue(item.detailsUrl());
354 return QVariant::fromValue(item.delay());
356 return QVariant::fromValue(item.delayClass());
357 case ExpectedPlatformRole:
358 return QVariant::fromValue(item.expectedPlatform());
359 case ActualPlatformRole:
360 return QVariant::fromValue(item.actualPlatform());
362 return QVariant::fromValue(QString("Unknown role requested"));