diff --git a/src/datatypes/reservation.cpp b/src/datatypes/reservation.cpp index 3ceaf5d..4027bcd 100644 --- a/src/datatypes/reservation.cpp +++ b/src/datatypes/reservation.cpp @@ -1,115 +1,128 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "reservation.h" #include "organization.h" #include "datatypes_p.h" #include #include using namespace KItinerary; namespace KItinerary { class ReservationPrivate : public QSharedData { KITINERARY_PRIVATE_BASE_GADGET(Reservation) public: QString reservationNumber; QVariant reservationFor; QVariant reservedTicket; QVariant underName; QUrl url; QUrl cancelReservationUrl; QUrl modifyReservationUrl; QString pkpassPassTypeIdentifier; QString pkpassSerialNumber; Organization provider; }; KITINERARY_MAKE_BASE_CLASS(Reservation) KITINERARY_MAKE_PROPERTY(Reservation, QString, reservationNumber, setReservationNumber) KITINERARY_MAKE_PROPERTY(Reservation, QVariant, reservationFor, setReservationFor) KITINERARY_MAKE_PROPERTY(Reservation, QVariant, reservedTicket, setReservedTicket) KITINERARY_MAKE_PROPERTY(Reservation, QVariant, underName, setUnderName) KITINERARY_MAKE_PROPERTY(Reservation, QUrl, cancelReservationUrl, setCancelReservationUrl) KITINERARY_MAKE_PROPERTY(Reservation, QUrl, modifyReservationUrl, setModifyReservationUrl) KITINERARY_MAKE_PROPERTY(Reservation, QUrl, url, setUrl) KITINERARY_MAKE_PROPERTY(Reservation, QString, pkpassPassTypeIdentifier, setPkpassPassTypeIdentifier) KITINERARY_MAKE_PROPERTY(Reservation, QString, pkpassSerialNumber, setPkpassSerialNumber) KITINERARY_MAKE_PROPERTY(Reservation, Organization, provider, setProvider) class LodgingReservationPrivate : public ReservationPrivate { KITINERARY_PRIVATE_GADGET(LodgingReservation) public: QDateTime checkinTime; QDateTime checkoutTime; }; KITINERARY_MAKE_SUB_CLASS(LodgingReservation, Reservation) KITINERARY_MAKE_PROPERTY(LodgingReservation, QDateTime, checkinTime, setCheckinTime) KITINERARY_MAKE_PROPERTY(LodgingReservation, QDateTime, checkoutTime, setCheckoutTime) QString LodgingReservation::checkinDateLocalized() const { K_D(const LodgingReservation); return QLocale().toString(d->checkinTime.date(), QLocale::ShortFormat); } QString LodgingReservation::checkoutDateLocalized() const { K_D(const LodgingReservation); return QLocale().toString(d->checkoutTime.date(), QLocale::ShortFormat); } class FlightReservationPrivate : public ReservationPrivate { KITINERARY_PRIVATE_GADGET(FlightReservation) public: QString airplaneSeat; QString boardingGroup; QUrl ticketDownloadUrl; }; KITINERARY_MAKE_SUB_CLASS(FlightReservation, Reservation) KITINERARY_MAKE_PROPERTY(FlightReservation, QString, airplaneSeat, setAirplaneSeat) KITINERARY_MAKE_PROPERTY(FlightReservation, QString, boardingGroup, setBoardingGroup) KITINERARY_MAKE_PROPERTY(FlightReservation, QUrl, ticketDownloadUrl, setTicketDownloadUrl) class TrainReservationPrivate : public ReservationPrivate { KITINERARY_PRIVATE_GADGET(TrainReservation) }; KITINERARY_MAKE_SUB_CLASS(TrainReservation, Reservation) class BusReservationPrivate : public ReservationPrivate { KITINERARY_PRIVATE_GADGET(BusReservation) }; KITINERARY_MAKE_SUB_CLASS(BusReservation, Reservation) +class FoodEstablishmentReservationPrivate : public ReservationPrivate +{ + KITINERARY_PRIVATE_GADGET(FoodEstablishmentReservation) +public: + QDateTime endTime; + QDateTime startTime; + int partySize = 0; +}; +KITINERARY_MAKE_SUB_CLASS(FoodEstablishmentReservation, Reservation) +KITINERARY_MAKE_PROPERTY(FoodEstablishmentReservation, QDateTime, endTime, setEndTime) +KITINERARY_MAKE_PROPERTY(FoodEstablishmentReservation, int, partySize, setPartySize) +KITINERARY_MAKE_PROPERTY(FoodEstablishmentReservation, QDateTime, startTime, setStartTime) + } template <> KItinerary::ReservationPrivate *QExplicitlySharedDataPointer::clone() { return d->clone(); } #include "moc_reservation.cpp" diff --git a/src/datatypes/reservation.h b/src/datatypes/reservation.h index 820da35..fe681f7 100644 --- a/src/datatypes/reservation.h +++ b/src/datatypes/reservation.h @@ -1,120 +1,133 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KITINERARY_RESERVATION_H #define KITINERARY_RESERVATION_H #include "kitinerary_export.h" #include "datatypes.h" class QUrl; namespace KItinerary { class ReservationPrivate; class Organization; /** Abstract base class for reservations. * @see https://schema.org/Reservation */ class KITINERARY_EXPORT Reservation { KITINERARY_BASE_GADGET(Reservation) KITINERARY_PROPERTY(QString, reservationNumber, setReservationNumber) KITINERARY_PROPERTY(QVariant, reservationFor, setReservationFor) KITINERARY_PROPERTY(QVariant, reservedTicket, setReservedTicket) KITINERARY_PROPERTY(QVariant, underName, setUnderName) KITINERARY_PROPERTY(KItinerary::Organization, provider, setProvider) KITINERARY_PROPERTY(QUrl, url, setUrl) // Google extension KITINERARY_PROPERTY(QUrl, cancelReservationUrl, setCancelReservationUrl) KITINERARY_PROPERTY(QUrl, modifyReservationUrl, setModifyReservationUrl) // KDE extensions /** Pass type identifier of an associated Apple Wallet boarding pass. * @see KPkPass::Pass::passTypeIdentifier */ KITINERARY_PROPERTY(QString, pkpassPassTypeIdentifier, setPkpassPassTypeIdentifier) /** Serial number of an associated Apple Wallet boarding pass. * @see KPkPass::Pass::serialNumber */ KITINERARY_PROPERTY(QString, pkpassSerialNumber, setPkpassSerialNumber) protected: ///@cond internal QExplicitlySharedDataPointer d; ///@endcond }; /** A hotel reservation. * @see https://schema.org/LodgingReservation * @see https://developers.google.com/gmail/markup/reference/hotel-reservation */ class KITINERARY_EXPORT LodgingReservation : public Reservation { KITINERARY_GADGET(LodgingReservation) KITINERARY_PROPERTY(QDateTime, checkinTime, setCheckinTime) KITINERARY_PROPERTY(QDateTime, checkoutTime, setCheckoutTime) Q_PROPERTY(QString checkinDateLocalized READ checkinDateLocalized STORED false CONSTANT) Q_PROPERTY(QString checkoutDateLocalized READ checkoutDateLocalized STORED false CONSTANT) private: QString checkinDateLocalized() const; QString checkoutDateLocalized() const; }; /** A flight reservation. * @see https://schema.org/FlightReservation * @see https://developers.google.com/gmail/markup/reference/flight-reservation */ class KITINERARY_EXPORT FlightReservation : public Reservation { KITINERARY_GADGET(FlightReservation) // Google extensions KITINERARY_PROPERTY(QString, airplaneSeat, setAirplaneSeat) KITINERARY_PROPERTY(QString, boardingGroup, setBoardingGroup) KITINERARY_PROPERTY(QUrl, ticketDownloadUrl, setTicketDownloadUrl) }; /** A train reservation. * @see https://schema.org/TrainReservation */ class KITINERARY_EXPORT TrainReservation : public Reservation { KITINERARY_GADGET(TrainReservation) }; /** A bus reservation. * @see https://schema.org/BusReservation */ class KITINERARY_EXPORT BusReservation : public Reservation { KITINERARY_GADGET(BusReservation) }; +/** A restaurant reservation. + * @see https://schema.org/FoodEstablishmentReservation + * @see https://developers.google.com/gmail/markup/reference/restaurant-reservation + */ +class KITINERARY_EXPORT FoodEstablishmentReservation : public Reservation +{ + KITINERARY_GADGET(FoodEstablishmentReservation) + KITINERARY_PROPERTY(QDateTime, endTime, setEndTime) + KITINERARY_PROPERTY(int, partySize, setPartySize) + KITINERARY_PROPERTY(QDateTime, startTime, setStartTime) +}; + } Q_DECLARE_METATYPE(KItinerary::FlightReservation) Q_DECLARE_METATYPE(KItinerary::LodgingReservation) Q_DECLARE_METATYPE(KItinerary::TrainReservation) Q_DECLARE_METATYPE(KItinerary::BusReservation) +Q_DECLARE_METATYPE(KItinerary::FoodEstablishmentReservation) #endif // KITINERARY_RESERVATION_H diff --git a/src/jsonlddocument.cpp b/src/jsonlddocument.cpp index 5522e41..8654750 100644 --- a/src/jsonlddocument.cpp +++ b/src/jsonlddocument.cpp @@ -1,321 +1,322 @@ /* Copyright (c) 2017 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "jsonlddocument.h" #include "jsonldimportfilter.h" #include "logging.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KItinerary; static QVariant createInstance(const QJsonObject &obj); // Eurowings workarounds... static const char *fallbackDateTimePattern[] = { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "MM-dd-yyyy HH:mm" // yes, seriously ;( }; static const auto fallbackDateTimePatternCount = sizeof(fallbackDateTimePattern) / sizeof(const char *); static QVariant propertyValue(const QMetaProperty &prop, const QJsonValue &v) { switch (prop.type()) { case QVariant::String: return v.toString(); case QVariant::Date: return QDate::fromString(v.toString(), Qt::ISODate); case QVariant::DateTime: { QDateTime dt; if (v.isObject()) { const auto dtObj = v.toObject(); if (dtObj.value(QLatin1String("@type")).toString() == QLatin1String("QDateTime")) { dt = QDateTime::fromString(dtObj.value(QLatin1String("@value")).toString(), Qt::ISODate); dt.setTimeZone(QTimeZone(dtObj.value(QLatin1String("timezone")).toString().toUtf8())); } } else { auto str = v.toString(); dt = QDateTime::fromString(str, Qt::ISODate); for (unsigned int i = 0; i < fallbackDateTimePatternCount && dt.isNull(); ++i) { dt = QDateTime::fromString(str, QString::fromLatin1(fallbackDateTimePattern[i])); } if (dt.isNull()) { qCDebug(Log) << "Datetime parsing failed for" << str; } } return dt; } case QVariant::Double: return v.toDouble(); case QVariant::Url: return QUrl(v.toString()); default: break; } if (prop.type() == qMetaTypeId()) { return v.toDouble(); } return createInstance(v.toObject()); } static void createInstance(const QMetaObject *mo, void *v, const QJsonObject &obj) { for (auto it = obj.begin(); it != obj.end(); ++it) { if (it.key().startsWith(QLatin1Char('@'))) { continue; } const auto idx = mo->indexOfProperty(it.key().toLatin1().constData()); if (idx < 0) { qCDebug(Log) << "property" << it.key() << "could not be set on object of type" << mo->className(); continue; } const auto prop = mo->property(idx); const auto value = propertyValue(prop, it.value()); prop.writeOnGadget(v, value); } } template static QVariant createInstance(const QJsonObject &obj) { T t; createInstance(&T::staticMetaObject, &t, obj); return QVariant::fromValue(t); } #define MAKE_FACTORY(Class) \ if (type == QLatin1String(#Class)) \ return createInstance(obj) static QVariant createInstance(const QJsonObject &obj) { const auto type = obj.value(QLatin1String("@type")).toString(); MAKE_FACTORY(Airline); MAKE_FACTORY(Airport); MAKE_FACTORY(BusReservation); MAKE_FACTORY(BusStation); MAKE_FACTORY(BusTrip); MAKE_FACTORY(Flight); MAKE_FACTORY(FlightReservation); MAKE_FACTORY(FoodEstablishment); + MAKE_FACTORY(FoodEstablishmentReservation); MAKE_FACTORY(GeoCoordinates); MAKE_FACTORY(LodgingBusiness); MAKE_FACTORY(LodgingReservation); MAKE_FACTORY(Organization); MAKE_FACTORY(Person); MAKE_FACTORY(Place); MAKE_FACTORY(PostalAddress); MAKE_FACTORY(Seat); MAKE_FACTORY(Ticket); MAKE_FACTORY(TouristAttraction); MAKE_FACTORY(TrainReservation); MAKE_FACTORY(TrainStation); MAKE_FACTORY(TrainTrip); return {}; } #undef MAKE_FACTORY QVector JsonLdDocument::fromJson(const QJsonArray &array) { QVector l; l.reserve(array.size()); for (const auto &obj : array) { const auto v = createInstance(JsonLdImportFilter::filterObject(obj.toObject())); if (!v.isNull()) { l.push_back(v); } } return l; } static bool valueIsNull(const QVariant &v) { if (v.type() == QVariant::Url) { return !v.toUrl().isValid(); } if (v.type() == qMetaTypeId()) { return std::isnan(v.toFloat()); } return v.isNull(); } static QJsonValue toJson(const QVariant &v) { const auto mo = QMetaType(v.userType()).metaObject(); if (!mo) { // basic types switch (v.type()) { case QVariant::String: return v.toString(); case QVariant::Double: return v.toDouble(); case QVariant::Int: return v.toInt(); case QVariant::Date: return v.toDate().toString(Qt::ISODate); case QVariant::DateTime: { const auto dt = v.toDateTime(); if (dt.timeSpec() == Qt::TimeZone) { QJsonObject dtObj; dtObj.insert(QStringLiteral("@type"), QStringLiteral("QDateTime")); dtObj.insert(QStringLiteral("@value"), dt.toString(Qt::ISODate)); dtObj.insert(QStringLiteral("timezone"), QString::fromUtf8(dt.timeZone().id())); return dtObj; } return v.toDateTime().toString(Qt::ISODate); } case QVariant::Url: return v.toUrl().toString(); default: break; } if (v.userType() == qMetaTypeId()) { return v.toFloat(); } qCDebug(Log) << "unhandled value:" << v; return {}; } // composite types QJsonObject obj; obj.insert(QStringLiteral("@type"), JsonLdDocument::readProperty(v, "className").toString()); for (int i = 0; i < mo->propertyCount(); ++i) { const auto prop = mo->property(i); if (!prop.isStored()) { continue; } const auto value = prop.readOnGadget(v.constData()); if (!valueIsNull(value)) { const auto jsVal = toJson(value); if (jsVal.type() != QJsonValue::Null) { obj.insert(QString::fromUtf8(prop.name()), jsVal); } } } if (obj.size() > 1) { return obj; } return {}; } QJsonArray JsonLdDocument::toJson(const QVector &data) { QJsonArray a; for (const auto &d : data) { const auto value = toJson(d); if (!value.isObject()) { continue; } auto obj = value.toObject(); obj.insert(QStringLiteral("@context"), QStringLiteral("http://schema.org")); a.push_back(obj); } return a; } QVariant JsonLdDocument::readProperty(const QVariant &obj, const char *name) { const auto mo = QMetaType(obj.userType()).metaObject(); if (!mo) { return {}; } const auto idx = mo->indexOfProperty(name); if (idx < 0) { return {}; } const auto prop = mo->property(idx); return prop.readOnGadget(obj.constData()); } void JsonLdDocument::writeProperty(QVariant &obj, const char *name, const QVariant &value) { const auto mo = QMetaType(obj.userType()).metaObject(); if (!mo) { return; } const auto idx = mo->indexOfProperty(name); if (idx < 0) { return; } const auto prop = mo->property(idx); prop.writeOnGadget(obj.data(), value); } void JsonLdDocument::removeProperty(QVariant &obj, const char *name) { writeProperty(obj, name, QVariant()); } QVariant JsonLdDocument::apply(const QVariant& lhs, const QVariant& rhs) { if (rhs.isNull()) { return lhs; } if (lhs.isNull()) { return rhs; } if (lhs.userType() != rhs.userType()) { qCWarning(Log) << "type mismatch during merging:" << lhs << rhs; return {}; } auto res = lhs; const auto mo = QMetaType(res.userType()).metaObject(); for (int i = 0; i < mo->propertyCount(); ++i) { const auto prop = mo->property(i); if (!prop.isStored()) { continue; } auto pv = prop.readOnGadget(rhs.constData()); if (QMetaType(pv.userType()).metaObject()) { pv = apply(prop.readOnGadget(lhs.constData()), pv); } if (!pv.isNull()) { prop.writeOnGadget(res.data(), pv); } } return res; }