Commit bee7ad8b authored by Volker Krause's avatar Volker Krause
Browse files

Update reservations from live data where applicable

This also removes the last traces of KPublicTransport::Departure, and
extends the unit testing of LiveDataManager.
parent fbcd0def
......@@ -19,6 +19,9 @@
#include <livedatamanager.h>
#include <reservationmanager.h>
#include <KItinerary/Reservation>
#include <KItinerary/TrainTrip>
#include <KPublicTransport/Manager>
#include <QJsonDocument>
......@@ -30,6 +33,8 @@
#define s(x) QStringLiteral(x)
using namespace KItinerary;
class LiveDataManagerTest : public QObject
{
Q_OBJECT
......@@ -87,9 +92,12 @@ private Q_SLOTS:
{
ReservationManager resMgr;
clearReservations(&resMgr);
QSignalSpy resChangeSpy(&resMgr, &ReservationManager::batchContentChanged);
LiveData::clearStorage();
LiveDataManager ldm;
QSignalSpy arrivalUpdateSpy(&ldm, &LiveDataManager::arrivalUpdated);
QSignalSpy departureUpdateSpy(&ldm, &LiveDataManager::departureUpdated);
ldm.setPollingEnabled(false); // we don't want to trigger network requests here
QVERIFY(ldm.publicTransportManager());
ldm.m_unitTestTime = QDateTime({2017, 9, 10}, {12, 0}, QTimeZone("Europe/Zurich")); // that's in the middle of the first train leg
......@@ -117,11 +125,19 @@ private Q_SLOTS:
QCOMPARE(ldm.nextPollTimeForReservation(trainLeg1), 0); // no current data available, so we want to poll ASAP
QCOMPARE(ldm.nextPollTimeForReservation(trainLeg2), 0);
QCOMPARE(ldm.nextPollTime(), 0);
QCOMPARE(resMgr.reservation(trainLeg1).value<TrainReservation>().reservationFor().value<TrainTrip>().arrivalStation().address().addressLocality(), QString());
const auto leg1Arr = KPublicTransport::Stopover::fromJson(QJsonDocument::fromJson(readFile(s(SOURCE_DIR "/data/livedata/randa2017-leg1-arrival.json"))).object());
ldm.stopoverQueryFinished({ leg1Arr }, LiveData::Arrival, trainLeg1);
QCOMPARE(arrivalUpdateSpy.size(), 1);
QCOMPARE(arrivalUpdateSpy.at(0).at(0).toString(), trainLeg1);
QCOMPARE(departureUpdateSpy.size(), 0);
QCOMPARE(ldm.arrival(trainLeg1).arrivalDelay(), 2);
QCOMPARE(ldm.nextPollTimeForReservation(trainLeg1), 15 * 60 * 1000); // 15 min in msecs
// reservation was updated with additional location data
QCOMPARE(resChangeSpy.size(), 1);
QCOMPARE(resChangeSpy.at(0).at(0).toString(), trainLeg1);
QCOMPARE(resMgr.reservation(trainLeg1).value<TrainReservation>().reservationFor().value<TrainTrip>().arrivalStation().address().addressLocality(), QLatin1String("Visp"));
// verify this was persisted
{
......
......@@ -218,9 +218,14 @@ void LiveDataManager::updateStopoverData(const KPublicTransport::Stopover &stop,
ld.setTimestamp(type, now());
ld.store(resId);
// TODO update reservation with live data
// TODO emit update signals
type == LiveData::Arrival ? updateArrivalData(stop, resId) : updateDepartureData(stop, resId); // ### temporary
// update reservation with live data
const auto newRes = type == LiveData::Arrival ? PublicTransport::mergeArrival(res, stop) : PublicTransport::mergeDeparture(res, stop);
if (!ReservationHelper::equals(res, newRes)) {
m_resMgr->updateReservation(resId, newRes);
}
// emit update signals
emit type == LiveData::Arrival ? arrivalUpdated(resId) : departureUpdated(resId);
// check if we need to notify
if (!NotificationHelper::shouldNotify(oldStop, stop, type)) {
......@@ -239,48 +244,6 @@ void LiveDataManager::updateStopoverData(const KPublicTransport::Stopover &stop,
}
}
void LiveDataManager::updateArrivalData(const KPublicTransport::Departure &arr, const QString &resId)
{
// check if we can update static information in the reservation with what we received
const auto res = m_resMgr->reservation(resId);
if (JsonLd::isA<TrainReservation>(res)) {
auto newRes = res.value<TrainReservation>();
auto trip = res.value<TrainReservation>().reservationFor().value<TrainTrip>();
trip.setArrivalStation(PublicTransport::mergeStation(trip.arrivalStation(), arr.stopPoint()));
if (trip.arrivalPlatform().isEmpty() && !arr.scheduledPlatform().isEmpty()) {
trip.setArrivalPlatform(arr.scheduledPlatform());
}
newRes.setReservationFor(trip);
if (res.value<TrainReservation>() != newRes) {
m_resMgr->updateReservation(resId, newRes);
}
}
emit arrivalUpdated(resId);
}
void LiveDataManager::updateDepartureData(const KPublicTransport::Departure &dep, const QString &resId)
{
// check if we can update static information in the reservation with what we received
const auto res = m_resMgr->reservation(resId);
if (JsonLd::isA<TrainReservation>(res)) {
auto newRes = res.value<TrainReservation>();
auto trip = res.value<TrainReservation>().reservationFor().value<TrainTrip>();
trip.setDepartureStation(PublicTransport::mergeStation(trip.departureStation(), dep.stopPoint()));
if (trip.departurePlatform().isEmpty() && !dep.scheduledPlatform().isEmpty()) {
trip.setDeparturePlatform(dep.scheduledPlatform());
}
newRes.setReservationFor(trip);
if (res.value<TrainReservation>() != newRes) {
m_resMgr->updateReservation(resId, newRes);
}
}
emit departureUpdated(resId);
}
QDateTime LiveDataManager::departureTime(const QString &resId, const QVariant &res) const
{
if (JsonLd::isA<TrainReservation>(res)) {
......@@ -376,13 +339,13 @@ void LiveDataManager::batchChanged(const QString &resId)
const auto res = m_resMgr->reservation(resId);
const auto dataIt = m_data.find(resId);
if (dataIt != m_data.end()) {
if (!isDepartureForReservation(res, (*dataIt).departure)) {
if ((*dataIt).departureTimestamp.isValid() && !isDepartureForReservation(res, (*dataIt).departure)) {
(*dataIt).departure = {};
(*dataIt).departureTimestamp = {};
(*dataIt).store(resId, LiveData::Departure);
emit departureUpdated(resId);
}
if (!isArrivalForReservation(res, (*dataIt).arrival)) {
if ((*dataIt).arrivalTimestamp.isValid() && !isArrivalForReservation(res, (*dataIt).arrival)) {
(*dataIt).arrival = {};
(*dataIt).arrivalTimestamp = {};
(*dataIt).store(resId, LiveData::Arrival);
......
......@@ -20,8 +20,6 @@
#include "livedata.h"
#include <KPublicTransport/Departure>
#include <QDateTime>
#include <QHash>
#include <QObject>
......@@ -95,8 +93,6 @@ private:
void stopoverQueryFinished(std::vector<KPublicTransport::Stopover> &&result, LiveData::Type type, const QString &resId);
void updateStopoverData(const KPublicTransport::Stopover &stop, LiveData::Type type, const QString &resId, const QVariant &res);
void updateArrivalData(const KPublicTransport::Departure &arr, const QString &resId);
void updateDepartureData(const KPublicTransport::Departure &dep, const QString &resId);
/** Best known departure time. */
QDateTime departureTime(const QString &resId, const QVariant &res) const;
......
......@@ -27,6 +27,7 @@
#include <KPublicTransport/Journey>
#include <KPublicTransport/Line>
#include <KPublicTransport/Location>
#include <KPublicTransport/Stopover>
#include <KPublicTransport/StopoverRequest>
#include <QDateTime>
......@@ -149,12 +150,6 @@ QString PublicTransport::lineModeIcon(int lineMode)
return QStringLiteral("question");
}
KItinerary::TrainStation PublicTransport::mergeStation(const KItinerary::TrainStation &station, const KPublicTransport::Location &loc)
{
using namespace KItinerary;
return MergeUtil::merge(placeFromLocation<TrainStation>(loc), station).value<TrainStation>();
}
static KItinerary::Ticket clearSeat(KItinerary::Ticket ticket)
{
auto seat = ticket.ticketedSeat();
......@@ -234,6 +229,58 @@ QVariant PublicTransport::applyJourneySection(const QVariant &res, const KPublic
return res;
}
QVariant PublicTransport::mergeDeparture(const QVariant &res, const KPublicTransport::Stopover &dep)
{
using namespace KItinerary;
QVariant newRes;
if (isTrainMode(dep.route().line().mode())) {
TrainTrip trip;
trip.setDepartureStation(placeFromLocation<TrainStation>(dep.stopPoint()));
trip.setDepartureTime(dep.scheduledDepartureTime());
trip.setDeparturePlatform(dep.scheduledPlatform());
TrainReservation trainRes;
trainRes.setReservationFor(trip);
newRes = trainRes;
} else if (isBusMode(dep.route().line().mode())) {
BusTrip trip;
trip.setDepartureBusStop(placeFromLocation<BusStation>(dep.stopPoint()));
trip.setDepartureTime(dep.scheduledDepartureTime());
trip.setDeparturePlatform(dep.scheduledPlatform());
BusReservation busRes;
busRes.setReservationFor(trip);
newRes = busRes;
}
return MergeUtil::merge(newRes, res);
}
QVariant PublicTransport::mergeArrival(const QVariant &res, const KPublicTransport::Stopover &arr)
{
using namespace KItinerary;
QVariant newRes;
if (isTrainMode(arr.route().line().mode())) {
TrainTrip trip;
trip.setArrivalStation(placeFromLocation<TrainStation>(arr.stopPoint()));
trip.setArrivalTime(arr.scheduledArrivalTime());
trip.setArrivalPlatform(arr.scheduledPlatform());
TrainReservation trainRes;
trainRes.setReservationFor(trip);
newRes = trainRes;
} else if (isBusMode(arr.route().line().mode())) {
BusTrip trip;
trip.setArrivalBusStop(placeFromLocation<BusStation>(arr.stopPoint()));
trip.setArrivalTime(arr.scheduledArrivalTime());
trip.setArrivalPlatform(arr.scheduledPlatform());
BusReservation busRes;
busRes.setReservationFor(trip);
newRes = busRes;
}
return MergeUtil::merge(newRes, res);
}
bool PublicTransport::isSameMode(const QVariant &res, KPublicTransport::Line::Mode mode)
{
using namespace KPublicTransport;
......
......@@ -29,6 +29,7 @@
namespace KPublicTransport {
class JourneySection;
class Stopover;
class StopoverRequest;
}
......@@ -49,14 +50,8 @@ public:
template <typename T>
static T placeFromLocation(const KPublicTransport::Location &loc);
/** Merge information from @p location into the given train station.
* This assumes both sides refer to the same station, and @p loc merely provides additional information.
* In case of conflict @p station has precedence.
*/
static KItinerary::TrainStation mergeStation(const KItinerary::TrainStation &station, const KPublicTransport::Location &loc);
/** Applies information from @p location to the given KItinerary place.
* Unlike above, this does not assume both sides refer to the same location, and @p location has precedence.
* Unlike mergeStation(), this does not assume both sides refer to the same location, and @p location has precedence.
* Data from @p station is only considered if both sides refer to the same location.
*/
template <typename T>
......@@ -72,6 +67,13 @@ public:
*/
static QVariant applyJourneySection(const QVariant &res, const KPublicTransport::JourneySection &section);
/** Update a reservation from a KPublicTransport::Stopover.
* This assumes the stopover matches the departure/arrival of the reservation, and in case
* of conflict, the reservation is preferred.
*/
static QVariant mergeDeparture(const QVariant &res, const KPublicTransport::Stopover &dep);
static QVariant mergeArrival(const QVariant &res, const KPublicTransport::Stopover &arr);
/** Checks if the given reservation and journey section have a compatible mode of transportation. */
static bool isSameMode(const QVariant &res, KPublicTransport::Line::Mode mode);
static bool isSameMode(const QVariant &res, const KPublicTransport::JourneySection &section);
......
......@@ -41,3 +41,22 @@ std::pair<QString, QString> ReservationHelper::lineNameAndNumber(const QVariant
return {};
}
bool ReservationHelper::equals(const QVariant &lhs, const QVariant &rhs)
{
if (lhs.userType() != rhs.userType()) {
return false;
}
if (JsonLd::isA<TrainReservation>(lhs)) {
return lhs.value<TrainReservation>() == rhs.value<TrainReservation>();
}
if (JsonLd::isA<BusReservation>(lhs)) {
return lhs.value<BusReservation>() == rhs.value<BusReservation>();
}
if (JsonLd::isA<FlightReservation>(lhs)) {
return lhs.value<FlightReservation>() == rhs.value<FlightReservation>();
}
return false;
}
......@@ -27,6 +27,8 @@ class QVariant;
namespace ReservationHelper
{
std::pair<QString, QString> lineNameAndNumber(const QVariant &res);
bool equals(const QVariant &lhs, const QVariant &rhs);
}
#endif // RESERVATIONHELPER_H
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment