File indexing completed on 2025-01-19 04:46:50
0001 /* 0002 SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "itinerarymemento.h" 0008 0009 #include <CalendarSupport/CalendarSingleton> 0010 0011 #include <KItinerary/CalendarHandler> 0012 #include <KItinerary/ExtractorValidator> 0013 #include <KItinerary/Flight> 0014 #include <KItinerary/MergeUtil> 0015 #include <KItinerary/Reservation> 0016 #include <KItinerary/SortUtil> 0017 0018 #include <KPkPass/Pass> 0019 0020 #include <KMime/ContentIndex> 0021 0022 using namespace KItinerary; 0023 0024 ItineraryMemento::ItineraryMemento() 0025 { 0026 m_postProc.setValidationEnabled(false); 0027 } 0028 0029 void ItineraryMemento::detach() 0030 { 0031 } 0032 0033 const char *ItineraryMemento::identifier() 0034 { 0035 return "org.kde.messageviewer.itineraryData"; 0036 } 0037 0038 bool ItineraryMemento::isParsed(const KMime::ContentIndex &index) const 0039 { 0040 return m_parsedParts.contains(index); 0041 } 0042 0043 void ItineraryMemento::setParsed(const KMime::ContentIndex &index) 0044 { 0045 m_parsedParts.insert(index); 0046 } 0047 0048 void ItineraryMemento::setMessageDate(const QDateTime &contextDt) 0049 { 0050 m_postProc.setContextDate(contextDt); 0051 } 0052 0053 void ItineraryMemento::appendData(const QList<QVariant> &data) 0054 { 0055 m_postProc.process(data); 0056 } 0057 0058 bool ItineraryMemento::hasData() const 0059 { 0060 return !m_data.isEmpty() || !m_postProc.result().isEmpty(); 0061 } 0062 0063 QList<ItineraryMemento::TripData> ItineraryMemento::data() 0064 { 0065 if (m_data.isEmpty() && !m_postProc.result().isEmpty()) { 0066 // filter out types we can't handle, but keep incomplete elements to see if we can complete them from the calendar 0067 ExtractorValidator validator; 0068 validator.setAcceptedTypes<BoatReservation, 0069 BusReservation, 0070 EventReservation, 0071 FlightReservation, 0072 FoodEstablishmentReservation, 0073 LodgingReservation, 0074 RentalCarReservation, 0075 TaxiReservation, 0076 TrainReservation>(); 0077 validator.setAcceptOnlyCompleteElements(false); 0078 auto postProcResult = m_postProc.result(); 0079 postProcResult.erase(std::remove_if(postProcResult.begin(), 0080 postProcResult.end(), 0081 [&validator](const auto &elem) { 0082 return !validator.isValidElement(elem); 0083 }), 0084 postProcResult.end()); 0085 0086 // perform calendar lookup and merge results 0087 std::vector<std::pair<QVariant, KCalendarCore::Event::Ptr>> resolvedEvents; 0088 resolvedEvents.reserve(postProcResult.size()); 0089 const auto calendar = CalendarSupport::calendarSingleton(!qEnvironmentVariableIsSet("BPF_ITINERARY_TESTMODE")); 0090 for (const auto &r : std::as_const(postProcResult)) { 0091 const auto events = CalendarHandler::findEvents(calendar, r); 0092 if (events.empty()) { 0093 resolvedEvents.emplace_back(r, KCalendarCore::Event::Ptr()); 0094 continue; 0095 } 0096 0097 for (const auto &event : events) { 0098 const auto existingRes = CalendarHandler::reservationsForEvent(event); 0099 for (const auto &ev : existingRes) { 0100 if (MergeUtil::isSame(ev, r)) { 0101 resolvedEvents.emplace_back(MergeUtil::merge(ev, r), event); 0102 } 0103 } 0104 } 0105 } 0106 0107 // discard elemnents we couldn't complete from the calendar 0108 validator.setAcceptOnlyCompleteElements(true); 0109 resolvedEvents.erase(std::remove_if(resolvedEvents.begin(), 0110 resolvedEvents.end(), 0111 [&validator](const auto &p) { 0112 return !validator.isValidElement(p.first); 0113 }), 0114 resolvedEvents.end()); 0115 0116 // merge multi-traveler elements 0117 for (auto it = resolvedEvents.begin(); it != resolvedEvents.end();) { 0118 TripData data; 0119 data.reservations.push_back((*it).first); 0120 data.event = (*it).second; 0121 data.expanded = false; 0122 ++it; 0123 0124 if (JsonLd::canConvert<Reservation>(data.reservations.at(0))) { 0125 for (; it != resolvedEvents.end(); ++it) { 0126 if (!MergeUtil::isSameIncidence(data.reservations.at(0), (*it).first)) { 0127 break; 0128 } 0129 data.reservations.push_back((*it).first); 0130 } 0131 0132 // add other traveler we already know about from previous data in the calendar 0133 // but that aren't in the currently extracted data set 0134 if (data.event) { 0135 const auto reservationsForEvent = CalendarHandler::reservationsForEvent(data.event); 0136 for (const auto &prev : reservationsForEvent) { 0137 const auto notFound = std::find_if(data.reservations.constBegin(), 0138 data.reservations.constEnd(), 0139 [prev](const QVariant &v) { 0140 return MergeUtil::isSame(v, prev); 0141 }) 0142 == data.reservations.constEnd(); 0143 if (notFound) { 0144 data.reservations.push_back(prev); 0145 } 0146 } 0147 } 0148 } 0149 0150 m_data.push_back(data); 0151 } 0152 } 0153 0154 return m_data; 0155 } 0156 0157 void ItineraryMemento::toggleExpanded(int index) 0158 { 0159 if (index >= m_data.size()) { 0160 return; 0161 } 0162 m_data[index].expanded = !m_data.at(index).expanded; 0163 } 0164 0165 void ItineraryMemento::addPass(KPkPass::Pass *pass, const QByteArray &rawData) 0166 { 0167 m_passes.emplace_back(PassData{pass->passTypeIdentifier(), pass->serialNumber(), rawData}); 0168 } 0169 0170 QByteArray ItineraryMemento::rawPassData(const QString &passTypeIdentifier, const QString &serialNumber) const 0171 { 0172 for (const auto &pass : m_passes) { 0173 if (pass.passTypeIdentifier == passTypeIdentifier && pass.serialNumber == serialNumber) { 0174 return pass.rawData; 0175 } 0176 } 0177 return {}; 0178 } 0179 0180 void ItineraryMemento::addDocument(const QString &docId, const QVariant &docInfo, const QByteArray &docData) 0181 { 0182 m_docs.push_back({docId, docInfo, docData}); 0183 } 0184 0185 bool ItineraryMemento::canAddToCalendar() const 0186 { 0187 for (const auto &d : m_data) { 0188 if (JsonLd::isA<FlightReservation>(d.reservations.at(0))) { 0189 const auto f = d.reservations.at(0).value<FlightReservation>().reservationFor().value<Flight>(); 0190 if (f.departureTime().isValid() && f.arrivalTime().isValid()) { 0191 return true; 0192 } 0193 continue; 0194 } else if (SortUtil::startDateTime(d.reservations.at(0)).isValid()) { 0195 return true; 0196 } 0197 } 0198 return false; 0199 } 0200 0201 QDate ItineraryMemento::startDate() const 0202 { 0203 for (const auto &d : m_data) { 0204 const auto dt = SortUtil::startDateTime(d.reservations.at(0)); 0205 if (dt.isValid()) { 0206 return dt.date(); 0207 } 0208 } 0209 return {}; 0210 }