File indexing completed on 2025-03-09 04:45:38
0001 /* 0002 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "timelineelement.h" 0008 0009 #include "publictransport.h" 0010 #include "reservationmanager.h" 0011 #include "timelinemodel.h" 0012 #include "transfer.h" 0013 0014 #include <KItinerary/Event> 0015 #include <KItinerary/LocationUtil> 0016 #include <KItinerary/Place> 0017 #include <KItinerary/Reservation> 0018 #include <KItinerary/SortUtil> 0019 #include <KItinerary/Visit> 0020 0021 #include <KPublicTransport/Journey> 0022 #include <KPublicTransport/Stopover> 0023 0024 using namespace KItinerary; 0025 0026 static TimelineElement::ElementType elementType(const QVariant &res) 0027 { 0028 if (JsonLd::isA<FlightReservation>(res)) { return TimelineElement::Flight; } 0029 if (JsonLd::isA<LodgingReservation>(res)) { return TimelineElement::Hotel; } 0030 if (JsonLd::isA<TrainReservation>(res)) { return TimelineElement::TrainTrip; } 0031 if (JsonLd::isA<BusReservation>(res)) { return TimelineElement::BusTrip; } 0032 if (JsonLd::isA<BoatReservation>(res)) { return TimelineElement::BoatTrip; } 0033 if (JsonLd::isA<FoodEstablishmentReservation>(res)) { return TimelineElement::Restaurant; } 0034 if (JsonLd::isA<TouristAttractionVisit>(res)) { return TimelineElement::TouristAttraction; } 0035 if (JsonLd::isA<EventReservation>(res)) { return TimelineElement::Event; } 0036 if (JsonLd::isA<RentalCarReservation>(res)) { return TimelineElement::CarRental; } 0037 return {}; 0038 } 0039 0040 TimelineElement::TimelineElement() = default; 0041 0042 TimelineElement::TimelineElement(TimelineModel *model, TimelineElement::ElementType type, const QDateTime &dateTime, const QVariant &data) 0043 : dt(dateTime) 0044 , elementType(type) 0045 , m_content(data) 0046 , m_model(model) 0047 { 0048 } 0049 0050 TimelineElement::TimelineElement(TimelineModel *model, const QString& resId, const QVariant& res, TimelineElement::RangeType rt) 0051 : dt(relevantDateTime(res, rt)) 0052 , elementType(::elementType(res)) 0053 , rangeType(rt) 0054 , m_content(resId) 0055 , m_model(model) 0056 { 0057 } 0058 0059 TimelineElement::TimelineElement(TimelineModel *model, const ::Transfer &transfer) 0060 : dt(transfer.anchorTime()) 0061 , elementType(Transfer) 0062 , rangeType(SelfContained) 0063 , m_content(QVariant::fromValue(transfer)) 0064 , m_model(model) 0065 { 0066 } 0067 0068 static bool operator<(TimelineElement::RangeType lhs, TimelineElement::RangeType rhs) 0069 { 0070 static const int order_map[] = { 1, 0, 2 }; 0071 return order_map[lhs] < order_map[rhs]; 0072 } 0073 0074 bool TimelineElement::operator<(const TimelineElement &other) const 0075 { 0076 if (dt == other.dt) { 0077 if (rangeType == RangeEnd || other.rangeType == RangeEnd) { 0078 if (rangeType == RangeBegin || other.rangeType == RangeBegin) { 0079 return rangeType < other.rangeType; 0080 } 0081 return elementType > other.elementType; 0082 } 0083 return elementType < other.elementType; 0084 } 0085 return dt < other.dt; 0086 } 0087 0088 bool TimelineElement::operator<(const QDateTime &otherDt) const 0089 { 0090 return dt < otherDt; 0091 } 0092 0093 bool TimelineElement::operator==(const TimelineElement &other) const 0094 { 0095 if (elementType != other.elementType || rangeType != other.rangeType || dt != other.dt || batchId() != other.batchId()) { 0096 return false; 0097 } 0098 0099 switch (elementType) { 0100 case Transfer: 0101 { 0102 const auto lhsT = m_content.value<::Transfer>(); 0103 const auto rhsT = other.m_content.value<::Transfer>(); 0104 return lhsT.alignment() == rhsT.alignment(); 0105 } 0106 default: 0107 return true; 0108 } 0109 } 0110 0111 bool TimelineElement::isReservation() const 0112 { 0113 switch (elementType) { 0114 case Flight: 0115 case TrainTrip: 0116 case CarRental: 0117 case BusTrip: 0118 case BoatTrip: 0119 case Restaurant: 0120 case TouristAttraction: 0121 case Event: 0122 case Hotel: 0123 return true; 0124 case Undefined: 0125 case TodayMarker: 0126 case TripGroup: 0127 case WeatherForecast: 0128 case LocationInfo: 0129 case Transfer: 0130 return false; 0131 } 0132 0133 Q_UNREACHABLE(); 0134 return false; 0135 } 0136 0137 QString TimelineElement::batchId() const 0138 { 0139 if (isReservation()) { 0140 return m_content.toString(); 0141 } 0142 if (elementType == Transfer) { 0143 return m_content.value<::Transfer>().reservationId(); 0144 } 0145 return {}; 0146 } 0147 0148 QVariant TimelineElement::content() const 0149 { 0150 return m_content; 0151 } 0152 0153 void TimelineElement::setContent(const QVariant& content) 0154 { 0155 m_content = content; 0156 } 0157 0158 QDateTime TimelineElement::relevantDateTime(const QVariant &res, TimelineElement::RangeType range) 0159 { 0160 if (range == TimelineElement::RangeBegin || range == TimelineElement::SelfContained) { 0161 return SortUtil::startDateTime(res); 0162 } 0163 if (range == TimelineElement::RangeEnd) { 0164 return SortUtil::endDateTime(res); 0165 } 0166 0167 return {}; 0168 } 0169 0170 bool TimelineElement::isLocationChange() const 0171 { 0172 if (isReservation()) { 0173 // ### can be done without reservation lookup for some of the reservation element types 0174 const auto res = m_model->m_resMgr->reservation(batchId()); 0175 return LocationUtil::isLocationChange(res); 0176 } 0177 0178 if (elementType == Transfer) { 0179 return m_content.value<::Transfer>().state() == Transfer::Selected; 0180 } 0181 0182 return false; 0183 } 0184 0185 bool TimelineElement::isTimeBoxed() const 0186 { 0187 switch (elementType) { 0188 case Undefined: 0189 case TodayMarker: 0190 case TripGroup: 0191 case WeatherForecast: 0192 case LocationInfo: 0193 return false; 0194 case Transfer: 0195 return m_content.value<::Transfer>().state() == Transfer::Selected; 0196 case Flight: 0197 case TrainTrip: 0198 case BusTrip: 0199 case BoatTrip: 0200 return true; 0201 case Hotel: 0202 case CarRental: 0203 return false; 0204 case Restaurant: 0205 case TouristAttraction: 0206 case Event: 0207 return SortUtil::endDateTime(m_model->m_resMgr->reservation(batchId())).isValid(); 0208 } 0209 return false; 0210 } 0211 0212 bool TimelineElement::isInformational() const 0213 { 0214 switch (elementType) { 0215 case Undefined: 0216 case TodayMarker: 0217 case TripGroup: 0218 case WeatherForecast: 0219 case LocationInfo: 0220 return true; 0221 case Transfer: 0222 case Flight: 0223 case TrainTrip: 0224 case BusTrip: 0225 case BoatTrip: 0226 case Hotel: 0227 case CarRental: 0228 case Restaurant: 0229 case TouristAttraction: 0230 case Event: 0231 return false; 0232 } 0233 Q_UNREACHABLE(); 0234 } 0235 0236 bool TimelineElement::isCanceled() const 0237 { 0238 if (isReservation()) { 0239 const auto res = m_model->m_resMgr->reservation(batchId()); 0240 return JsonLd::canConvert<Reservation>(res) && JsonLd::convert<Reservation>(res).reservationStatus() == Reservation::ReservationCancelled; 0241 } 0242 0243 // TODO transfers with Disruption::NoService 0244 return false; 0245 } 0246 0247 static KPublicTransport::Location destinationOfJourney(const KPublicTransport::Journey &jny) 0248 { 0249 if (jny.sections().empty()) { 0250 return {}; 0251 } 0252 const auto §ions = jny.sections(); 0253 auto loc = sections.back().arrival().stopPoint(); 0254 0255 if (sections.size() == 1 || sections.back().mode() != KPublicTransport::JourneySection::Walking || sections.back().distance() > 1000) { 0256 return loc; 0257 } 0258 0259 // the last section is a short walk, its arrival location might not have all the data we have on the previous stop 0260 const auto prevLoc = sections[sections.size() - 2].arrival().stopPoint(); 0261 const auto locName = loc.name(); 0262 0263 // "anonymous" location with the coordinates as name 0264 if (std::none_of(locName.begin(), locName.end(), [](QChar c) { return c.isLetter(); }) && !prevLoc.name().isEmpty()) { 0265 return prevLoc; 0266 } 0267 0268 // propagate address information from the last stop to the final destination 0269 if (loc.locality().isEmpty()) { loc.setLocality(prevLoc.locality()); } 0270 if (loc.region().isEmpty()) { loc.setRegion(prevLoc.region()); } 0271 if (loc.country().isEmpty()) { loc.setLocality(prevLoc.country()); } 0272 0273 return loc; 0274 } 0275 0276 QVariant TimelineElement::destination() const 0277 { 0278 if (isReservation()) { 0279 const auto res = m_model->m_resMgr->reservation(batchId()); 0280 if (LocationUtil::isLocationChange(res)) { 0281 return LocationUtil::arrivalLocation(res); 0282 } 0283 return LocationUtil::location(res); 0284 } 0285 0286 if (elementType == Transfer) { 0287 const auto transfer = m_content.value<::Transfer>(); 0288 if (transfer.state() == Transfer::Selected) { 0289 const auto loc = destinationOfJourney(transfer.journey()); 0290 return loc.isEmpty() ? QVariant() : PublicTransport::placeFromLocation<KItinerary::Place>(loc); 0291 } 0292 } 0293 0294 return {}; 0295 } 0296 0297 QDateTime TimelineElement::endDateTime() const 0298 { 0299 if (isReservation()) { 0300 const auto res = m_model->m_resMgr->reservation(batchId()); 0301 return SortUtil::endDateTime(res); 0302 } 0303 0304 if (elementType == Transfer) { 0305 const auto transfer = m_content.value<::Transfer>(); 0306 if (transfer.state() == Transfer::Selected) { 0307 return transfer.journey().scheduledArrivalTime(); 0308 } 0309 } 0310 0311 return {}; 0312 } 0313 0314 #include "moc_timelineelement.cpp"