File indexing completed on 2024-05-12 04:42:45

0001 /*
0002     SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "stopover.h"
0008 #include "datatypes_p.h"
0009 #include "json_p.h"
0010 #include "loadutil_p.h"
0011 #include "mergeutil_p.h"
0012 #include "notesutil_p.h"
0013 #include "platformutils_p.h"
0014 #include "stopoverutil_p.h"
0015 
0016 #include <QDateTime>
0017 #include <QDebug>
0018 
0019 using namespace KPublicTransport;
0020 
0021 namespace KPublicTransport {
0022 class StopoverPrivate : public QSharedData {
0023 public:
0024     Disruption::Effect disruptionEffect = Disruption::NormalService;
0025     QDateTime scheduledArrivalTime;
0026     QDateTime expectedArrivalTime;
0027     QDateTime scheduledDepartureTime;
0028     QDateTime expectedDepartureTime;
0029     QString scheduledPlatform;
0030     QString expectedPlatform;
0031     Route route;
0032     Location stopPoint;
0033     QStringList notes;
0034     std::vector<LoadInfo> loadInformation;
0035     Vehicle vehicleLayout;
0036     Platform platformLayout;
0037 };
0038 }
0039 
0040 KPUBLICTRANSPORT_MAKE_GADGET(Stopover)
0041 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, scheduledArrivalTime, setScheduledArrivalTime)
0042 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, expectedArrivalTime, setExpectedArrivalTime)
0043 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, scheduledDepartureTime, setScheduledDepartureTime)
0044 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, expectedDepartureTime, setExpectedDepartureTime)
0045 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Route, route, setRoute)
0046 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Location, stopPoint, setStopPoint)
0047 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Disruption::Effect, disruptionEffect, setDisruptionEffect)
0048 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QStringList, notes, setNotes)
0049 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Vehicle, vehicleLayout, setVehicleLayout)
0050 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Platform, platformLayout, setPlatformLayout)
0051 
0052 bool Stopover::hasExpectedArrivalTime() const
0053 {
0054     return d->expectedArrivalTime.isValid();
0055 }
0056 
0057 int Stopover::arrivalDelay() const
0058 {
0059     if (hasExpectedArrivalTime()) {
0060         return d->scheduledArrivalTime.secsTo(d->expectedArrivalTime) / 60;
0061     }
0062     return 0;
0063 }
0064 
0065 bool Stopover::hasExpectedDepartureTime() const
0066 {
0067     return d->expectedDepartureTime.isValid();
0068 }
0069 
0070 int Stopover::departureDelay() const
0071 {
0072     if (hasExpectedDepartureTime()) {
0073         return d->scheduledDepartureTime.secsTo(d->expectedDepartureTime) / 60;
0074     }
0075     return 0;
0076 }
0077 
0078 QString Stopover::scheduledPlatform() const
0079 {
0080     return d->scheduledPlatform;
0081 }
0082 
0083 void Stopover::setScheduledPlatform(const QString &platform)
0084 {
0085     d.detach();
0086     d->scheduledPlatform = PlatformUtils::normalizePlatform(platform);
0087 }
0088 
0089 QString Stopover::expectedPlatform() const
0090 {
0091     return d->expectedPlatform;
0092 }
0093 
0094 void Stopover::setExpectedPlatform(const QString &platform)
0095 {
0096     d.detach();
0097     d->expectedPlatform = PlatformUtils::normalizePlatform(platform);
0098 }
0099 
0100 bool Stopover::hasExpectedPlatform() const
0101 {
0102     return !d->expectedPlatform.isEmpty();
0103 }
0104 
0105 bool Stopover::platformChanged() const
0106 {
0107     return PlatformUtils::platformChanged(d->scheduledPlatform, d->expectedPlatform);
0108 }
0109 
0110 void Stopover::addNote(const QString &note)
0111 {
0112     const auto n = NotesUtil::normalizeNote(note);
0113     const auto idx = NotesUtil::needsAdding(d->notes, n);
0114     if (idx >= 0) {
0115         d.detach();
0116         NotesUtil::performAdd(d->notes, n, idx);
0117     }
0118 }
0119 
0120 void Stopover::addNotes(const QStringList &notes)
0121 {
0122     for (const auto &n : notes) {
0123         addNote(n);
0124     }
0125 }
0126 
0127 const std::vector<LoadInfo>& Stopover::loadInformation() const
0128 {
0129     return d->loadInformation;
0130 }
0131 
0132 std::vector<LoadInfo>&& Stopover::takeLoadInformation()
0133 {
0134     d.detach();
0135     return std::move(d->loadInformation);
0136 }
0137 
0138 void Stopover::setLoadInformation(std::vector<LoadInfo> &&loadInfo)
0139 {
0140     d.detach();
0141     d->loadInformation = std::move(loadInfo);
0142 }
0143 
0144 QVariantList Stopover::loadInformationVariant() const
0145 {
0146     QVariantList l;
0147     l.reserve(d->loadInformation.size());
0148     std::transform(d->loadInformation.begin(), d->loadInformation.end(), std::back_inserter(l), [](const auto &load) { return QVariant::fromValue(load); });
0149     return l;
0150 }
0151 
0152 void Stopover::applyMetaData(bool download)
0153 {
0154     auto line = d->route.line();
0155     line.applyMetaData(stopPoint(), download);
0156     d->route.setLine(line);
0157 }
0158 
0159 bool Stopover::isSame(const Stopover &lhs, const Stopover &rhs)
0160 {
0161     // same time is mandatory
0162     const auto departureTimeMatch = lhs.scheduledDepartureTime().isValid()
0163         && rhs.scheduledDepartureTime().isValid()
0164         && MergeUtil::distance(lhs.scheduledDepartureTime(), rhs.scheduledDepartureTime()) < 60;
0165     const auto arrivalTimeMatch = lhs.scheduledArrivalTime().isValid()
0166         && rhs.scheduledArrivalTime().isValid()
0167         && MergeUtil::distance(lhs.scheduledArrivalTime(), rhs.scheduledArrivalTime()) < 60;
0168     if (!departureTimeMatch && !arrivalTimeMatch) {
0169         return false;
0170     }
0171 
0172     // same route would be sufficient, if that's not the case, look for other hints
0173     // this might be the same below
0174     if (Route::isSame(lhs.route(), rhs.route())) {
0175         return true;
0176     }
0177 
0178     // different platform can't be the same train
0179     if (!lhs.scheduledPlatform().isEmpty() && !rhs.scheduledPlatform().isEmpty() && lhs.scheduledPlatform() != rhs.scheduledPlatform()) {
0180         return false;
0181     }
0182 
0183     // same destination and departure time is likely the same route after all
0184     // TODO we should check for conflicting line names or train types here maybe?
0185     return (!lhs.route().destination().isEmpty() && !rhs.route().destination().isEmpty() && Location::isSame(lhs.route().destination(), rhs.route().destination()))
0186         || Location::isSameName(lhs.route().direction(), rhs.route().direction());
0187 }
0188 
0189 Stopover Stopover::merge(const Stopover &lhs, const Stopover &rhs)
0190 {
0191     auto stopover = lhs;
0192 
0193     using namespace MergeUtil;
0194     stopover.setScheduledDepartureTime(mergeDateTimeEqual(lhs.scheduledDepartureTime(), rhs.scheduledDepartureTime()));
0195     stopover.setExpectedDepartureTime(mergeDateTimeMax(lhs.expectedDepartureTime(), rhs.expectedDepartureTime()));
0196     stopover.setScheduledArrivalTime(mergeDateTimeEqual(lhs.scheduledArrivalTime(), rhs.scheduledArrivalTime()));
0197     stopover.setExpectedArrivalTime(mergeDateTimeMax(lhs.expectedArrivalTime(), rhs.expectedArrivalTime()));
0198 
0199     if (stopover.scheduledPlatform().isEmpty() && !rhs.scheduledPlatform().isEmpty()) {
0200         stopover.setScheduledPlatform(rhs.scheduledPlatform());
0201     }
0202     if (!stopover.hasExpectedPlatform() && rhs.hasExpectedPlatform()) {
0203         stopover.setExpectedPlatform(rhs.expectedPlatform());
0204     }
0205 
0206     stopover.setRoute(Route::merge(lhs.route(), rhs.route()));
0207     stopover.setStopPoint(Location::merge(lhs.stopPoint(), rhs.stopPoint()));
0208     stopover.setDisruptionEffect(std::max(lhs.disruptionEffect(), rhs.disruptionEffect()));
0209     stopover.setNotes(NotesUtil::mergeNotes(lhs.notes(), rhs.notes()));
0210     stopover.d->loadInformation = LoadUtil::merge(lhs.d->loadInformation, rhs.d->loadInformation);
0211     stopover.d->vehicleLayout = Vehicle::merge(lhs.d->vehicleLayout, rhs.d->vehicleLayout);
0212     stopover.d->platformLayout = Platform::merge(lhs.d->platformLayout, rhs.d->platformLayout);
0213     return stopover;
0214 }
0215 
0216 QJsonObject Stopover::toJson(const Stopover &stopover)
0217 {
0218     auto obj = Json::toJson(stopover);
0219     const auto routeObj = Route::toJson(stopover.route());
0220     if (!routeObj.empty()) {
0221         obj.insert(QLatin1String("route"), routeObj);
0222     }
0223     const auto locObj = Location::toJson(stopover.stopPoint());
0224     if (!locObj.empty()) {
0225         obj.insert(QLatin1String("stopPoint"), locObj);
0226     }
0227     if (!stopover.loadInformation().empty()) {
0228         obj.insert(QLatin1String("load"), LoadInfo::toJson(stopover.loadInformation()));
0229     }
0230     if (!stopover.vehicleLayout().isEmpty()) {
0231         obj.insert(QLatin1String("vehicleLayout"), Vehicle::toJson(stopover.vehicleLayout()));
0232     }
0233     if (!stopover.platformLayout().isEmpty()) {
0234         obj.insert(QLatin1String("platformLayout"), Platform::toJson(stopover.platformLayout()));
0235     }
0236 
0237     if (obj.size() == 1) { // only the disruption enum, ie. this is an empty object
0238         return {};
0239     }
0240     return obj;
0241 }
0242 
0243 QJsonArray Stopover::toJson(const std::vector<Stopover> &deps)
0244 {
0245     return Json::toJson(deps);
0246 }
0247 
0248 Stopover Stopover::fromJson(const QJsonObject &obj)
0249 {
0250     auto stopover = Json::fromJson<Stopover>(obj);
0251     stopover.setRoute(Route::fromJson(obj.value(QLatin1String("route")).toObject()));
0252     stopover.setStopPoint(Location::fromJson(obj.value(QLatin1String("stopPoint")).toObject()));
0253     stopover.setLoadInformation(LoadInfo::fromJson(obj.value(QLatin1String("load")).toArray()));
0254     stopover.setVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("vehicleLayout")).toObject()));
0255     stopover.setPlatformLayout(Platform::fromJson(obj.value(QLatin1String("platformLayout")).toObject()));
0256     stopover.applyMetaData(false);
0257     return stopover;
0258 }
0259 
0260 std::vector<Stopover> Stopover::fromJson(const QJsonArray &array)
0261 {
0262     return Json::fromJson<Stopover>(array);
0263 }
0264 
0265 #include "moc_stopover.cpp"