7#include "jsonldimportfilter.h"
8#include "json/jsonld.h"
9#include "json/jsonldfilterengine.h"
15#include <QJsonDocument>
27 {
"AutoDealer",
"LocalBusiness" },
28 {
"AutoRepair",
"LocalBusiness" },
29 {
"AutomotiveBusiness",
"LocalBusiness" },
30 {
"Bakery",
"FoodEstablishment" },
31 {
"BarOrPub",
"FoodEstablishment" },
32 {
"BedAndBreakfast",
"LodgingBusiness" },
33 {
"Brewery",
"FoodEstablishment" },
34 {
"BusStop",
"BusStation" },
35 {
"BusinessEvent",
"Event" },
36 {
"CafeOrCoffeeShop",
"FoodEstablishment" },
37 {
"Campground",
"LodgingBusiness" },
38 {
"ChildrensEvent",
"Event" },
39 {
"ComedyEvent",
"Event" },
40 {
"ComputerStore",
"LocalBusiness" },
41 {
"DanceEvent",
"Event" },
42 {
"Distillery",
"FoodEstablishment" },
43 {
"EditAction",
"UpdateAction" },
44 {
"EducationEvent",
"Event" },
45 {
"ElectronicsStore",
"LocalBusiness" },
46 {
"EntertainmentBusiness",
"LocalBusiness" },
47 {
"ExhibitionEvent",
"Event" },
48 {
"FastFoodRestaurant",
"FoodEstablishment" },
49 {
"Festival",
"Event" },
50 {
"HobbyShop",
"LocalBusiness" },
51 {
"HomeAndConstructionBusiness",
"LocalBusiness" },
52 {
"Hostel",
"LodgingBusiness" },
53 {
"Hotel",
"LodgingBusiness" },
54 {
"IceCreamShop",
"FoodEstablishment" },
55 {
"LiteraryEvent",
"Event" },
56 {
"Motel",
"LodgingBusiness" },
57 {
"MovieTheater",
"LocalBusiness" },
58 {
"MusicEvent",
"Event" },
59 {
"Resort",
"LodgingBusiness" },
60 {
"Restaurant",
"FoodEstablishment" },
61 {
"SaleEvent",
"Event" },
62 {
"ScreeningEvent",
"Event" },
63 {
"SocialEvent",
"Event" },
64 {
"SportsEvent",
"Event" },
65 {
"Store",
"LocalBusiness" },
66 {
"TheaterEvent",
"Event" },
67 {
"VisualArtsEvent",
"Event" },
68 {
"Winery",
"FoodEstablishment" },
72 const auto val = obj.
value(key);
80 obj.
insert(key, arr.at(0));
83static void migrateToAction(
QJsonObject &obj,
const char *propName,
const char *typeName,
bool remove)
86 if (value.isNull() || value.isUndefined()) {
90 const auto actionsVal = obj.
value(
"potentialAction"_L1);
92 if (actionsVal.isArray()) {
93 actions = actionsVal.toArray();
94 }
else if (actionsVal.isObject()) {
95 actions = { actionsVal };
98 for (
const auto &act : actions) {
106 action.
insert(QStringLiteral(
"target"), value);
108 obj.
insert(QStringLiteral(
"potentialAction"), actions);
118 if (
const auto addr = obj.
value(
"address"_L1); addr.isString()) {
120 {
"@type"_L1,
"PostalAddress"_L1},
121 {
"streetAddress"_L1, addr.toString()},
125 const auto lat = obj.
value(
"latitude"_L1);
126 const auto lon = obj.
value(
"longitude"_L1);
127 if (lat.isDouble() && lon.isDouble()) {
129 if (!
geo.contains(
"@type"_L1)) {
130 geo.insert(
"@type"_L1,
"GeoCoordinates"_L1);
132 if (!
geo.contains(
"latitude"_L1)) {
133 geo.insert(
"latitude"_L1, lat);
135 if (!
geo.contains(
"longitude"_L1)) {
136 geo.insert(
"longitude"_L1, lat);
138 obj.
insert(
"geo"_L1, geo);
153 for (
const auto key : {
"ticketToken"_L1,
"ticketNumber"_L1}) {
157 if (ticket.isEmpty()) {
158 ticket.
insert(
"@type"_L1, u
"Ticket"_s);
160 if (!ticket.contains(key)) {
161 ticket.insert(key, v);
162 res.
insert(
"reservedTicket"_L1, ticket);
170 if (!resStat.isEmpty()) {
171 if (resStat.startsWith(
"https:"_L1)) {
174 if (!resStat.contains(
"/Reservation"_L1)) {
175 res.
insert(
"reservationStatus"_L1, resStat.replace(
"http://schema.org/"_L1,
"http://schema.org/Reservation"_L1));
186 migrateToAction(res,
"cancelReservationUrl",
"CancelAction",
true);
187 migrateToAction(res,
"checkinUrl",
"CheckInAction",
true);
188 migrateToAction(res,
"modifyReservationUrl",
"UpdateAction",
true);
189 migrateToAction(res,
"ticketDownloadUrl",
"DownloadAction",
true);
190 migrateToAction(res,
"url",
"ViewAction",
false);
199static void filterFoodEstablishment(
QJsonObject &restaurant)
202 auto reservationsValue =
204 if (reservationsValue.isString()) {
205 const QString reservations = reservationsValue.toString();
211 migrateToAction(restaurant,
"acceptsReservations",
"ReserveAction",
216 filterPlace(restaurant);
222 QString filteredTargetUrlString;
231 for (
auto it = targets.
begin(); it != targets.
end(); ++it) {
232 auto target = (*it).toObject();
238 if (actionPlatform.
isArray()) {
239 platforms = actionPlatform.
toArray();
245 if (!filteredTargetUrlString.
isEmpty()) {
246 const bool hasPreferredPlatform = std::any_of(platforms.
begin(), platforms.
end(), [](
const QJsonValue &platformValue) {
247 const QString platform = platformValue.toString();
249 return platform == QLatin1StringView(
250 "http://schema.org/DesktopWebPlatform");
253 if (!hasPreferredPlatform) {
261 if (!url.isValid()) {
265 filteredTargetUrlString = url.toString();
268 if (filteredTargetUrlString.
isEmpty()) {
271 action.
insert(QStringLiteral(
"target"), filteredTargetUrlString);
284 for (
auto it = actions.
begin(); it != actions.
end(); ++it) {
285 auto action = (*it).toObject();
286 filterActionTarget(action);
295 unpackArray(obj,
"location"_L1);
298 if (
const auto endDate = obj.
value(
"endDate"_L1).
toString(); endDate.size() == 10) {
300 if (date.isValid()) {
319 {
"Event", filterEvent },
320 {
"Flight", filterFlight },
321 {
"FoodEstablishment", filterFoodEstablishment },
322 {
"LocalBusiness", filterPlace },
323 {
"Organization", filterPlace },
324 {
"Place", filterPlace },
325 {
"PostalAddress", filterPostalAddress },
331 {
"BusTrip",
"arrivalStation",
"arrivalBusStop" },
332 {
"BusTrip",
"busCompany",
"provider" },
333 {
"BusTrip",
"departureStation",
"departureBusStop" },
336 {
"Flight",
"provider",
"airline" },
339 {
"LodgingReservation",
"checkinDate",
"checkinTime" },
340 {
"LodgingReservation",
"checkoutDate",
"checkoutTime" },
342 {
"ProgramMembership",
"program",
"programName" },
343 {
"ProgramMembership",
"memberNumber",
"membershipNumber" },
345 {
"Reservation",
"price",
"totalPrice" },
346 {
"Ticket",
"price",
"totalPrice" },
349 {
"TrainTrip",
"trainCompany",
"provider" },
357 for (
const auto &o : graph) {
359 std::copy(a.begin(), a.end(), std::back_inserter(result));
369 if (typeVal.isString()) {
371 }
else if (typeVal.isArray()) {
372 const auto typeNames = typeVal.toArray();
373 for (
const auto &t : typeNames) {
382 return graphExpand(obj);
388 filterEngine.setTypeMappings(type_mapping);
389 filterEngine.setTypeFilters(type_filters);
390 filterEngine.setPropertyMappings(property_mappings);
391 for (
const auto &type : types) {
393 res.
insert(QStringLiteral(
"@type"), type);
397 if (
const auto mainEntityOfPage =
399 !mainEntityOfPage.isEmpty()) {
401 for (
auto it = mainEntityOfPage.begin(); it != mainEntityOfPage.end();
407 res.
insert(it.key(), it.value());
412 filterReservation(res);
416 if (!actions.isUndefined()) {
417 res.
insert(QStringLiteral(
"potentialAction"), filterActions(actions));
422 if (image.isObject()) {
423 const auto imageObject = image.
toObject();
426 res.
insert(QStringLiteral(
"image"),
433 if (
const auto a = resFor.toArray(); !a.isEmpty()) {
434 for (
const auto &entry : a) {
JSON-LD filtering for input normalization or type transforms.
void filterRecursive(QJsonObject &obj)
Recursively apply filtering rules to obj.
char * toString(const EngineQuery &query)
QJsonArray filterObject(const QJsonObject &obj)
Filter the top-level object obj for loading with JsonLdDocument.
QString typeName(const QJsonObject &obj)
Normalized type name from object.
void renameProperty(QJsonObject &obj, const char *oldName, const char *newName)
Rename a property, if present and the new name isn't in use already.
GeoCoordinates geo(const QVariant &location)
Returns the geo coordinates of a given location.
Classes for reservation/travel data models, data extraction and data augmentation.
KI18NLOCALEDATA_EXPORT KCountry country(const char *ianaId)
QDate fromString(QStringView string, QStringView format, QCalendar cal)
void push_back(const QJsonValue &value)
bool contains(QLatin1StringView key) const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
void remove(QLatin1StringView key)
QJsonValue value(QLatin1StringView key) const const
bool isArray() const const
bool isObject() const const
QJsonArray toArray() const const
QJsonObject toObject() const const
QString toString() const const
bool isEmpty() const const
void push_back(parameter_type value)
bool isEmpty() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)