#include "stationschedulemodel.h"
#include "dataprovider.h"
+#include "settings.h"
#include <QDebug>
+#include <QWebElement>
+#include <QWebFrame>
+#include <QWebPage>
StationScheduleModel::StationScheduleModel(const QString &name, QObject *parent) :
- QStringListModel(parent),
+ QAbstractListModel(parent),
m_name(name)
{
+ DataProvider *provider = DataProvider::instance();
+ QHash<int, QByteArray> roles;
+ roles[TrainRole] = "train";
+ roles[DepartureStationRole] = "departureStation";
+ roles[DepartureTimeRole] = "departureTime";
+ roles[ArrivalStationRole] = "arrivalStation";
+ roles[ArrivalTimeRole] = "arrivalTime";
+ roles[DetailsUrlRole] = "detailsUrl";
+ roles[DelayRole] = "delay";
+ roles[DelayClassRole] = "delayClassRole";
+ setRoleNames(roles);
+
+ connect(provider, SIGNAL(stationScheduleReady(QByteArray,QUrl)),
+ this, SLOT(parse(QByteArray,QUrl)));
}
QString & StationScheduleModel::name()
void StationScheduleModel::setName(const QString &name)
{
- m_name = name;
- emit nameChanged();
+ if (name != m_name) {
+ m_name = name;
+ emit nameChanged();
+ }
+}
+
+StationScheduleModel::ScheduleType StationScheduleModel::type()
+{
+ return m_scheduleType;
+}
+
+void StationScheduleModel::setType(StationScheduleModel::ScheduleType type)
+{
+ if (type != m_scheduleType) {
+ emit layoutAboutToBeChanged();
+ beginResetModel();
+ m_scheduleType = type;
+ emit typeChanged();
+ endResetModel();
+ emit layoutChanged();
+ Settings *settings = Settings::instance();
+ settings->setShowArrivalsPreferred(m_scheduleType == ArrivalSchedule ? true : false);
+ }
+}
+
+static void parseDelayClass(const QWebElement &element, StationScheduleItem &item)
+{
+ if (!element.isNull()) {
+ QWebElement image = element.findFirst("img");
+ if (!image.isNull()) {
+ int delayClass = 42;
+ QString imageName = image.attribute("src");
+ if (!imageName.isEmpty()) {
+ QRegExp delayClassRegexp("pallinoRit([0-9])\\.png");
+ int pos = delayClassRegexp.indexIn(imageName);
+ qDebug() << "regexp matched at pos:" << pos << "match:" << delayClassRegexp.cap(0);
+ delayClass = (pos >= 0) ? delayClassRegexp.cap(1).toInt() : 0;
+ }
+ item.setDelayClass(delayClass);
+ } else {
+ qDebug() << "img not found";
+ }
+ } else {
+ qDebug() << "div.bloccotreno not found";
+ }
+}
+
+static void parseDetailsUrl(const QWebElement &element, StationScheduleItem &item)
+{
+ if (!element.isNull()) {
+ QWebElement link = element.findFirst("a");
+ QString url = link.attribute("href");
+ item.setDetailsUrl(url);
+ } else {
+ qDebug() << "link not found";
+ }
+}
+
+static void parseTrain(const QString &text, StationScheduleItem &item)
+{
+ QRegExp filter("^(Per|Da) (.*)\\n"
+ "Delle ore (.*)\n"
+ "Binario Previsto: (.*)\n"
+ "Binario Reale: (.*)\n"
+ "(.*)$");
+ int pos = filter.indexIn(text);
+ if (pos >= 0) {
+ if (filter.cap(1) == "Per") {
+ item.setDepartureStation(filter.cap(2));
+ item.setDepartureTime(filter.cap(3));
+ } else {
+ item.setArrivalStation(filter.cap(2));
+ item.setArrivalTime(filter.cap(3));
+ }
+ item.setDelay(filter.cap(6));
+ } else {
+ qDebug() << "could not parse" << text;
+ }
+}
+
+StationScheduleItem parseResult(const QWebElement &result)
+{
+ StationScheduleItem item;
+
+ QWebElement current = result.findFirst("h2");
+ if (!current.isNull()) {
+ item.setTrain(current.toPlainText());
+ }
+ parseDetailsUrl(result, item);
+ current = result.findFirst("div.bloccotreno");
+ parseDelayClass(current, item);
+ QString rawText = current.toPlainText();
+ parseTrain(rawText, item);
+
+ qDebug() << "train:" << item.train();
+ qDebug() << "delayClass:" << item.delayClass();
+ qDebug() << "detailsUrl:" << item.detailsUrl();
+ qDebug() << "departureStation:" << item.departureStation();
+ qDebug() << "departureTime:" << item.departureTime();
+ qDebug() << "arrivalStation:" << item.arrivalStation();
+ qDebug() << "arrivalTime:" << item.arrivalTime();
+ qDebug() << "delay:" << item.delay();
+ return item;
}
void StationScheduleModel::parse(const QByteArray &htmlReply, const QUrl &baseUrl)
{
Q_UNUSED(baseUrl);
qDebug() << "--- start of query result --- cut here ------";
- qDebug() << htmlReply;
+ qDebug() << QString::fromUtf8(htmlReply.constData());
qDebug() << "--- end of query result ----- cut here ------";
+
+ emit layoutAboutToBeChanged();
+ beginResetModel();
+ QWebPage page;
+ page.mainFrame()->setContent(htmlReply, "text/html", baseUrl);
+ QWebElement doc = page.mainFrame()->documentElement();
+
+ // Find the first div
+ QWebElement current = doc.findFirst("div");
+
+ QStringList departures, arrivals;
+ qDebug() << "skipping to the departures";
+ // Skip to the first div of class corpocentrale, which contains the first
+ // departure-related contents
+ while (!current.classes().contains("corpocentrale")) {
+ current = current.nextSibling();
+ qDebug() << "skipping to the next element";
+ if (current.isNull())
+ break;
+ }
+ // Mark every div as a departure class element; the next corpocentrale
+ // marks the start of the arrivals section
+ qDebug() << "marking departures";
+ do {
+ if (current.classes().contains("bloccorisultato")) {
+ departures << current.toPlainText();
+ StationScheduleItem schedule = parseResult(current);
+ if (schedule.isValid()) {
+ m_departureSchedules << schedule;
+ }
+ }
+ current = current.nextSibling();
+ qDebug() << "marking as departures";
+ if (current.isNull())
+ break;
+ } while (!current.classes().contains("corpocentrale"));
+
+ // Mark everything as an arrival, until reaching the footer
+ while (!current.classes().contains("footer")) {
+ if (current.classes().contains("bloccorisultato")) {
+ arrivals << current.toPlainText();
+ StationScheduleItem schedule = parseResult(current);
+ if (schedule.isValid()) {
+ m_arrivalSchedules << schedule;
+ }
+ }
+ current = current.nextSibling();
+ qDebug() << "marking as arrival";
+ if (current.isNull())
+ break;
+ }
+ endResetModel();
+ emit layoutChanged();
}
void StationScheduleModel::fetch(const QString &name)
{
DataProvider *provider = DataProvider::instance();
- connect(provider, SIGNAL(stationScheduleReady(QByteArray,QUrl)),
- this, SLOT(parse(QByteArray,QUrl)));
provider->fetchStationSchedule(name);
setName(name);
}
+
+int StationScheduleModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ if (m_scheduleType == DepartureSchedule) {
+ qDebug() << "schedule.count" << m_departureSchedules.count();
+ return m_departureSchedules.count();
+ } else {
+ qDebug() << "schedule.count" << m_arrivalSchedules.count();
+ return m_arrivalSchedules.count();
+ }
+}
+
+QVariant StationScheduleModel::data(const QModelIndex &index, int role) const
+{
+ qDebug() << "getting data for role" << role;
+ if (!index.isValid()) {
+ return QVariant();
+ }
+ const QList<StationScheduleItem> &schedules =
+ (m_scheduleType == DepartureSchedule) ? m_departureSchedules : m_arrivalSchedules;
+ if (index.row() < 0 || index.row() >= schedules.count()) {
+ return QVariant();
+ }
+ StationScheduleItem item = schedules[index.row()];
+ switch (role) {
+ case Qt::DisplayRole:
+ case TrainRole:
+ return QVariant::fromValue(item.train());
+ case DepartureStationRole:
+ return QVariant::fromValue(item.departureStation());
+ case DepartureTimeRole:
+ return QVariant::fromValue(item.departureTime());
+ case ArrivalStationRole:
+ return QVariant::fromValue(item.arrivalStation());
+ case ArrivalTimeRole:
+ return QVariant::fromValue(item.arrivalTime());
+ case DetailsUrlRole:
+ return QVariant::fromValue(item.detailsUrl());
+ case DelayRole:
+ return QVariant::fromValue(item.delay());
+ case DelayClassRole:
+ return QVariant::fromValue(item.delayClass());
+ default:
+ return QVariant::fromValue(QString("Unknown role requested"));
+ }
+}