KItinerary

jsapi/jsonld.cpp
1/*
2 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "datatypes/place.h"
8#include "jsonld.h"
9#include "locationutil.h"
10#include "reservationconverter.h"
11
12#include <KItinerary/JsonLdDocument>
13
14#include <QDebug>
15#include <QJSEngine>
16#include <QJsonArray>
17#include <QJsonObject>
18#include <QLocale>
19#include <QMetaProperty>
20#include <QRegularExpression>
21#include <QUrl>
22
23using namespace KItinerary;
24
25JsApi::JsonLd::JsonLd(QJSEngine* engine)
26 : QObject(engine)
27 , m_engine(engine)
28{
29}
30
31JsApi::JsonLd::~JsonLd() = default;
32
34{
35 auto v = m_engine->newObject();
36 v.setProperty(QStringLiteral("@type"), typeName);
37 return v;
38}
39
40QJSValue JsApi::JsonLd::newPlace(const QString &type) const
41{
42 const auto addr = newObject(QStringLiteral("PostalAddress"));
43 const auto geo = newObject(QStringLiteral("GeoCoordinates"));
44
45 auto p = newObject(type);
46 p.setProperty(QStringLiteral("address"), addr);
47 p.setProperty(QStringLiteral("geo"), geo);
48
49 return p;
50}
51
53{
54 const auto dep = newPlace(QStringLiteral("Airport"));
55 const auto arr = newPlace(QStringLiteral("Airport"));
56 const auto airline = newObject(QStringLiteral("Airline"));
57 const auto person = newObject(QStringLiteral("Person"));
58
59 auto resFor = newObject(QStringLiteral("Flight"));
60 resFor.setProperty(QStringLiteral("departureAirport"), dep);
61 resFor.setProperty(QStringLiteral("arrivalAirport"), arr);
62 resFor.setProperty(QStringLiteral("airline"), airline);
63
64 const auto ticket = newObject(QStringLiteral("Ticket"));
65
66 auto res = newObject(QStringLiteral("FlightReservation"));
67 res.setProperty(QStringLiteral("reservationFor"), resFor);
68 res.setProperty(QStringLiteral("underName"), person);
69 res.setProperty(QStringLiteral("reservedTicket"), ticket);
70
71 return res;
72}
73
75{
76 const auto dep = newPlace(QStringLiteral("TrainStation"));
77 const auto arr = newPlace(QStringLiteral("TrainStation"));
78 const auto person = newObject(QStringLiteral("Person"));
79 const auto seat = newObject(QStringLiteral("Seat"));
80
81 auto ticket = newObject(QStringLiteral("Ticket"));
82 ticket.setProperty(QStringLiteral("ticketedSeat"), seat);
83
84 auto resFor = newObject(QStringLiteral("TrainTrip"));
85 resFor.setProperty(QStringLiteral("departureStation"), dep);
86 resFor.setProperty(QStringLiteral("arrivalStation"), arr);
87 resFor.setProperty(QStringLiteral("provider"), newObject(QStringLiteral("Organization")));
88
89 auto res = newObject(QStringLiteral("TrainReservation"));
90 res.setProperty(QStringLiteral("reservationFor"), resFor);
91 res.setProperty(QStringLiteral("underName"), person);
92 res.setProperty(QStringLiteral("reservedTicket"), ticket);
93 res.setProperty(QStringLiteral("programMembershipUsed"), newObject(QStringLiteral("ProgramMembership")));
94
95 return res;
96}
97
99{
100 const auto dep = newPlace(QStringLiteral("BusStation"));
101 const auto arr = newPlace(QStringLiteral("BusStation"));
102 const auto person = newObject(QStringLiteral("Person"));
103 const auto seat = newObject(QStringLiteral("Seat"));
104
105 auto ticket = newObject(QStringLiteral("Ticket"));
106 ticket.setProperty(QStringLiteral("ticketedSeat"), seat);
107
108 auto resFor = newObject(QStringLiteral("BusTrip"));
109 resFor.setProperty(QStringLiteral("departureBusStop"), dep);
110 resFor.setProperty(QStringLiteral("arrivalBusStop"), arr);
111 resFor.setProperty(QStringLiteral("provider"), newObject(QStringLiteral("Organization")));
112
113 auto res = newObject(QStringLiteral("BusReservation"));
114 res.setProperty(QStringLiteral("reservationFor"), resFor);
115 res.setProperty(QStringLiteral("underName"), person);
116 res.setProperty(QStringLiteral("reservedTicket"), ticket);
117
118 return res;
119}
120
122{
123 const auto person = newObject(QStringLiteral("Person"));
124 const auto resFor = newPlace(QStringLiteral("LodgingBusiness"));
125
126 auto res = newObject(QStringLiteral("LodgingReservation"));
127 res.setProperty(QStringLiteral("reservationFor"), resFor);
128 res.setProperty(QStringLiteral("underName"), person);
129
130 return res;
131}
132
134{
135 auto resFor = newObject(QStringLiteral("Event"));
136 resFor.setProperty(QStringLiteral("location"), newPlace(QStringLiteral("Place")));
137
138 auto res = newObject(QStringLiteral("EventReservation"));
139 res.setProperty(QStringLiteral("reservationFor"), resFor);
140
141 auto ticket = newObject(QStringLiteral("Ticket"));
142 ticket.setProperty(QStringLiteral("ticketedSeat"), newObject(QStringLiteral("Seat")));
143 res.setProperty(QStringLiteral("reservedTicket"), ticket);
144
145 const auto person = newObject(QStringLiteral("Person"));
146 res.setProperty(QStringLiteral("underName"), person);
147
148 return res;
149}
150
152{
153 const auto person = newObject(QStringLiteral("Person"));
154 const auto resFor = newPlace(QStringLiteral("FoodEstablishment"));
155
156 auto res = newObject(QStringLiteral("FoodEstablishmentReservation"));
157 res.setProperty(QStringLiteral("reservationFor"), resFor);
158 res.setProperty(QStringLiteral("underName"), person);
159
160 return res;
161}
162
164{
165 const auto pickup = newPlace(QStringLiteral("Place"));
166 const auto dropoff = newPlace(QStringLiteral("Place"));
167 const auto person = newObject(QStringLiteral("Person"));
168 const auto org = newObject(QStringLiteral("Organization"));
169
170 auto resFor = newObject(QStringLiteral("RentalCar"));
171 resFor.setProperty(QStringLiteral("rentalCompany"), org);
172
173 auto res = newObject(QStringLiteral("RentalCarReservation"));
174 res.setProperty(QStringLiteral("reservationFor"), resFor);
175 res.setProperty(QStringLiteral("pickupLocation"), pickup);
176 res.setProperty(QStringLiteral("dropoffLocation"), dropoff);
177 res.setProperty(QStringLiteral("underName"), person);
178
179 return res;
180}
181
183{
184 const auto dep = newPlace(QStringLiteral("BoatTerminal"));
185 const auto arr = newPlace(QStringLiteral("BoatTerminal"));
186 const auto person = newObject(QStringLiteral("Person"));
187 const auto ticket = newObject(QStringLiteral("Ticket"));
188
189 auto resFor = newObject(QStringLiteral("BoatTrip"));
190 resFor.setProperty(QStringLiteral("departureBoatTerminal"), dep);
191 resFor.setProperty(QStringLiteral("arrivalBoatTerminal"), arr);
192
193 auto res = newObject(QStringLiteral("BoatReservation"));
194 res.setProperty(QStringLiteral("reservationFor"), resFor);
195 res.setProperty(QStringLiteral("underName"), person);
196 res.setProperty(QStringLiteral("reservedTicket"), ticket);
197
198 return res;
199}
200
202{
203 return m_engine->toScriptValue(ReservationConverter::trainToBus(QJsonValue::fromVariant(trainRes.toVariant()).toObject()));
204}
205
207{
208 return m_engine->toScriptValue(ReservationConverter::busToTrain(QJsonValue::fromVariant(busRes.toVariant()).toObject()));
209}
210
211QDateTime JsApi::JsonLd::toDateTime(const QString &dtStr, const QString &format, const QString &localeName) const
212{
213 QLocale locale(localeName);
214 auto dt = locale.toDateTime(dtStr, format);
215
216 // try harder for the "MMM" month format
217 // QLocale expects the exact string in QLocale::shortMonthName(), while we often encounter a three
218 // letter month identifier. For en_US that's the same, for Swedish it isn't though for example. So
219 // let's try to fix up the month identifiers to the full short name.
220 if (!dt.isValid() && format.contains(QLatin1StringView("MMM"))) {
221 auto dtStrFixed = dtStr;
222 for (int i = 1; i <= 12; ++i) {
223 const auto monthName = locale.monthName(i, QLocale::ShortFormat);
224 dtStrFixed.replace(monthName.left(3), monthName, Qt::CaseInsensitive);
225 }
226 dt = locale.toDateTime(dtStrFixed, format);
227 }
228
229 // try even harder for "MMM" month format
230 // in the de_DE locale we encounter sometimes almost arbitrary abbreviations for month
231 // names (eg. Mrz, Mär for März). So try to identify those and replace them with what QLocale
232 // expects
233 if (!dt.isValid() && format.contains(QLatin1StringView("MMM"))) {
234 auto dtStrFixed = dtStr;
235 for (int i = 1; i <= 12; ++i) {
236 const auto monthName = locale.monthName(i, QLocale::LongFormat);
237 const auto beginIdx = dtStr.indexOf(monthName.at(0));
238 if (beginIdx < 0) {
239 continue;
240 }
241 auto endIdx = beginIdx;
242 for (auto nameIdx = 0;
243 endIdx < dtStr.size() && nameIdx < monthName.size(); ++nameIdx) {
244 if (dtStr.at(endIdx).toCaseFolded() ==
245 monthName.at(nameIdx).toCaseFolded()) {
246 ++endIdx;
247 }
248 }
249 if (endIdx - beginIdx >= 3) {
250 dtStrFixed.replace(beginIdx, endIdx - beginIdx,
251 locale.monthName(i, QLocale::ShortFormat));
252 break;
253 }
254 }
255 dt = locale.toDateTime(dtStrFixed, format);
256 }
257
258 if (!dt.isValid()) {
259 return dt;
260 }
261
262 const bool hasFullYear = format.contains(QLatin1StringView("yyyy"));
263 const bool hasYear =
264 hasFullYear || format.contains(QLatin1StringView("yy"));
265 const bool hasMonth = format.contains(QLatin1Char('M'));
266 const bool hasDay = format.contains(QLatin1Char('d'));
267
268 // time only, set a default date
269 if (!hasDay && !hasMonth && !hasYear) {
270 dt.setDate({1970, 1, 1});
271 }
272
273 // if the date does not contain a year number, determine that based on the context date, if set
274 else if (!hasYear && m_contextDate.isValid()) {
275 dt.setDate({m_contextDate.date().year(), dt.date().month(), dt.date().day()});
276 // go one day back to leave a bit of room for documents produced very close to
277 // or even during the trip
278 if (dt < m_contextDate.addDays(-1)) {
279 dt = dt.addYears(1);
280 }
281 }
282
283 // fix two-digit years ending up in the wrong century
284 else if (!hasFullYear && dt.date().year() / 100 == 19) {
285 // be careful to only change the date, QDateTime::addYears can change
286 // the time as well if e.g. DST has been changed in the corresponding timezone
287 dt.setDate(dt.date().addYears(100));
288 }
289
290 return dt;
291}
292
293QDateTime JsApi::JsonLd::toDateTime(const QString &dtStr, const QJSValue &format, const QJSValue &localeName) const
294{
295 if (localeName.isString() && format.isString()) {
296 return toDateTime(dtStr, format.toString(), localeName.toString());
297 }
298 if (format.isArray()) {
299 const auto count = format.property(QLatin1StringView("length")).toInt();
300 for (auto i = 0; i < count; ++i) {
301 const auto dt = toDateTime(dtStr, format.property(i), localeName);
302 if (dt.isValid()) {
303 return dt;
304 }
305 }
306 }
307 if (format.isString() && localeName.isArray()) {
308 const auto count =
309 localeName.property(QLatin1StringView("length")).toInt();
310 for (auto i = 0; i < count; ++i) {
311 const auto dt = toDateTime(dtStr, format.toString(),
312 localeName.property(i).toString());
313 if (dt.isValid()) {
314 return dt;
315 }
316 }
317 }
318 return {};
319}
320
322{
323 if (v.canConvert<QList<QVariant>>()) {
324 return m_engine->toScriptValue(
326 }
327
328 const auto json = JsonLdDocument::toJsonValue(v);
329 return m_engine->toScriptValue(json);
330}
331
333{
334 return m_engine->toScriptValue(v.toVariant());
335}
336
338{
339 const QUrl url(mapUrl);
340 const auto geo = LocationUtil::geoFromUrl(url);
341 if (geo.isValid()) {
342 return toJson(geo);
343 }
344
345 return {};
346}
347
348QJSValue JsApi::JsonLd::readQDateTime(const QVariant &obj, const QString &propName) const
349{
350 const auto mo = QMetaType(obj.userType()).metaObject();
351 if (!mo) {
352 return {};
353 }
354 const auto propIdx = mo->indexOfProperty(propName.toUtf8().constData());
355 if (propIdx < 0) {
356 qWarning() << "Unknown property name:" << mo->className() << propName;
357 return {};
358 }
359 const auto prop = mo->property(propIdx);
360 const auto dt = prop.readOnGadget(obj.constData());
361 return toJson(dt);
362}
363
364QJSValue JsApi::JsonLd::apply(const QJSValue &lhs, const QJSValue &rhs) const
365{
368 const auto v = JsonLdDocument::apply(lhsVar, rhsVar);
369 return toJson(v);
370}
371
372void JsApi::JsonLd::setContextDate(const QDateTime& dt)
373{
374 m_contextDate = dt;
375}
376
377#include "moc_jsonld.cpp"
Q_INVOKABLE QJSValue newEventReservation() const
Convenience method that generates a full EventReservation JS object.
Q_INVOKABLE QJSValue toGeoCoordinates(const QString &mapUrl)
Parses geo coordinates from a given mapping service URLs.
Q_INVOKABLE QJSValue newLodgingReservation() const
Convenience method that generates a full LodgingReservation JS object.
Q_INVOKABLE QJSValue newBusReservation() const
Convenience method that generates a full BusReservation JS object.
Q_INVOKABLE QJSValue busToTrainReservation(const QJSValue &busRes) const
Convert a bus reservation to a train reservation.
Q_INVOKABLE QJSValue newObject(const QString &typeName) const
Create a new JSON-LD object of type typeName.
Q_INVOKABLE QJSValue apply(const QJSValue &lhs, const QJSValue &rhs) const
Q_INVOKABLE QJSValue clone(const QJSValue &v) const
Clones the given JS object.
Q_INVOKABLE QJSValue toJson(const QVariant &v) const
Convert object v to a JSON-LD object.
Q_INVOKABLE QJSValue newRentalCarReservation() const
Convenience method that generates a full RentalCarReservation JS object.
Q_INVOKABLE QDateTime toDateTime(const QString &dtStr, const QJSValue &format, const QJSValue &localeName) const
Convert a date/time string to a date/time value.
Q_INVOKABLE QJSValue readQDateTime(const QVariant &obj, const QString &propName) const
Read a QDateTime property and return a JSON-LD serialization of it.
Q_INVOKABLE QJSValue newFoodEstablishmentReservation() const
Convenience method that generates a full FoodEstablishmentReservation JS object.
Q_INVOKABLE QJSValue newTrainReservation() const
Convenience method that generates a full TrainReservation JS object.
Q_INVOKABLE QJSValue newFlightReservation() const
Convenience method that generates a full FlightReservation JS object.
Q_INVOKABLE QJSValue newBoatReservation() const
Convenience method that generates a full BoatReservation JS object.
Q_INVOKABLE QJSValue trainToBusReservation(const QJSValue &trainRes) const
Convert a train reservation to a bus reservation.
static QJsonValue toJsonValue(const QVariant &data)
JSON-LD serrialization of an invidividual data value.
static QVariant apply(const QVariant &lhs, const QVariant &rhs)
Apply all properties of rhs on to lhs.
static QJsonArray toJson(const QList< QVariant > &data)
Serialize instantiated data types to JSON-LD.
static QVariant fromJsonSingular(const QJsonObject &obj)
Convert a single JSON-LD object into an instantiated data type.
GeoCoordinates geoFromUrl(const QUrl &url)
Parses geo coordinates from a given mapping service URLs, such as Google Maps links.
QJsonObject trainToBus(const QJsonObject &trainRes)
Convert a train reservation to a bus reservation.
QJsonObject busToTrain(const QJsonObject &busRes)
Convert a bus reservation to a train reservation.
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
const char * constData() const const
char32_t toCaseFolded(char32_t ucs4)
QJSValue newObject()
QJsonValue fromVariant(const QVariant &variant)
QJsonObject toObject() const const
bool isArray() const const
bool isString() const const
QJSValue property(const QString &name) const const
void setProperty(const QString &name, const QJSValue &value)
qint32 toInt() const const
QString toString() const const
QVariant toVariant() const const
int indexOfProperty(const char *name) const const
const QMetaObject * metaObject() const const
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
qsizetype size() const const
QByteArray toUtf8() const const
CaseInsensitive
bool canConvert() const const
const void * constData() const const
int userType() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:56:36 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.