Warning, file /pim/kitinerary/src/lib/calendarhandler.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002    SPDX-FileCopyrightText: 2017 Volker Krause <vkrause@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "config-kitinerary.h"
0008 #include "calendarhandler.h"
0009 #include "jsonlddocument.h"
0010 #include "locationutil_p.h"
0011 #include "logging.h"
0012 #include "mergeutil.h"
0013 #include "sortutil.h"
0014 
0015 #include <KItinerary/BoatTrip>
0016 #include <KItinerary/BusTrip>
0017 #include <KItinerary/Event>
0018 #include <KItinerary/Flight>
0019 #include <KItinerary/Organization>
0020 #include <KItinerary/Person>
0021 #include <KItinerary/Place>
0022 #include <KItinerary/RentalCar>
0023 #include <KItinerary/Reservation>
0024 #include <KItinerary/Ticket>
0025 #include <KItinerary/TrainTrip>
0026 #include <KItinerary/Visit>
0027 
0028 #include <KCalendarCore/Alarm>
0029 #include <KCalendarCore/Calendar>
0030 #include <KCalendarCore/Event>
0031 
0032 #include <KContacts/Address>
0033 
0034 #include <KLocalizedString>
0035 
0036 #include <QDateTime>
0037 #include <QJsonArray>
0038 #include <QJsonDocument>
0039 
0040 using namespace KItinerary;
0041 
0042 static QString formatAddress(const PostalAddress &addr)
0043 {
0044     return LocationUtil::toAddress(addr).formatted(KContacts::AddressFormatStyle::MultiLineInternational);
0045 }
0046 
0047 static QString formatAddressSingleLine(const PostalAddress &addr)
0048 {
0049     return LocationUtil::toAddress(addr).formatted(KContacts::AddressFormatStyle::SingleLineInternational);
0050 }
0051 
0052 using namespace KCalendarCore;
0053 static void fillFlightReservation(const QList<QVariant> &reservations,
0054                                   const KCalendarCore::Event::Ptr &event);
0055 static void fillTrainReservation(const TrainReservation &reservation, const KCalendarCore::Event::Ptr &event);
0056 static void fillBusReservation(const BusReservation &reservation, const KCalendarCore::Event::Ptr &event);
0057 static void fillBoatReservation(const BoatReservation &reservation, const KCalendarCore::Event::Ptr &event);
0058 static void fillLodgingReservation(const QList<QVariant> &reservations,
0059                                    const KCalendarCore::Event::Ptr &event);
0060 static void fillEvent(const KItinerary::Event &ev, const KCalendarCore::Event::Ptr &event);
0061 static void fillEventReservation(const QList<QVariant> &reservations,
0062                                  const KCalendarCore::Event::Ptr &event);
0063 static void fillGeoPosition(const QVariant &place, const KCalendarCore::Event::Ptr &event);
0064 static void fillFoodReservation(const FoodEstablishmentReservation &reservation, const KCalendarCore::Event::Ptr &event);
0065 static void fillRentalCarReservation(const RentalCarReservation &reservation, const KCalendarCore::Event::Ptr &event);
0066 static void fillTaxiReservation(const TaxiReservation &reservation, const KCalendarCore::Event::Ptr &event);
0067 
0068 QList<QSharedPointer<KCalendarCore::Event>> CalendarHandler::findEvents(
0069     const QSharedPointer<KCalendarCore::Calendar> &calendar,
0070     const QVariant &reservation) {
0071     return findEvents(calendar.data(), reservation);
0072 }
0073 
0074 QList<QSharedPointer<KCalendarCore::Event>>
0075 CalendarHandler::findEvents(KCalendarCore::Calendar *calendar,
0076                             const QVariant &reservation) {
0077     if (!(JsonLd::canConvert<Reservation>(reservation) || JsonLd::canConvert<KItinerary::Event>(reservation)) || !calendar) {
0078         return {};
0079     }
0080 
0081     QList<KCalendarCore::Event::Ptr> events;
0082     QList<KCalendarCore::Event::Ptr> results;
0083     const auto startDt = SortUtil::startDateTime(reservation);
0084     const auto endDt = SortUtil::endDateTime(reservation);
0085     if (startDt.isValid() && endDt.isValid() && startDt == startDt.date().startOfDay(startDt.timeZone())
0086         && std::abs(endDt.secsTo(endDt.date().endOfDay(endDt.timeZone()))) <= 1) {
0087         // looks like an all day event, don't adjust for timezones in that case
0088         events = calendar->events(startDt.date());
0089     } else if (startDt.isValid()) {
0090         // we know the exact day to search at
0091         events = calendar->events(startDt.toTimeZone(calendar->timeZone()).date());
0092     } else if (JsonLd::canConvert<Reservation>(reservation)) {
0093         // for minimal cancellations, we need to search in a larger range
0094         const auto res = JsonLd::convert<Reservation>(reservation);
0095         if (!res.modifiedTime().isValid() || res.reservationStatus() != Reservation::ReservationCancelled) {
0096             return {};
0097         }
0098         const auto date = res.modifiedTime().toTimeZone(calendar->timeZone()).date();
0099         events = calendar->events(date, date.addDays(180));
0100     }
0101 
0102     for (const auto &event : events) {
0103       if (!event->uid().startsWith(QLatin1StringView("KIT-"))) {
0104         continue;
0105       }
0106         const auto otherRes = CalendarHandler::reservationsForEvent(event);
0107         for (const auto &other : otherRes) {
0108             if (MergeUtil::isSame(other, reservation)) {
0109                 results.push_back(event);
0110             }
0111         }
0112     }
0113 
0114     return results;
0115 }
0116 
0117 QList<QVariant> CalendarHandler::reservationsForEvent(
0118     const QSharedPointer<KCalendarCore::Event> &event) {
0119     const auto payload = event->customProperty("KITINERARY", "RESERVATION").toUtf8();
0120     const auto json = QJsonDocument::fromJson(payload).array();
0121     return JsonLdDocument::fromJson(json);
0122 }
0123 
0124 bool CalendarHandler::canCreateEvent(const QVariant &reservation)
0125 {
0126     if (JsonLd::isA<FlightReservation>(reservation)) {
0127         const auto f = reservation.value<FlightReservation>().reservationFor().value<Flight>();
0128         if (f.departureTime().isValid() && f.arrivalTime().isValid()) {
0129             return true;
0130         }
0131     }
0132     return SortUtil::startDateTime(reservation).isValid();
0133 }
0134 
0135 void CalendarHandler::fillEvent(
0136     const QList<QVariant> &reservations,
0137     const QSharedPointer<KCalendarCore::Event> &event) {
0138     if (reservations.isEmpty()) {
0139         return;
0140     }
0141 
0142     // TODO pass reservationS into all functions below for multi-traveler support
0143     const auto &reservation = reservations.at(0);
0144     const int typeId = reservation.userType();
0145     if (typeId == qMetaTypeId<FlightReservation>()) {
0146         fillFlightReservation(reservations, event);
0147     } else if (typeId == qMetaTypeId<LodgingReservation>()) {
0148         fillLodgingReservation(reservations, event);
0149     } else if (typeId == qMetaTypeId<TrainReservation>()) {
0150         fillTrainReservation(reservation.value<TrainReservation>(), event);
0151     } else if (typeId == qMetaTypeId<BusReservation>()) {
0152         fillBusReservation(reservation.value<BusReservation>(), event);
0153     } else if (typeId == qMetaTypeId<BoatReservation>()) {
0154         fillBoatReservation(reservation.value<BoatReservation>(), event);
0155     } else if (JsonLd::isA<EventReservation>(reservation)) {
0156         fillEventReservation(reservations, event);
0157     } else if  (JsonLd::isA<Event>(reservation)) {
0158         fillEvent(reservation.value<KItinerary::Event>(), event);
0159     } else if (JsonLd::isA<FoodEstablishmentReservation>(reservation)) {
0160         fillFoodReservation(reservation.value<FoodEstablishmentReservation>(), event);
0161     } else if (JsonLd::isA<RentalCarReservation>(reservation)) {
0162         fillRentalCarReservation(reservation.value<RentalCarReservation>(), event);
0163     } else if (JsonLd::isA<TaxiReservation>(reservation)) {
0164         fillTaxiReservation(reservation.value<TaxiReservation>(), event);
0165     } else {
0166         return;
0167     }
0168 
0169     if (JsonLd::canConvert<Reservation>(reservation) && JsonLd::convert<Reservation>(reservation).reservationStatus() == Reservation::ReservationCancelled) {
0170         event->setTransparency(KCalendarCore::Event::Transparent);
0171         event->setSummary(i18nc("canceled train/flight/loading reservation", "Canceled: %1", event->summary()));
0172         event->clearAlarms();
0173     }
0174 
0175     if (!event->uid().startsWith(QLatin1StringView("KIT-"))) {
0176       event->setUid(QLatin1StringView("KIT-") + event->uid());
0177     }
0178 
0179     const auto payload = QJsonDocument(JsonLdDocument::toJson(reservations)).toJson(QJsonDocument::Compact);
0180     event->setCustomProperty("KITINERARY", "RESERVATION", QString::fromUtf8(payload));
0181 }
0182 
0183 static QString airportDisplayCode(const Airport &airport)
0184 {
0185     return airport.iataCode().isEmpty() ? airport.name() : airport.iataCode();
0186 }
0187 
0188 static void fillFlightReservation(const QList<QVariant> &reservations,
0189                                   const KCalendarCore::Event::Ptr &event) {
0190     const auto flight = reservations.at(0).value<FlightReservation>().reservationFor().value<Flight>();
0191     const auto airline = flight.airline();
0192     const auto depPort = flight.departureAirport();
0193     const auto arrPort = flight.arrivalAirport();
0194 
0195     const QString flightNumber = airline.iataCode() + QLatin1Char(' ') + flight.flightNumber();
0196 
0197     event->setSummary(i18n("Flight %1 from %2 to %3", flightNumber, airportDisplayCode(depPort), airportDisplayCode(arrPort)));
0198     event->setLocation(depPort.name());
0199     fillGeoPosition(depPort, event);
0200     event->setDtStart(flight.departureTime());
0201     event->setDtEnd(flight.arrivalTime());
0202     event->setAllDay(false);
0203 
0204     const auto boardingTime = flight.boardingTime();
0205     const auto departureGate = flight.departureGate();
0206     if (boardingTime.isValid()) {
0207         const auto startOffset = Duration(event->dtStart(), boardingTime);
0208         const auto existinAlarms = event->alarms();
0209         const auto it = std::find_if(existinAlarms.begin(), existinAlarms.end(), [startOffset](const Alarm::Ptr &other) {
0210             return other->startOffset() == startOffset;
0211         });
0212         if (it == existinAlarms.end()) {
0213             Alarm::Ptr alarm(new Alarm(event.data()));
0214             alarm->setStartOffset(Duration(event->dtStart(), boardingTime));
0215             if (departureGate.isEmpty()) {
0216                 alarm->setDisplayAlarm(i18n("Boarding for flight %1", flightNumber));
0217             } else {
0218                 alarm->setDisplayAlarm(i18n("Boarding for flight %1 at gate %2", flightNumber, departureGate));
0219             }
0220             alarm->setEnabled(true);
0221             event->addAlarm(alarm);
0222         }
0223     }
0224 
0225     QStringList desc;
0226     if (boardingTime.isValid()) {
0227         desc.push_back(i18n("Boarding time: %1", QLocale().toString(boardingTime.time(), QLocale::ShortFormat)));
0228     }
0229     if (!departureGate.isEmpty()) {
0230         desc.push_back(i18nc("flight departure gate", "Departure gate: %1", departureGate));
0231     }
0232 
0233     for (const auto &r : reservations) {
0234         const auto reservation = r.value<FlightReservation>();
0235         const auto person = reservation.underName().value<KItinerary::Person>();
0236         if (!person.name().isEmpty()) {
0237             desc.push_back(person.name());
0238         }
0239         if (!reservation.boardingGroup().isEmpty()) {
0240             desc.push_back(i18n("Boarding group: %1", reservation.boardingGroup()));
0241         }
0242         if (!reservation.airplaneSeat().isEmpty()) {
0243             desc.push_back(i18n("Seat: %1", reservation.airplaneSeat()));
0244         }
0245         if (!reservation.reservationNumber().isEmpty()) {
0246             desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0247         }
0248     }
0249     event->setDescription(desc.join(QLatin1Char('\n')));
0250 }
0251 
0252 static void fillTrainReservation(const TrainReservation &reservation, const KCalendarCore::Event::Ptr &event)
0253 {
0254     const auto trip = reservation.reservationFor().value<TrainTrip>();
0255     const auto depStation = trip.departureStation();
0256     const auto arrStation = trip.arrivalStation();
0257 
0258     event->setSummary(i18n("Train %1 from %2 to %3", trip.trainNumber(), depStation.name(), arrStation.name()));
0259     event->setLocation(depStation.name());
0260     fillGeoPosition(depStation, event);
0261     event->setDtStart(trip.departureTime());
0262     event->setDtEnd(trip.arrivalTime());
0263     event->setAllDay(false);
0264 
0265     QStringList desc;
0266     if (!trip.departurePlatform().isEmpty()) {
0267         desc.push_back(i18n("Departure platform: %1", trip.departurePlatform()));
0268     }
0269     const auto ticket = reservation.reservedTicket().value<Ticket>();
0270     const auto seat = ticket.ticketedSeat();
0271     if (!seat.seatSection().isEmpty()) {
0272         desc.push_back(i18n("Coach: %1", seat.seatSection()));
0273     }
0274     if (!seat.seatNumber().isEmpty()) {
0275         desc.push_back(i18n("Seat: %1", seat.seatNumber()));
0276     }
0277     if (!trip.arrivalPlatform().isEmpty()) {
0278         desc.push_back(i18n("Arrival platform: %1", trip.arrivalPlatform()));
0279     }
0280     if (!reservation.reservationNumber().isEmpty()) {
0281         desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0282     }
0283     event->setDescription(desc.join(QLatin1Char('\n')));
0284 }
0285 
0286 static void fillBusReservation(const BusReservation &reservation, const KCalendarCore::Event::Ptr &event)
0287 {
0288     const auto trip = reservation.reservationFor().value<BusTrip>();
0289     const auto depStation = trip.departureBusStop();
0290     const auto arrStation = trip.arrivalBusStop();
0291 
0292     event->setSummary(i18n("Bus %1 from %2 to %3", trip.busNumber(), depStation.name(), arrStation.name()));
0293     event->setLocation(depStation.name());
0294     fillGeoPosition(depStation, event);
0295     event->setDtStart(trip.departureTime());
0296     event->setDtEnd(trip.arrivalTime());
0297     event->setAllDay(false);
0298 
0299     QStringList desc;
0300     const auto ticket = reservation.reservedTicket().value<Ticket>();
0301     const auto seat = ticket.ticketedSeat();
0302     if (!seat.seatNumber().isEmpty()) {
0303         desc.push_back(i18n("Seat: %1", seat.seatNumber()));
0304     }
0305     if (!reservation.reservationNumber().isEmpty()) {
0306         desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0307     }
0308     event->setDescription(desc.join(QLatin1Char('\n')));
0309 }
0310 
0311 static void fillBoatReservation(const KItinerary::BoatReservation &reservation, const KCalendarCore::Event::Ptr &event)
0312 {
0313     const auto trip = reservation.reservationFor().value<BoatTrip>();
0314     const auto depTerminal = trip.departureBoatTerminal();
0315     const auto arrTerminal = trip.arrivalBoatTerminal();
0316 
0317     event->setSummary(i18n("Ferry from %1 to %2", depTerminal.name(), arrTerminal.name()));
0318     event->setLocation(depTerminal.name());
0319     fillGeoPosition(depTerminal, event);
0320     event->setDtStart(trip.departureTime());
0321     event->setDtEnd(trip.arrivalTime());
0322     event->setAllDay(false);
0323 
0324     QStringList desc;
0325     const auto ticket = reservation.reservedTicket().value<Ticket>();
0326     if (!reservation.reservationNumber().isEmpty()) {
0327         desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0328     }
0329     if (!ticket.ticketNumber().isEmpty() && ticket.ticketNumber() != reservation.reservationNumber()) {
0330         desc.push_back(i18n("Ticket number: %1", ticket.ticketNumber()));
0331     }
0332     event->setDescription(desc.join(QLatin1Char('\n')));
0333 }
0334 
0335 static void fillLodgingReservation(const QList<QVariant> &reservations,
0336                                    const KCalendarCore::Event::Ptr &event) {
0337     const auto reservation = reservations.at(0).value<LodgingReservation>();
0338     const auto lodgingBusiness = reservation.reservationFor().value<LodgingBusiness>();
0339 
0340     event->setSummary(i18n("Hotel reservation: %1", lodgingBusiness.name()));
0341     event->setLocation(formatAddressSingleLine(lodgingBusiness.address()));
0342     fillGeoPosition(lodgingBusiness, event);
0343 
0344     event->setDtStart(QDateTime(reservation.checkinTime().date(), QTime()));
0345     event->setDtEnd(QDateTime(reservation.checkoutTime().date(), QTime()));
0346     event->setAllDay(true);
0347     event->setTransparency(KCalendarCore::Event::Transparent);
0348 
0349     QStringList desc;
0350     if (reservation.checkinTime().isValid()) {
0351         desc.push_back(i18n("Check-in: %1", QLocale().toString(reservation.checkinTime().time(), QLocale::ShortFormat)));
0352     }
0353     if (reservation.checkoutTime().isValid()) {
0354         desc.push_back(i18n("Check-out: %1", QLocale().toString(reservation.checkoutTime().time(), QLocale::ShortFormat)));
0355     }
0356     if (!lodgingBusiness.telephone().isEmpty()) {
0357         desc.push_back(i18n("Phone: %1", lodgingBusiness.telephone()));
0358     }
0359     if (!lodgingBusiness.email().isEmpty()) {
0360         desc.push_back(i18n("Email: %1", lodgingBusiness.email()));
0361     }
0362     if (!lodgingBusiness.url().isEmpty()) {
0363         desc.push_back(i18n("Website: %1", lodgingBusiness.url().toString()));
0364     }
0365 
0366     for (const auto &r : reservations) {
0367         const auto reservation = r.value<LodgingReservation>();
0368         const auto person = reservation.underName().value<KItinerary::Person>();
0369         if (!person.name().isEmpty()) {
0370             desc.push_back(person.name());
0371         }
0372         if (!reservation.reservationNumber().isEmpty()) {
0373             desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0374         }
0375     }
0376     event->setDescription(desc.join(QLatin1Char('\n')));
0377 }
0378 
0379 static void fillEvent(const KItinerary::Event &ev, const KCalendarCore::Event::Ptr &event)
0380 {
0381     Place location;
0382     if (JsonLd::canConvert<Place>(ev.location())) {
0383         location = JsonLd::convert<Place>(ev.location());
0384     }
0385 
0386     event->setSummary(ev.name());
0387     event->setLocation(location.name());
0388     fillGeoPosition(location, event);
0389     event->setDtStart(ev.startDate());
0390     if (ev.endDate().isValid()) {
0391         event->setDtEnd(ev.endDate());
0392         // QDate::endOfDay adds 999ms, while endDate has that truncated
0393         event->setAllDay(ev.startDate() == ev.startDate().date().startOfDay(ev.startDate().timeZone())
0394                      && std::abs(ev.endDate().secsTo(ev.endDate().date().endOfDay(ev.endDate().timeZone()))) <= 1);
0395         if (event->allDay()) {
0396             event->setDtStart(QDateTime(event->dtStart().date(), QTime()));
0397             event->setDtEnd(QDateTime(event->dtEnd().date(), QTime()));
0398         }
0399     } else {
0400         event->setDtEnd(ev.startDate().addSecs(3600));
0401         event->setAllDay(false);
0402     }
0403 
0404     if (ev.doorTime().isValid()) {
0405         const auto startOffset = Duration(event->dtStart(), ev.doorTime());
0406         const auto existinAlarms = event->alarms();
0407         const auto it = std::find_if(existinAlarms.begin(), existinAlarms.end(), [startOffset](const Alarm::Ptr &other) {
0408             return other->startOffset() == startOffset;
0409         });
0410         if (it == existinAlarms.end()) {
0411             Alarm::Ptr alarm(new Alarm(event.data()));
0412             alarm->setStartOffset(Duration(event->dtStart(), ev.doorTime()));
0413             alarm->setDisplayAlarm(i18n("Entrance for %1", ev.name()));
0414             alarm->setEnabled(true);
0415             event->addAlarm(alarm);
0416         }
0417     }
0418 
0419     event->setDescription(formatAddress(location.address()) + QLatin1Char('\n'));
0420 }
0421 
0422 static void fillEventReservation(const QList<QVariant> &reservations,
0423                                  const KCalendarCore::Event::Ptr &event) {
0424     const auto ev = reservations.at(0).value<EventReservation>().reservationFor().value<KItinerary::Event>();
0425     fillEvent(ev, event);
0426 
0427     QStringList desc = { event->description() };
0428     for (const auto &r : reservations) {
0429         const auto reservation = r.value<EventReservation>();
0430         const auto person = reservation.underName().value<KItinerary::Person>();
0431         if (!person.name().isEmpty()) {
0432             desc.push_back(person.name());
0433         }
0434         // TODO: add seat information if present
0435         if (!reservation.reservationNumber().isEmpty()) {
0436             desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0437         }
0438     }
0439     event->setDescription(desc.join(QLatin1Char('\n')));
0440 }
0441 
0442 static void fillGeoPosition(const QVariant &place, const KCalendarCore::Event::Ptr &event)
0443 {
0444     if (!JsonLd::canConvert<Place>(place)) {
0445         return;
0446     }
0447     const auto geo = JsonLd::convert<Place>(place).geo();
0448     if (!geo.isValid()) {
0449         return;
0450     }
0451 
0452     event->setGeoLatitude(geo.latitude());
0453     event->setGeoLongitude(geo.longitude());
0454 }
0455 
0456 static void fillFoodReservation(const FoodEstablishmentReservation &reservation, const KCalendarCore::Event::Ptr &event)
0457 {
0458     const auto foodEstablishment = reservation.reservationFor().value<FoodEstablishment>();
0459 
0460     event->setSummary(i18n("Restaurant reservation: %1", foodEstablishment.name()));
0461     event->setLocation(formatAddressSingleLine(foodEstablishment.address()));
0462     fillGeoPosition(foodEstablishment, event);
0463 
0464     event->setDtStart(reservation.startTime());
0465     auto endTime = reservation.endTime();
0466     if (!endTime.isValid()) {
0467         endTime = reservation.startTime().addSecs(7200); // if we have no end time, let's assume 2h
0468     }
0469     event->setDtEnd(endTime);
0470     event->setAllDay(false);
0471 
0472     QStringList desc;
0473     if (reservation.partySize() > 0) {
0474         desc.push_back(i18n("Number of people: %1", reservation.partySize()));
0475     }
0476     if (!reservation.reservationNumber().isEmpty()) {
0477         desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0478     }
0479     const auto person = reservation.underName().value<KItinerary::Person>();
0480     if (!person.name().isEmpty()) {
0481         desc.push_back(i18n("Under name: %1", person.name()));
0482     }
0483     event->setDescription(desc.join(QLatin1Char('\n')));
0484 }
0485 
0486 static void fillRentalCarReservation(const RentalCarReservation &reservation, const KCalendarCore::Event::Ptr &event)
0487 {
0488     const auto rentalCalPickup = reservation.pickupLocation();
0489     const auto addressPickUp = rentalCalPickup.address();
0490     const auto rentalCar = reservation.reservationFor().value<RentalCar>();
0491     event->setSummary(i18n("Rental car reservation: %1", rentalCar.name()));
0492     event->setLocation(rentalCalPickup.name());
0493     fillGeoPosition(rentalCalPickup, event);
0494 
0495     event->setDtStart(reservation.pickupTime());
0496     event->setDtEnd(reservation.dropoffTime());
0497     event->setAllDay(false);
0498     event->setTransparency(KCalendarCore::Event::Transparent);
0499 
0500     QStringList desc;
0501     if (!addressPickUp.isEmpty()) {
0502         desc.push_back(i18n("Pickup location: %1\n%2\n", rentalCalPickup.name(), formatAddress(addressPickUp)));
0503     }
0504     const auto dropOff = reservation.dropoffLocation();
0505     if (!dropOff.name().isEmpty()) {
0506         desc.push_back(i18n("Dropoff location: %1\n%2\n", dropOff.name(), formatAddress(dropOff.address())));
0507     }
0508     if (!reservation.reservationNumber().isEmpty()) {
0509         desc.push_back(i18n("Booking reference: %1", reservation.reservationNumber()));
0510     }
0511     const auto person = reservation.underName().value<KItinerary::Person>();
0512     if (!person.name().isEmpty()) {
0513         desc.push_back(i18n("Under name: %1", person.name()));
0514     }
0515 
0516     event->setDescription(desc.join(QLatin1Char('\n')));
0517 }
0518 
0519 static void fillTaxiReservation(const TaxiReservation &reservation, const KCalendarCore::Event::Ptr &event)
0520 {
0521     const auto taxiPickup = reservation.pickupLocation();
0522     const auto addressPickUp = taxiPickup.address();
0523     //TODO const auto rentalCar = reservation.reservationFor().value<RentalCar>();
0524     //TODO event->setSummary(i18n("Rental Car reservation: %1", rentalCar.name()));
0525     event->setLocation(formatAddressSingleLine(addressPickUp));
0526     fillGeoPosition(taxiPickup, event);
0527 
0528     event->setDtStart(reservation.pickupTime());
0529     //TODO event->setDtEnd(reservation.dropoffTime());
0530     event->setAllDay(false);
0531     event->setTransparency(KCalendarCore::Event::Transparent);
0532     //TODO event->setSummary(i18n("Rent car reservation: %1", rentalCar.name()));
0533     const QString pickUpAddress = formatAddress(addressPickUp);
0534 
0535     const QString description = i18n("Reservation reference: %1\nUnder name: %2\nPickup location: %3",
0536                                      reservation.reservationNumber(),
0537                                      reservation.underName().value<KItinerary::Person>().name(),
0538                                      pickUpAddress);
0539 
0540     event->setDescription(description);
0541 }
0542