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),
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";
51 connect(provider, SIGNAL(stationScheduleReady(QByteArray,QUrl)),
52 this, SLOT(parse(QByteArray,QUrl)));
55 QString & StationScheduleModel::name()
60 void StationScheduleModel::setName(const QString &name)
68 StationScheduleModel::ScheduleType StationScheduleModel::type()
70 return m_scheduleType;
73 void StationScheduleModel::setType(StationScheduleModel::ScheduleType type)
75 if (type != m_scheduleType) {
76 emit layoutAboutToBeChanged();
78 m_scheduleType = type;
82 Settings *settings = Settings::instance();
83 settings->setShowArrivalsPreferred(m_scheduleType == ArrivalSchedule ? true : false);
87 static void parseDelayClass(const QWebElement &element, StationScheduleItem &item)
89 if (!element.isNull()) {
90 QWebElement image = element.findFirst("img");
91 if (!image.isNull()) {
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;
100 item.setDelayClass(delayClass);
102 qDebug() << "img not found";
105 qDebug() << "div.bloccotreno not found";
109 static void parseDetailsUrl(const QWebElement &element, StationScheduleItem &item)
111 if (!element.isNull()) {
112 QWebElement link = element.findFirst("a");
113 QString url = link.attribute("href");
114 item.setDetailsUrl(url);
116 qDebug() << "link not found";
120 static void parseTrain(const QString &text, StationScheduleItem &item)
122 QRegExp filter("^(Per|Da) (.*)\\n"
124 "Binario Previsto: (.*)\n"
125 "Binario Reale: (.*)\n"
127 int pos = filter.indexIn(text);
129 if (filter.cap(1) == "Per") {
130 item.setDepartureStation(filter.cap(2));
131 item.setDepartureTime(filter.cap(3));
133 item.setArrivalStation(filter.cap(2));
134 item.setArrivalTime(filter.cap(3));
136 item.setDelay(filter.cap(6));
137 item.setExpectedPlatform(filter.cap(4));
138 item.setActualPlatform(filter.cap(5));
140 qDebug() << "could not parse" << text;
144 StationScheduleItem parseResult(const QWebElement &result)
146 StationScheduleItem item;
148 QWebElement current = result.findFirst("h2");
149 if (!current.isNull()) {
150 item.setTrain(current.toPlainText());
152 parseDetailsUrl(result, item);
153 current = result.findFirst("div.bloccotreno");
154 parseDelayClass(current, item);
155 QString rawText = current.toPlainText();
156 parseTrain(rawText, item);
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();
171 void StationScheduleModel::parse(const QByteArray &htmlReply, const QUrl &baseUrl)
174 qDebug() << "--- start of query result --- cut here ------";
175 qDebug() << QString::fromUtf8(htmlReply.constData());
176 qDebug() << "--- end of query result ----- cut here ------";
178 emit layoutAboutToBeChanged();
181 page.mainFrame()->setContent(htmlReply, "text/html", baseUrl);
182 QWebElement doc = page.mainFrame()->documentElement();
184 // Find the first div
185 QWebElement current = doc.findFirst("div");
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())
197 // Mark every div as a departure class element; the next corpocentrale
198 // marks the start of the arrivals section
199 qDebug() << "marking departures";
201 if (current.classes().contains("bloccorisultato")) {
202 departures << current.toPlainText();
203 StationScheduleItem schedule = parseResult(current);
204 if (schedule.isValid()) {
205 m_departureSchedules << schedule;
208 current = current.nextSibling();
209 qDebug() << "marking as departures";
210 if (current.isNull())
212 } while (!current.classes().contains("corpocentrale"));
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;
223 current = current.nextSibling();
224 qDebug() << "marking as arrival";
225 if (current.isNull())
229 emit layoutChanged();
232 void StationScheduleModel::fetch(const QString &name)
234 DataProvider *provider = DataProvider::instance();
236 provider->fetchStationSchedule(name);
240 int StationScheduleModel::rowCount(const QModelIndex &parent) const
243 if (m_scheduleType == DepartureSchedule) {
244 qDebug() << "schedule.count" << m_departureSchedules.count();
245 return m_departureSchedules.count();
247 qDebug() << "schedule.count" << m_arrivalSchedules.count();
248 return m_arrivalSchedules.count();
252 QVariant StationScheduleModel::data(const QModelIndex &index, int role) const
254 qDebug() << "getting data for role" << role;
255 if (!index.isValid()) {
258 const QList<StationScheduleItem> &schedules =
259 (m_scheduleType == DepartureSchedule) ? m_departureSchedules : m_arrivalSchedules;
260 if (index.row() < 0 || index.row() >= schedules.count()) {
263 StationScheduleItem item = schedules[index.row()];
265 case Qt::DisplayRole:
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());
277 return QVariant::fromValue(item.detailsUrl());
279 return QVariant::fromValue(item.delay());
281 return QVariant::fromValue(item.delayClass());
283 return QVariant::fromValue(QString("Unknown role requested"));