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 }