1 /*
2 SPDX-FileCopyrightText: 2018-2021 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "uic9183documentprocessor.h"
8
9 #include <KItinerary/ExtractorResult>
10 #include <KItinerary/JsonLdDocument>
11 #include <KItinerary/Uic9183Parser>
12 #include <KItinerary/Rct2Ticket>
13 #include <uic9183/uic9183head.h>
14
15 #include <KLocalizedString>
16
17 #include <QDateTime>
18 #include <QJsonArray>
19 #include <QJsonObject>
20
21 using namespace KItinerary;
22
canHandleData(const QByteArray & encodedData,QStringView fileName) const23 bool Uic9183DocumentProcessor::canHandleData(const QByteArray &encodedData, [[maybe_unused]] QStringView fileName) const
24 {
25 return Uic9183Parser::maybeUic9183(encodedData);
26 }
27
createNodeFromData(const QByteArray & encodedData) const28 ExtractorDocumentNode Uic9183DocumentProcessor::createNodeFromData(const QByteArray &encodedData) const
29 {
30 Uic9183Parser p;
31 p.parse(encodedData);
32 if (!p.isValid()) {
33 return {};
34 }
35
36 ExtractorDocumentNode node;
37 node.setContent(p);
38 return node;
39 }
40
expandNode(ExtractorDocumentNode & node,const ExtractorEngine * engine) const41 void Uic9183DocumentProcessor::expandNode(ExtractorDocumentNode &node, [[maybe_unused]] const ExtractorEngine *engine) const
42 {
43 // only use the U_HEAD issuing time as context if we have nothing better
44 // while that is usually correct it cannot contain a time zone, unlike the (often) enclosing PDF document´
45 if (!node.contextDateTime().isValid()) {
46 const auto p = node.content<Uic9183Parser>();
47 const auto u_head = p.findBlock<Uic9183Head>();
48 node.setContextDateTime(u_head.issuingDateTime());
49 }
50 }
51
preExtract(ExtractorDocumentNode & node,const ExtractorEngine * engine) const52 void Uic9183DocumentProcessor::preExtract(ExtractorDocumentNode &node, [[maybe_unused]] const ExtractorEngine *engine) const
53 {
54 const auto p = node.content<Uic9183Parser>();
55
56 QJsonObject trip;
57 trip.insert(QLatin1String("@type"), QLatin1String("TrainTrip"));
58 QJsonObject provider;
59 provider.insert(QLatin1String("@type"), QLatin1String("Organization"));
60 provider.insert(QLatin1String("identifier"), QJsonValue(QLatin1String("uic:") + p.carrierId()));
61 trip.insert(QLatin1String("provider"), provider);
62 QJsonObject seat;
63 seat.insert(QLatin1String("@type"), QLatin1String("Seat"));
64 seat.insert(QLatin1String("seatingType"), p.seatingType());
65
66 const auto rct2 = p.rct2Ticket();
67 if (rct2.isValid()) {
68 switch (rct2.type()) {
69 case Rct2Ticket::Reservation:
70 case Rct2Ticket::TransportReservation:
71 {
72 trip.insert(QStringLiteral("trainNumber"), rct2.trainNumber());
73 seat.insert(QStringLiteral("seatSection"), rct2.coachNumber());
74 seat.insert(QStringLiteral("seatNumber"), rct2.seatNumber());
75 Q_FALLTHROUGH();
76 }
77 case Rct2Ticket::Transport:
78 case Rct2Ticket::Upgrade:
79 {
80 QJsonObject dep;
81 dep.insert(QStringLiteral("@type"), QLatin1String("TrainStation"));
82 dep.insert(QStringLiteral("name"), rct2.outboundDepartureStation());
83 trip.insert(QStringLiteral("departureStation"), dep);
84
85 QJsonObject arr;
86 arr.insert(QStringLiteral("@type"), QLatin1String("TrainStation"));
87 arr.insert(QStringLiteral("name"), rct2.outboundArrivalStation());
88 trip.insert(QStringLiteral("arrivalStation"), arr);
89
90 if (rct2.outboundDepartureTime().isValid()) {
91 trip.insert(QStringLiteral("departureDay"), rct2.outboundDepartureTime().date().toString(Qt::ISODate));
92 } else {
93 trip.insert(QStringLiteral("departureDay"), rct2.firstDayOfValidity().toString(Qt::ISODate));
94 }
95
96 if (rct2.outboundDepartureTime() != rct2.outboundArrivalTime()) {
97 trip.insert(QStringLiteral("departureTime"), rct2.outboundDepartureTime().toString(Qt::ISODate));
98 trip.insert(QStringLiteral("arrivalTime"), rct2.outboundArrivalTime().toString(Qt::ISODate));
99 }
100
101 break;
102 }
103 default:
104 break;
105 }
106 }
107
108 QJsonObject ticket;
109 ticket.insert(QStringLiteral("@type"), QLatin1String("Ticket"));
110 ticket.insert(QStringLiteral("ticketToken"), QString(QLatin1String("aztecbin:") + QString::fromLatin1(p.rawData().toBase64())));
111 ticket.insert(QStringLiteral("ticketedSeat"), seat);
112 if (rct2.isValid()) {
113 switch (rct2.type()) { // provide names for typically "addon" tickets, so we can distinguish them in the UI
114 case Rct2Ticket::Reservation:
115 ticket.insert(QStringLiteral("name"), i18n("Reservation"));
116 break;
117 case Rct2Ticket::Upgrade:
118 ticket.insert(QStringLiteral("name"), i18n("Upgrade"));
119 break;
120 default:
121 break;
122 }
123 }
124
125 QJsonObject res;
126 res.insert(QStringLiteral("@type"), QLatin1String("TrainReservation"));
127 if (trip.size() > 1) {
128 res.insert(QStringLiteral("reservationFor"), trip);
129 }
130 res.insert(QStringLiteral("reservationNumber"), p.pnr());
131 res.insert(QStringLiteral("reservedTicket"), ticket);
132 res.insert(QStringLiteral("underName"), JsonLdDocument::toJson(p.person()));
133 node.addResult(QJsonArray({res}));
134 }
135
136