9#include "uic9183ticketlayout.h"
11#include "text/pricefinder_p.h"
15#include <QRegularExpression>
28 QDate firstDayOfValidity()
const;
38QDate Rct2TicketPrivate::firstDayOfValidity()
const
40 const auto f = layout.
text(3, 1, 48, 1);
41 const auto it = std::find_if(f.begin(), f.end(), [](
QChar c) { return c.isDigit(); });
46 const auto dtStr =
QStringView(f).
mid(std::distance(f.begin(), it));
47 for (
const auto format : {
"dd.MM.yyyy"_L1,
"dd/MM/yyyy"_L1,
"dd.MM.yy"_L1,
"yyyy"_L1 }) {
50 if (dt.year() < 2000) {
51 dt.setDate(dt.year() + 100, dt.month(), dt.day());
73 const auto validDt = firstDayOfValidity();
74 const auto baseDate = validDt.isValid() ? validDt : contextDt.
date();
75 auto dt =
QDateTime({baseDate.year(), d.month(), d.day()}, t);
76 if (dt.isValid() && dt.date() < baseDate) {
82static constexpr const char* res_patterns[] = {
83 "ZUG +(?P<train_number>\\d+) +(?P<train_category>[A-Z][A-Z0-9]+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)",
84 "ZUG +(?P<train_number>\\d+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)",
89 const auto text = layout.
text(8, 0, 72, 1);
90 for (
const auto *pattern : res_patterns) {
93 Q_ASSERT(re.isValid());
94 const auto match = re.match(text);
95 if (
match.hasMatch()) {
96 return match.captured(name);
109Rct2Ticket::Rct2Ticket()
110 : d(new Rct2TicketPrivate)
115 : d(new Rct2TicketPrivate)
120Rct2Ticket::Rct2Ticket(
const Rct2Ticket&) =
default;
121Rct2Ticket::~Rct2Ticket() =
default;
127 return d->layout.isValid() && (d->layout.type() ==
"RCT2"_L1 || d->layout.type() ==
"RTC2"_L1);
132 d->contextDt = contextDt;
135QDate Rct2Ticket::firstDayOfValidity()
const
137 return d->firstDayOfValidity();
140static constexpr const struct {
143} rct2_ticket_type_map[] = {
170 const auto typeName1 = d->layout.text(0, 14, 38, 1).trimmed().remove(
QLatin1Char(
' ')).toCaseFolded();
171 const auto typeName2 = d->layout.text(1, 14, 38, 1).trimmed().remove(
QLatin1Char(
' ')).toCaseFolded();
174 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
180 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
188 for (
const auto &f : d->layout.fields(0, 14, 38, 2)) {
189 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
203 return d->layout.text(0, 18, 19, 1);
207 if (d->layout.text(0, 15, 3, 1).trimmed().isEmpty()) {
208 const auto s = d->layout.text(0, 18, 33, 1).trimmed();
209 return s.isEmpty() ? d->layout.text(1, 18, 33, 1).trimmed() : s;
213 return d->layout.text(0, 0, 52, 1).trimmed();
218 const auto name = d->layout.text(0, 52, 19, 1).trimmed();
220 return std::any_of(name.begin(), name.end(), [](
QChar c) { return c.isDigit(); }) ?
QString() : name;
225 return d->parseTime(d->layout.text(6, 1, 5, 1).trimmed(), d->layout.text(6, 7, 5, 1).trimmed());
230 auto dt = d->parseTime(d->layout.text(6, 52, 5, 1).trimmed(), d->layout.text(6, 58, 5, 1).trimmed());
231 if (dt.isValid() && dt < outboundDepartureTime()) {
240 if (std::all_of(s.
begin(), s.
end(), [](
QChar c) { return c == QLatin1Char(
'*'); })) {
246QString Rct2Ticket::outboundDepartureStation()
const
253 const auto fields = d->layout.containedFields(6, 13, 17, 1);
254 if (fields.size() == 1) {
255 return rct2Clean(fields[0].text().trimmed());
257 return rct2Clean(d->layout.text(6, 12, 18, 1).trimmed());
260QString Rct2Ticket::outboundArrivalStation()
const
262 return type() !=
RailPass ? rct2Clean(d->layout.text(6, 34, 17, 1).trimmed()) :
QString();
267 return rct2Clean(d->layout.text(6, 66, 5, 1).trimmed());
272 return d->parseTime(d->layout.text(7, 1, 5, 1).trimmed(), d->layout.text(7, 7, 5, 1).trimmed());
277 auto dt = d->parseTime(d->layout.text(7, 52, 5, 1).trimmed(), d->layout.text(7, 58, 5, 1).trimmed());
278 if (dt.isValid() && dt < returnDepartureTime()) {
284QString Rct2Ticket::returnDepartureStation()
const
287 return type() !=
RailPass ? rct2Clean(d->layout.text(7, 12, 18, 1).trimmed()) :
QString();
290QString Rct2Ticket::returnArrivalStation()
const
292 return type() !=
RailPass ? rct2Clean(d->layout.text(7, 34, 17, 1).trimmed()) :
QString();
297 return rct2Clean(d->layout.text(7, 66, 5, 1).trimmed());
302 const auto t = type();
304 auto num = d->reservationPatternCapture(u
"train_number");
305 if (!num.isEmpty()) {
306 return d->reservationPatternCapture(u
"train_category") +
QLatin1Char(
' ') + num;
309 const auto cat = d->layout.text(8, 13, 3, 1).trimmed();
310 num = d->layout.text(8, 7, 5, 1).trimmed();
313 if (num.isEmpty() || num.at(0).isDigit()) {
314 const auto numPrefix = d->layout.text(8, 1, 6, 1);
315 for (
int i = numPrefix.size() - 1; i >= 0; --i) {
316 if (numPrefix.at(i).isDigit()) {
317 num.prepend(numPrefix.at(i));
325 if (!cat.isEmpty()) {
335 const auto t = type();
337 const auto coach = d->reservationPatternCapture(u
"coach");
338 return coach.isEmpty() ? d->layout.text(8, 26, 3, 1).trimmed() : coach;
345 const auto t = type();
347 const auto seat = d->reservationPatternCapture(u
"seat");
348 if (!seat.isEmpty()) {
352 const auto row8 = d->layout.text(8, 48, 23, 1).trimmed();
353 if (!row8.isEmpty()) {
357 return d->layout.text(9, 32, 19, 2).simplified();
364 std::vector<PriceFinder::Result> result;
366 finder.findAll(d->layout.text(13, 52, 19, 1).remove(
QLatin1Char(
'*')), result);
367 return result.
size() == 1 ? result[0].currency :
QString();
370double Rct2Ticket::price()
const
372 std::vector<PriceFinder::Result> result;
374 finder.findAll(d->layout.text(13, 52, 19, 1).remove(
QLatin1Char(
'*')), result);
375 return result.size() == 1 ? result[0].value : NAN;
378#include "moc_rct2ticket.cpp"
RCT2 ticket layout payload of an UIC 918.3 ticket token.
@ Transport
Non-integrated Reservation Ticket (NRT)
@ RailPass
Rail Pass Ticket (RPT)
@ Upgrade
Update Document (UPG)
@ TransportReservation
Integration Reservation Ticket (IRT)
@ Unknown
ticket type could not be detected, or ticket type not supported yet
@ Reservation
Reservation Only Document (RES)
void setContextDate(const QDateTime &contextDt)
Date/time this ticket was first encountered, to recover possibly missing year numbers.
bool isValid() const
Returns whether this is a valid RCT2 ticket layout block.
Abstract base class for reservations.
Parser for a U_TLAY block in a UIC 918-3 ticket container, such as a ERA TLB ticket.
Q_INVOKABLE QString text(int row, int column, int width, int height) const
Returns the text in the given coordinates.
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
Classes for reservation/travel data models, data extraction and data augmentation.
QDate fromString(QStringView string, QStringView format, QCalendar cal)
qsizetype size() const const
QStringView mid(qsizetype start, qsizetype length) const const
QTime fromString(QStringView string, QStringView format)