KItinerary

extractorpostprocessor.cpp
1 /*
2  Copyright (c) 2017 Volker Krause <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "config-kitinerary.h"
21 #include "extractorpostprocessor.h"
22 #include "extractorpostprocessor_p.h"
23 #include "extractorvalidator.h"
24 #include "flightpostprocessor_p.h"
25 
26 #include "extractorutil.h"
27 #include "iatabcbpparser.h"
28 #include "jsonlddocument.h"
29 #include "logging.h"
30 #include "mergeutil.h"
31 #include "sortutil.h"
32 
33 #include "knowledgedb/trainstationdb.h"
34 
35 #include <KItinerary/Action>
36 #include <KItinerary/BusTrip>
37 #include <KItinerary/Event>
38 #include <KItinerary/Flight>
39 #include <KItinerary/Organization>
40 #include <KItinerary/Person>
41 #include <KItinerary/Place>
42 #include <KItinerary/RentalCar>
43 #include <KItinerary/Reservation>
44 #include <KItinerary/Taxi>
45 #include <KItinerary/Ticket>
46 #include <KItinerary/TrainTrip>
47 #include <KItinerary/Visit>
48 
49 #include <KContacts/Address>
50 
51 #include <QDebug>
52 #include <QJsonArray>
53 #include <QJsonDocument>
54 #include <QTimeZone>
55 #include <QUrl>
56 
57 #ifdef HAVE_PHONENUMBER
58 #include <phonenumbers/phonenumberutil.h>
59 #endif
60 
61 #include <algorithm>
62 
63 using namespace KItinerary;
64 
65 ExtractorPostprocessor::ExtractorPostprocessor()
66  : d(new ExtractorPostprocessorPrivate)
67 {
68  // configure the default set of accepted types, for backward compatibility
69  d->m_validator.setAcceptedTypes<
78  // reservationFor types
79  Flight,
80  TrainTrip,
81  BusTrip,
82  RentalCar,
83  Taxi,
84  Event,
85  TouristAttractionVisit,
87  // PBI types
89  >();
90 }
91 
92 ExtractorPostprocessor::ExtractorPostprocessor(ExtractorPostprocessor &&) noexcept = default;
94 
95 void ExtractorPostprocessor::process(const QVector<QVariant> &data)
96 {
97  d->m_resultFinalized = false;
98  d->m_data.reserve(d->m_data.size() + data.size());
99  for (auto elem : data) {
100  // reservation types
101  if (JsonLd::isA<FlightReservation>(elem)) {
102  elem = d->processFlightReservation(elem.value<FlightReservation>());
103  } else if (JsonLd::isA<TrainReservation>(elem)) {
104  elem = d->processTrainReservation(elem.value<TrainReservation>());
105  } else if (JsonLd::isA<LodgingReservation>(elem)) {
106  elem = d->processLodgingReservation(elem.value<LodgingReservation>());
107  } else if (JsonLd::isA<FoodEstablishmentReservation>(elem)) {
108  elem = d->processFoodEstablishmentReservation(elem.value<FoodEstablishmentReservation>());
109  } else if (JsonLd::isA<TouristAttractionVisit>(elem)) {
110  elem = d->processTouristAttractionVisit(elem.value<TouristAttractionVisit>());
111  } else if (JsonLd::isA<BusReservation>(elem)) {
112  elem = d->processBusReservation(elem.value<BusReservation>());
113  } else if (JsonLd::isA<EventReservation>(elem)) {
114  elem = d->processEventReservation(elem.value<EventReservation>());
115  } else if (JsonLd::isA<RentalCarReservation>(elem)) {
116  elem = d->processRentalCarReservation(elem.value<RentalCarReservation>());
117  } else if (JsonLd::isA<TaxiReservation>(elem)) {
118  elem = d->processTaxiReservation(elem.value<TaxiReservation>());
119  }
120 
121  // "reservationFor" types
122  else if (JsonLd::isA<LodgingBusiness>(elem)) {
123  elem = d->processPlace(elem.value<LodgingBusiness>());
124  } else if (JsonLd::isA<FoodEstablishment>(elem)) {
125  elem = d->processPlace(elem.value<FoodEstablishment>());
126  } else if (JsonLd::isA<Event>(elem)) {
127  elem = d->processEvent(elem.value<Event>());
128  }
129 
130  d->mergeOrAppend(elem);
131  }
132 }
133 
135 {
136  if (!d->m_resultFinalized && d->m_validationEnabled) {
137  d->m_data.erase(std::remove_if(d->m_data.begin(), d->m_data.end(), [this](const auto &elem) {
138  return !d->m_validator.isValidElement(elem);
139  }), d->m_data.end());
140  d->m_resultFinalized = true;
141  }
142 
143  std::stable_sort(d->m_data.begin(), d->m_data.end(), SortUtil::isBefore);
144  return d->m_data;
145 }
146 
148 {
149  d->m_contextDate = dt;
150 }
151 
153 {
154  d->m_validationEnabled = validate;
155 }
156 
157 void ExtractorPostprocessorPrivate::mergeOrAppend(const QVariant &elem)
158 {
159  const auto it = std::find_if(m_data.begin(), m_data.end(), [elem](const QVariant &other) {
160  return MergeUtil::isSame(elem, other);
161  });
162 
163  if (it == m_data.end()) {
164  m_data.push_back(elem);
165  } else {
166  *it = MergeUtil::merge(*it, elem);
167  }
168 }
169 
170 QVariant ExtractorPostprocessorPrivate::processFlightReservation(FlightReservation res) const
171 {
172  // expand ticketToken for IATA BCBP data
173  const auto bcbp = res.reservedTicket().value<Ticket>().ticketTokenData();
174  if (!bcbp.isEmpty()) {
175  const auto bcbpData = IataBcbpParser::parse(bcbp, m_contextDate.date());
176  if (bcbpData.size() == 1) {
177  res = JsonLdDocument::apply(bcbpData.at(0), res).value<FlightReservation>();
178  // standardize on the BCBP booking reference, not some secondary one we might have in structured data for example
179  res.setReservationNumber(bcbpData.at(0).value<FlightReservation>().reservationNumber());
180  } else {
181  for (const auto &data : bcbpData) {
182  if (MergeUtil::isSame(res, data)) {
183  res = JsonLdDocument::apply(data, res).value<FlightReservation>();
184  break;
185  }
186  }
187  }
188  }
189 
190  if (res.reservationFor().isValid()) {
191  FlightPostProcessor p;
192  res.setReservationFor(p.processFlight(res.reservationFor().value<Flight>()));
193  }
194  return processReservation(res);
195 }
196 
197 TrainReservation ExtractorPostprocessorPrivate::processTrainReservation(TrainReservation res) const
198 {
199  if (res.reservationFor().isValid()) {
200  res.setReservationFor(processTrainTrip(res.reservationFor().value<TrainTrip>()));
201  }
202  return processReservation(res);
203 }
204 
205 TrainTrip ExtractorPostprocessorPrivate::processTrainTrip(TrainTrip trip) const
206 {
207  trip.setArrivalPlatform(trip.arrivalPlatform().trimmed());
208  trip.setDeparturePlatform(trip.departurePlatform().trimmed());
209  trip.setDepartureStation(processTrainStation(trip.departureStation()));
210  trip.setArrivalStation(processTrainStation(trip.arrivalStation()));
211  trip.setDepartureTime(processTrainTripTime(trip.departureTime(), trip.departureStation()));
212  trip.setArrivalTime(processTrainTripTime(trip.arrivalTime(), trip.arrivalStation()));
213  trip.setTrainNumber(trip.trainNumber().simplified());
214  trip.setTrainName(trip.trainName().simplified());
215  return trip;
216 }
217 
218 static void applyStationData(const KnowledgeDb::TrainStation &record, TrainStation &station)
219 {
220  if (!station.geo().isValid() && record.coordinate.isValid()) {
221  GeoCoordinates geo;
222  geo.setLatitude(record.coordinate.latitude);
223  geo.setLongitude(record.coordinate.longitude);
224  station.setGeo(geo);
225  }
226  auto addr = station.address();
227  if (addr.addressCountry().isEmpty() && record.country.isValid()) {
228  addr.setAddressCountry(record.country.toString());
229  station.setAddress(addr);
230  }
231 }
232 
233 static void applyStationCountry(const QString &isoCode, TrainStation &station)
234 {
235  auto addr = station.address();
236  if (addr.addressCountry().isEmpty()) {
237  addr.setAddressCountry(isoCode.toUpper());
238  station.setAddress(addr);
239  }
240 }
241 
242 TrainStation ExtractorPostprocessorPrivate::processTrainStation(TrainStation station) const
243 {
244  const auto id = station.identifier();
245  if (id.isEmpty()) { // empty -> null cleanup, to have more compact json-ld output
246  station.setIdentifier(QString());
247  } else if (id.startsWith(QLatin1String("sncf:")) && id.size() == 10) {
249  applyStationData(record, station);
250  applyStationCountry(id.mid(5, 2).toUpper(), station);
251  } else if (id.startsWith(QLatin1String("ibnr:")) && id.size() == 12) {
252  const auto record = KnowledgeDb::stationForIbnr(KnowledgeDb::IBNR{id.mid(5).toUInt()});
253  applyStationData(record, station);
254  const auto country = KnowledgeDb::countryIdForUicCode(id.midRef(5, 2).toUShort()).toString();
255  applyStationCountry(country, station);
256  } else if (id.startsWith(QLatin1String("uic:")) && id.size() == 11) {
257  const auto record = KnowledgeDb::stationForUic(KnowledgeDb::UICStation{id.mid(4).toUInt()});
258  applyStationData(record, station);
259  const auto country = KnowledgeDb::countryIdForUicCode(id.midRef(4, 2).toUShort()).toString();
260  applyStationCountry(country, station);
261  } else if (id.startsWith(QLatin1String("ir:")) && id.size() > 4) {
262  const auto record = KnowledgeDb::stationForIndianRailwaysStationCode(id.mid(3));
263  applyStationData(record, station);
264  } else if (id.startsWith(QLatin1String("benerail:")) && id.size() == 14) {
265  applyStationCountry(id.mid(9, 2).toUpper(), station);
266  } else if (id.startsWith(QLatin1String("vrfi:")) && id.size() >= 7 && id.size() <= 9) {
268  applyStationData(record, station);
269  }
270 
271  return processPlace(station);
272 }
273 
274 QDateTime ExtractorPostprocessorPrivate::processTrainTripTime(QDateTime dt, const TrainStation& station) const
275 {
276  if (!dt.isValid()) {
277  return dt;
278  }
279 
280  if (dt.timeSpec() == Qt::TimeZone) {
281  return dt;
282  }
283 
284  QTimeZone tz;
285  const auto geo = station.geo();
286  const KnowledgeDb::CountryId country{station.address().addressCountry()};
287  if (geo.isValid()) {
288  tz = KnowledgeDb::toQTimeZone(KnowledgeDb::timezoneForLocation(geo.latitude(), geo.longitude(), country));
289  } else {
291  }
292  if (!tz.isValid()) {
293  return dt;
294  }
295 
296  // prefer our timezone over externally provided UTC offset, if they match
297  if (dt.timeSpec() == Qt::OffsetFromUTC && tz.offsetFromUtc(dt) != dt.offsetFromUtc()) {
298  return dt;
299  }
300 
301  if (dt.timeSpec() == Qt::OffsetFromUTC || dt.timeSpec() == Qt::LocalTime) {
302  dt.setTimeSpec(Qt::TimeZone);
303  dt.setTimeZone(tz);
304  } else if (dt.timeSpec() == Qt::UTC) {
305  dt = dt.toTimeZone(tz);
306  }
307  return dt;
308 }
309 
310 BusReservation ExtractorPostprocessorPrivate::processBusReservation(BusReservation res) const
311 {
312  if (res.reservationFor().isValid()) {
313  res.setReservationFor(processBusTrip(res.reservationFor().value<BusTrip>()));
314  }
315  return processReservation(res);
316 }
317 
318 BusTrip ExtractorPostprocessorPrivate::processBusTrip(BusTrip trip) const
319 {
320  trip.setDepartureBusStop(processPlace(trip.departureBusStop()));
321  trip.setArrivalBusStop(processPlace(trip.arrivalBusStop()));
322  trip.setDepartureTime(processTimeForLocation(trip.departureTime(), trip.departureBusStop()));
323  trip.setArrivalTime(processTimeForLocation(trip.arrivalTime(), trip.arrivalBusStop()));
324  trip.setBusNumber(trip.busNumber().simplified());
325  trip.setBusName(trip.busName().simplified());
326  return trip;
327 }
328 
329 LodgingReservation ExtractorPostprocessorPrivate::processLodgingReservation(LodgingReservation res) const
330 {
331  if (res.reservationFor().isValid()) {
332  res.setReservationFor(processPlace(res.reservationFor().value<LodgingBusiness>()));
333  res.setCheckinTime(processTimeForLocation(res.checkinTime(), res.reservationFor().value<LodgingBusiness>()));
334  res.setCheckoutTime(processTimeForLocation(res.checkoutTime(), res.reservationFor().value<LodgingBusiness>()));
335  }
336  return processReservation(res);
337 }
338 
339 TaxiReservation ExtractorPostprocessorPrivate::processTaxiReservation(TaxiReservation res) const
340 {
341  res.setPickupLocation(processPlace(res.pickupLocation()));
342  res.setPickupTime(processTimeForLocation(res.pickupTime(), res.pickupLocation()));
343  return processReservation(res);
344 }
345 
346 RentalCarReservation ExtractorPostprocessorPrivate::processRentalCarReservation(RentalCarReservation res) const
347 {
348  if (res.reservationFor().isValid()) {
349  res.setReservationFor(processRentalCar(res.reservationFor().value<RentalCar>()));
350  }
351  res.setPickupLocation(processPlace(res.pickupLocation()));
352  res.setDropoffLocation(processPlace(res.dropoffLocation()));
353  res.setPickupTime(processTimeForLocation(res.pickupTime(), res.pickupLocation()));
354  res.setDropoffTime(processTimeForLocation(res.dropoffTime(), res.dropoffLocation()));
355  return processReservation(res);
356 }
357 
358 RentalCar ExtractorPostprocessorPrivate::processRentalCar(RentalCar car) const
359 {
360  car.setName(car.name().trimmed());
361  return car;
362 }
363 
364 FoodEstablishmentReservation ExtractorPostprocessorPrivate::processFoodEstablishmentReservation(FoodEstablishmentReservation res) const
365 {
366  if (res.reservationFor().isValid()) {
367  res.setReservationFor(processPlace(res.reservationFor().value<FoodEstablishment>()));
368  res.setStartTime(processTimeForLocation(res.startTime(), res.reservationFor().value<FoodEstablishment>()));
369  res.setEndTime(processTimeForLocation(res.endTime(), res.reservationFor().value<FoodEstablishment>()));
370  }
371  return processReservation(res);
372 }
373 
374 TouristAttractionVisit ExtractorPostprocessorPrivate::processTouristAttractionVisit(TouristAttractionVisit visit) const
375 {
376  visit.setTouristAttraction(processPlace(visit.touristAttraction()));
377  visit.setArrivalTime(processTimeForLocation(visit.arrivalTime(), visit.touristAttraction()));
378  visit.setDepartureTime(processTimeForLocation(visit.departureTime(), visit.touristAttraction()));
379  return visit;
380 }
381 
382 EventReservation ExtractorPostprocessorPrivate::processEventReservation(EventReservation res) const
383 {
384  if (res.reservationFor().isValid()) {
385  res.setReservationFor(processEvent(res.reservationFor().value<Event>()));
386  }
387  return processReservation(res);
388 }
389 
390 Event ExtractorPostprocessorPrivate::processEvent(Event event) const
391 {
392  // normalize location to be a Place
393  if (JsonLd::isA<PostalAddress>(event.location())) {
394  Place place;
395  place.setAddress(event.location().value<PostalAddress>());
396  event.setLocation(place);
397  }
398 
399  if (JsonLd::isA<Place>(event.location())) {
400  event.setLocation(processPlace(event.location().value<Place>()));
401 
402  // try to obtain timezones if we have a location
403  event.setStartDate(processTimeForLocation(event.startDate(), event.location().value<Place>()));
404  event.setEndDate(processTimeForLocation(event.endDate(), event.location().value<Place>()));
405  event.setDoorTime(processTimeForLocation(event.doorTime(), event.location().value<Place>()));
406  }
407 
408  return event;
409 }
410 
411 template <typename T>
412 T ExtractorPostprocessorPrivate::processReservation(T res) const
413 {
414  res.setUnderName(processPerson(res.underName().template value<Person>()));
415  res.setPotentialAction(processActions(res.potentialAction()));
416  return res;
417 }
418 
419 
420 Person ExtractorPostprocessorPrivate::processPerson(Person person) const
421 {
422  person.setName(person.name().simplified());
423 
424  // fill name with name parts, if it's empty
425  if ((person.name().isEmpty() || person.name() == person.familyName() || person.name() == person.givenName())
426  && !person.familyName().isEmpty() && !person.givenName().isEmpty())
427  {
428  person.setName(person.givenName() + QLatin1Char(' ') + person.familyName());
429  }
430 
431  // strip prefixes, they break comparisons
432  static const char* const honorificPrefixes[] = { "MR ", "MS ", "MRS " };
433  for (auto prefix : honorificPrefixes) {
434  if (person.name().startsWith(QLatin1String(prefix), Qt::CaseInsensitive)) {
435  person.setName(person.name().mid(strlen(prefix)));
436  break;
437  }
438  }
439 
440  return person;
441 }
442 
443 PostalAddress ExtractorPostprocessorPrivate::processAddress(PostalAddress addr, const QString &phoneNumber, const GeoCoordinates &geo)
444 {
445  // convert to ISO 3166-1 alpha-2 country codes
446  if (addr.addressCountry().size() > 2) {
447  const auto isoCode = KContacts::Address::countryToISO(addr.addressCountry()).toUpper();
448  if (!isoCode.isEmpty()) {
449  addr.setAddressCountry(isoCode);
450 
451  // try ISO 3166-1 alpha-3, we get that e.g. from Flixbus
452  } else if (addr.addressCountry().size() == 3) {
454  if (c.isValid()) {
455  addr.setAddressCountry(c.toString());
456  }
457  }
458  }
459 
460  // upper case country codes
461  if (addr.addressCountry().size() == 2) {
462  addr.setAddressCountry(addr.addressCountry().toUpper());
463  }
464 
465  // normalize strings
466  addr.setStreetAddress(addr.streetAddress().simplified());
467  addr.setAddressLocality(addr.addressLocality().simplified());
468  addr.setAddressRegion(addr.addressRegion().simplified());
469 
470 #ifdef HAVE_PHONENUMBER
471  // recover country from phone number, if we have that
472  if (!phoneNumber.isEmpty() && addr.addressCountry().size() != 2) {
473  const auto phoneStr = phoneNumber.toStdString();
474  const auto util = i18n::phonenumbers::PhoneNumberUtil::GetInstance();
475  i18n::phonenumbers::PhoneNumber number;
476  if (util->ParseAndKeepRawInput(phoneStr, "ZZ", &number) == i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) {
477  std::string isoCode;
478  util->GetRegionCodeForNumber(number, &isoCode);
479  if (!isoCode.empty()) {
480  addr.setAddressCountry(QString::fromStdString(isoCode));
481  }
482  }
483  }
484 #endif
485 
486  if (geo.isValid() && addr.addressCountry().isEmpty()) {
487  const auto isoCode = KnowledgeDb::countryForCoordinate(geo.latitude(), geo.longitude());
488  if (isoCode.isValid()) {
489  addr.setAddressCountry(isoCode.toString());
490  }
491  }
492 
494  return addr;
495 }
496 
497 QString ExtractorPostprocessorPrivate::processPhoneNumber(const QString &phoneNumber, const PostalAddress &addr)
498 {
499 #ifdef HAVE_PHONENUMBER
500  // or complete the phone number if we know the country
501  if (!phoneNumber.isEmpty() && addr.addressCountry().size() == 2) {
502  auto phoneStr = phoneNumber.toStdString();
503  const auto isoCode = addr.addressCountry().toStdString();
504  const auto util = i18n::phonenumbers::PhoneNumberUtil::GetInstance();
505  i18n::phonenumbers::PhoneNumber number;
506  if (util->ParseAndKeepRawInput(phoneStr, isoCode, &number) == i18n::phonenumbers::PhoneNumberUtil::NO_PARSING_ERROR) {
507  if (number.country_code_source() == i18n::phonenumbers::PhoneNumber_CountryCodeSource_FROM_DEFAULT_COUNTRY) {
508  util->Format(number, i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL, &phoneStr);
509  return QString::fromStdString(phoneStr);
510  }
511  }
512  }
513 #else
514  Q_UNUSED(addr);
515 #endif
516  return phoneNumber;
517 }
518 
519 QVariantList ExtractorPostprocessorPrivate::processActions(QVariantList actions) const
520 {
521  // remove non-actions and actions with invalid URLs
522  QUrl viewUrl;
523  for (auto it = actions.begin(); it != actions.end();) {
524  if (!JsonLd::canConvert<Action>(*it)) {
525  it = actions.erase(it);
526  continue;
527  }
528 
529  const auto action = JsonLd::convert<Action>(*it);
530  if (!action.target().isValid()) {
531  it = actions.erase(it);
532  continue;
533  }
534 
535  if (JsonLd::isA<ViewAction>(*it)) {
536  viewUrl = action.target();
537  }
538  ++it;
539  }
540 
541  // normalize the order, so JSON comparison still yields correct results
542  std::sort(actions.begin(), actions.end(), [](const QVariant &lhs, const QVariant &rhs) {
543  return strcmp(lhs.typeName(), rhs.typeName()) < 0;
544  });
545 
546  // remove actions that don't actually have their own target, or duplicates
547  QUrl prevUrl;
548  const char* prevType = nullptr;
549  for (auto it = actions.begin(); it != actions.end();) {
550  const auto action = JsonLd::convert<Action>(*it);
551  const auto isDuplicate = action.target() == prevUrl && (prevType ? strcmp(prevType, (*it).typeName()) == 0 : false);
552  if ((JsonLd::isA<ViewAction>(*it) || action.target() != viewUrl) && !isDuplicate) {
553  prevUrl = action.target();
554  prevType = (*it).typeName();
555  ++it;
556  } else {
557  it = actions.erase(it);
558  }
559  }
560 
561  return actions;
562 }
563 
564 template <typename T>
565 QDateTime ExtractorPostprocessorPrivate::processTimeForLocation(QDateTime dt, const T &place) const
566 {
567  if (!dt.isValid() || dt.timeSpec() == Qt::TimeZone) {
568  return dt;
569  }
570 
571  QTimeZone tz;
572  if (!place.address().addressCountry().isEmpty()) {
573  tz = KnowledgeDb::toQTimeZone(KnowledgeDb::timezoneForLocation(place.geo().latitude(), place.geo().longitude(),
574  KnowledgeDb::CountryId{place.address().addressCountry()}));
575  }
576  if (!tz.isValid()) {
577  return dt;
578  }
579 
580  // prefer our timezone over externally provided UTC offset, if they match
581  if (dt.timeSpec() == Qt::OffsetFromUTC && tz.offsetFromUtc(dt) != dt.offsetFromUtc()) {
582  qCDebug(Log) << "UTC offset clashes with expected timezone!" << dt << dt.offsetFromUtc() << tz.id() << tz.offsetFromUtc(dt);
583  return dt;
584  }
585 
586  if (dt.timeSpec() == Qt::OffsetFromUTC || dt.timeSpec() == Qt::LocalTime) {
587  dt.setTimeSpec(Qt::TimeZone);
588  dt.setTimeZone(tz);
589  } else if (dt.timeSpec() == Qt::UTC) {
590  dt = dt.toTimeZone(tz);
591  }
592  return dt;
593 }
An event reservation.
Definition: reservation.h:138
std::string toStdString() const const
static QVariant apply(const QVariant &lhs, const QVariant &rhs)
Apply all properties of rhs on to lhs.
QString toUpper() const const
constexpr bool isValid() const
Returns true if this is a valid idenfier.
Definition: alphaid.h:67
TrainStation stationForSncfStationId(SncfStationId sncfId)
Lookup train station data by SNCF station id.
Classes for reservation/travel data models, data extraction and data augmentation.
A restaurant reservation.
Definition: reservation.h:126
A hotel reservation.
Definition: reservation.h:80
bool isBefore(const QVariant &lhs, const QVariant &rhs)
Sorting function for top-level reservation/visit/event elements.
Definition: sortutil.cpp:149
QString toString() const
Returns a string representation of this identifier.
Definition: alphaid.h:86
static QString countryToISO(const QString &cname)
Train station entry in the station table.
TrainStation stationForUic(UICStation uic)
Lookup train station data by UIC station id.
TrainStation stationForIbnr(IBNR ibnr)
Lookup train station data by IBNR.
Train station.
Definition: place.h:116
CountryId countryIdFromIso3166_1alpha3(CountryId3 iso3Code)
Look up country ISO 3166-1 alpha 2 code from an ISO 3166-1 alpha 3 code.
Definition: countrydb.cpp:102
int size() const const
int offsetFromUtc(const QDateTime &atDateTime) const const
QString simplified() const const
T value() const const
A Taxi reservation.
Definition: reservation.h:158
Post-process extracted data to filter out garbage and augment data from other sources.
A flight reservation.
Definition: reservation.h:91
Food-related business (such as a restaurant, or a bakery).
Definition: organization.h:93
Base class for places.
Definition: place.h:80
A car rental.
Definition: rentalcar.h:33
Tz timezoneForLocation(float lat, float lon, CountryId country)
Returns the timezone for the given location consisting of coordinates and country.
Definition: timezonedb.cpp:110
QDateTime toTimeZone(const QTimeZone &timeZone) const const
void setTimeZone(const QTimeZone &toZone)
void setTimeSpec(Qt::TimeSpec spec)
A flight.
Definition: flight.h:36
CountryId countryForCoordinate(float lat, float lon)
Returns the country for the given coordinate.
Definition: timezonedb.cpp:164
QString fromStdString(const std::string &str)
A bus trip.
Definition: bustrip.h:33
A booked ticket.
Definition: ticket.h:47
TrainStation stationForIndianRailwaysStationCode(const QString &code)
Lookup train station data by Indian Railways station code.
QVector< QVariant > parse(const QString &message, const QDate &externalIssueDate=QDate())
Parses the bar coded boarding pass message message into a list of FlightReservation instances...
bool isEmpty() const const
QString trimmed() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
Geographic coordinates.
Definition: place.h:34
A Rental Car reservation.
Definition: reservation.h:146
A bus reservation.
Definition: reservation.h:117
CountryId countryIdForUicCode(uint16_t uicCountryCode)
Look up country ISO code from a UIC country code.
Definition: countrydb.cpp:114
QString addressCountry
The country this address is in, as ISO 3166-1 alpha 2 code.
Definition: place.h:65
Qt::TimeSpec timeSpec() const const
Postal address.
Definition: place.h:57
QString identifier
Identifier.
Definition: place.h:97
QByteArray id() const const
bool isValid() const const
QString mid(int position, int n) const const
A person.
Definition: person.h:31
Tz timezoneForCountry(CountryId country)
Returns the timezone for the given country, as long as there is exactly one timezone used in that cou...
Definition: timezonedb.cpp:40
void setValidationEnabled(bool validate)
Enable or disable validation.
TrainStation stationForVRStationCode(VRStationCode vrStation)
Lookup train station data by VR (Finland) station code.
const char * typeName() const const
void setContextDate(const QDateTime &dt)
The date the reservation(s) processed here have been made, if known.
bool isValid() const const
PostalAddress extractPostalCode(PostalAddress addr)
Try to extract postal codes included in the city name field.
bool isValid() const const
QVector< QVariant > result() const
This returns the final result of all previously executed processing steps followed by sorting and fil...
An event.
Definition: event.h:32
bool isSame(const QVariant &lhs, const QVariant &rhs)
Checks if two Reservation or Trip values refer to the same booking element.
Definition: mergeutil.cpp:84
A train reservation.
Definition: reservation.h:109
A train trip.
Definition: traintrip.h:35
int offsetFromUtc() const const
VR (Finland) station codes.
QVariant merge(const QVariant &lhs, const QVariant &rhs)
Merge the two given objects.
Definition: mergeutil.cpp:548
QTimeZone toQTimeZone(Tz tz)
Returns the corresponding QTimeZone.
Definition: timezonedb.cpp:32
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Tue May 26 2020 22:42:18 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.