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