File indexing completed on 2025-02-02 05:02:27

0001 /*
0002     SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 
0006 #include "calendarimportmodel.h"
0007 
0008 #include <KItinerary/Event>
0009 #include <KItinerary/ExtractorEngine>
0010 #include <KItinerary/ExtractorPostprocessor>
0011 #include <KItinerary/ExtractorValidator>
0012 #include <KItinerary/JsonLdDocument>
0013 #include <KItinerary/Reservation>
0014 #include <KItinerary/Visit>
0015 
0016 #include <QDebug>
0017 #include <QJsonArray>
0018 
0019 CalendarImportModel::CalendarImportModel(QObject *parent)
0020     : QAbstractListModel(parent)
0021 {
0022     qRegisterMetaType<QVector<QVariant>>();
0023 }
0024 
0025 CalendarImportModel::~CalendarImportModel() = default;
0026 
0027 KCalendarCore::Calendar* CalendarImportModel::calendar() const
0028 {
0029     return m_calendar;
0030 }
0031 
0032 void CalendarImportModel::setCalendar(KCalendarCore::Calendar *calendar)
0033 {
0034     if (m_calendar == calendar) {
0035         return;
0036     }
0037 
0038     if (m_calendar) {
0039         disconnect(m_calendar, nullptr, this, nullptr);
0040     }
0041 
0042     m_calendar = calendar;
0043     Q_EMIT calendarChanged();
0044     if (!m_calendar) {
0045         return;
0046     }
0047 
0048     if (!m_calendar->isLoading()) {
0049         reload();
0050     } else {
0051         connect(m_calendar, &KCalendarCore::Calendar::isLoadingChanged, this, &CalendarImportModel::reload);
0052     }
0053 }
0054 
0055 QVector<QVariant> CalendarImportModel::selectedReservations() const
0056 {
0057     QVector<QVariant> res;
0058     for (const auto &ev : m_events) {
0059         if (!ev.selected) {
0060             continue;
0061         }
0062         std::copy(ev.data.begin(), ev.data.end(), std::back_inserter(res));
0063     }
0064     return res;
0065 }
0066 
0067 int CalendarImportModel::rowCount(const QModelIndex &parent) const
0068 {
0069     if (parent.isValid()) {
0070         return 0;
0071     }
0072 
0073     return m_events.size();
0074 }
0075 
0076 QVariant CalendarImportModel::data(const QModelIndex &index, int role) const
0077 {
0078     using namespace KItinerary;
0079     if (!checkIndex(index)) {
0080         return {};
0081     }
0082 
0083     const auto &ev = m_events[index.row()];
0084     switch (role) {
0085         case TitleRole:
0086             return ev.event->summary();
0087         case SubtitleRole:
0088             return QLocale().toString(ev.event->dtStart());
0089         case IconNameRole:
0090             if (!ev.data.isEmpty()) {
0091                 const auto res = ev.data.at(0);
0092                 if (JsonLd::isA<FlightReservation>(res)) {
0093                     return QStringLiteral("qrc:///images/flight.svg");
0094                 }
0095                 if (JsonLd::isA<TrainReservation>(res)) {
0096                     return QStringLiteral("qrc:///images/train.svg");
0097                 }
0098                 if (JsonLd::isA<BusReservation>(res)) {
0099                     return QStringLiteral("qrc:///images/bus.svg");
0100                 }
0101                 if (JsonLd::isA<BoatReservation>(res)) {
0102                     return QStringLiteral("qrc:///images/ferry.svg");
0103                 }
0104                 if (JsonLd::isA<LodgingReservation>(res)) {
0105                     return QStringLiteral("go-home-symbolic");
0106                 }
0107                 if (JsonLd::isA<FoodEstablishmentReservation>(res)) {
0108                     return QStringLiteral("qrc:///images/foodestablishment.svg");
0109                 }
0110                 if (JsonLd::isA<RentalCarReservation>(res)) {
0111                     return QStringLiteral("qrc:///images/car.svg");
0112                 }
0113             }
0114             return QStringLiteral("meeting-attending");
0115         case ReservationsRole:
0116             return QVariant::fromValue(ev.data);
0117         case SelectedRole:
0118             return ev.selected;
0119     }
0120 
0121     return {};
0122 }
0123 
0124 bool CalendarImportModel::setData(const QModelIndex &index, const QVariant &value, int role)
0125 {
0126     auto &ev = m_events[index.row()];
0127     switch (role) {
0128         case SelectedRole:
0129             ev.selected = value.toBool();
0130             Q_EMIT dataChanged(index, index);
0131             Q_EMIT hasSelectionChanged();
0132             return true;
0133     }
0134     return false;
0135 }
0136 
0137 QHash<int, QByteArray> CalendarImportModel::roleNames() const
0138 {
0139     auto n = QAbstractListModel::roleNames();
0140     n.insert(TitleRole, "title");
0141     n.insert(SubtitleRole, "subtitle");
0142     n.insert(IconNameRole, "iconName");
0143     n.insert(ReservationsRole, "reservations");
0144     n.insert(SelectedRole, "selected");
0145     return n;
0146 }
0147 
0148 bool CalendarImportModel::hasSelection() const
0149 {
0150     return std::any_of(m_events.begin(), m_events.end(), [](const auto &ev) { return ev.selected; });
0151 }
0152 
0153 void CalendarImportModel::reload()
0154 {
0155     beginResetModel();
0156     m_events.clear();
0157 
0158     KItinerary::ExtractorEngine extractorEngine;
0159     extractorEngine.setHints(KItinerary::ExtractorEngine::ExtractGenericIcalEvents);
0160 
0161     KItinerary::ExtractorValidator validator;
0162     validator.setAcceptedTypes<
0163         KItinerary::BoatReservation,
0164         KItinerary::BusReservation,
0165         KItinerary::Event,
0166         KItinerary::EventReservation,
0167         KItinerary::FlightReservation,
0168         KItinerary::FoodEstablishmentReservation,
0169         KItinerary::LodgingReservation,
0170         KItinerary::RentalCarReservation,
0171         KItinerary::TrainReservation,
0172         KItinerary::TouristAttractionVisit
0173     >();
0174 
0175     auto calEvents = m_calendar->events(today().addDays(-5), today().addDays(180));
0176     calEvents = m_calendar->sortEvents(std::move(calEvents), KCalendarCore::EventSortStartDate, KCalendarCore::SortDirectionAscending);
0177     for (const auto &ev : std::as_const(calEvents)) {
0178         extractorEngine.clear();
0179         extractorEngine.setContent(QVariant::fromValue(ev), u"internal/event");
0180         KItinerary::ExtractorPostprocessor postProc;
0181         postProc.process(KItinerary::JsonLdDocument::fromJson(extractorEngine.extract()));
0182         auto res = postProc.result();
0183         res.erase(std::remove_if(res.begin(), res.end(), [&validator](const auto &elem) { return !validator.isValidElement(elem); }), res.end());
0184         if (res.empty()) {
0185             continue;
0186         }
0187 
0188         const auto isReservation = !KItinerary::JsonLd::isA<KItinerary::Event>(res.at(0));
0189         m_events.push_back({ev, res, isReservation});
0190     }
0191 
0192     endResetModel();
0193 
0194     Q_EMIT hasSelectionChanged();
0195 }
0196 
0197 QDate CalendarImportModel::today() const
0198 {
0199     if (Q_UNLIKELY(m_todayOverride.isValid())) {
0200         return m_todayOverride;
0201     }
0202     return QDate::currentDate();
0203 }
0204 
0205 #include "moc_calendarimportmodel.cpp"