9#include "uic9183ticketlayout.h"
11#include "text/pricefinder_p.h"
15#include <QRegularExpression>
27 QDate firstDayOfValidity()
const;
37QDate Rct2TicketPrivate::firstDayOfValidity()
const
39 const auto f = layout.
text(3, 1, 48, 1);
40 const auto it = std::find_if(f.begin(), f.end(), [](
QChar c) { return c.isDigit(); });
44 const auto dtStr =
QStringView(f).
mid(std::distance(f.begin(), it));
45 auto dt =
QDate::fromString(dtStr.left(10).toString(), QStringLiteral(
"dd.MM.yyyy"));
51 if (dt.year() < 2000) {
52 dt.setDate(dt.year() + 100, dt.month(), dt.day());
74 const auto validDt = firstDayOfValidity();
75 const auto baseDate = validDt.isValid() ? validDt : contextDt.
date();
76 auto dt =
QDateTime({baseDate.year(), d.month(), d.day()}, t);
77 if (dt.isValid() && dt.date() < baseDate) {
83static constexpr const char* res_patterns[] = {
84 "ZUG +(?P<train_number>\\d+) +(?P<train_category>[A-Z][A-Z0-9]+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)",
85 "ZUG +(?P<train_number>\\d+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)",
90 const auto text = layout.
text(8, 0, 72, 1);
91 for (
const auto *pattern : res_patterns) {
94 Q_ASSERT(re.isValid());
95 const auto match = re.match(text);
96 if (
match.hasMatch()) {
97 return match.captured(name);
110Rct2Ticket::Rct2Ticket()
111 : d(new Rct2TicketPrivate)
116 : d(new Rct2TicketPrivate)
121Rct2Ticket::Rct2Ticket(
const Rct2Ticket&) =
default;
122Rct2Ticket::~Rct2Ticket() =
default;
132 d->contextDt = contextDt;
135QDate Rct2Ticket::firstDayOfValidity()
const
137 return d->firstDayOfValidity();
140static constexpr const struct {
143} rct2_ticket_type_map[] = {
167 const auto typeName1 = d->layout.text(0, 14, 38, 1).trimmed().remove(
QLatin1Char(
' ')).toCaseFolded();
168 const auto typeName2 = d->layout.text(1, 14, 38, 1).trimmed().remove(
QLatin1Char(
' ')).toCaseFolded();
171 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
177 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
185 for (
const auto &f : d->layout.fields(0, 14, 38, 2)) {
186 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
200 return d->layout.text(0, 18, 19, 1);
204 if (d->layout.text(0, 15, 3, 1).trimmed().isEmpty()) {
205 const auto s = d->layout.text(0, 18, 33, 1).trimmed();
206 return s.isEmpty() ? d->layout.text(1, 18, 33, 1).trimmed() : s;
210 return d->layout.text(0, 0, 52, 1).trimmed();
215 const auto name = d->layout.text(0, 52, 19, 1).trimmed();
217 return std::any_of(name.begin(), name.end(), [](
QChar c) { return c.isDigit(); }) ?
QString() : name;
222 return d->parseTime(d->layout.text(6, 1, 5, 1).trimmed(), d->layout.text(6, 7, 5, 1).trimmed());
227 auto dt = d->parseTime(d->layout.text(6, 52, 5, 1).trimmed(), d->layout.text(6, 58, 5, 1).trimmed());
228 if (dt.isValid() && dt < outboundDepartureTime()) {
237 if (std::all_of(s.
begin(), s.
end(), [](
QChar c) { return c == QLatin1Char(
'*'); })) {
243QString Rct2Ticket::outboundDepartureStation()
const
250 const auto fields = d->layout.containedFields(6, 13, 17, 1);
251 if (fields.size() == 1) {
252 return rct2Clean(fields[0].text().trimmed());
254 return rct2Clean(d->layout.text(6, 12, 18, 1).trimmed());
257QString Rct2Ticket::outboundArrivalStation()
const
259 return type() !=
RailPass ? rct2Clean(d->layout.text(6, 34, 17, 1).trimmed()) :
QString();
264 return rct2Clean(d->layout.text(6, 66, 5, 1).trimmed());
269 return d->parseTime(d->layout.text(7, 1, 5, 1).trimmed(), d->layout.text(7, 7, 5, 1).trimmed());
274 auto dt = d->parseTime(d->layout.text(7, 52, 5, 1).trimmed(), d->layout.text(7, 58, 5, 1).trimmed());
275 if (dt.isValid() && dt < returnDepartureTime()) {
281QString Rct2Ticket::returnDepartureStation()
const
284 return type() !=
RailPass ? rct2Clean(d->layout.text(7, 12, 18, 1).trimmed()) :
QString();
287QString Rct2Ticket::returnArrivalStation()
const
289 return type() !=
RailPass ? rct2Clean(d->layout.text(7, 34, 17, 1).trimmed()) :
QString();
294 return rct2Clean(d->layout.text(7, 66, 5, 1).trimmed());
299 const auto t = type();
301 auto num = d->reservationPatternCapture(u
"train_number");
302 if (!num.isEmpty()) {
303 return d->reservationPatternCapture(u
"train_category") +
QLatin1Char(
' ') + num;
306 const auto cat = d->layout.text(8, 13, 3, 1).trimmed();
307 num = d->layout.text(8, 7, 5, 1).trimmed();
310 if (num.isEmpty() || num.at(0).isDigit()) {
311 const auto numPrefix = d->layout.text(8, 1, 6, 1);
312 for (
int i = numPrefix.size() - 1; i >= 0; --i) {
313 if (numPrefix.at(i).isDigit()) {
314 num.prepend(numPrefix.at(i));
322 if (!cat.isEmpty()) {
332 const auto t = type();
334 const auto coach = d->reservationPatternCapture(u
"coach");
335 return coach.isEmpty() ? d->layout.text(8, 26, 3, 1).trimmed() : coach;
342 const auto t = type();
344 const auto seat = d->reservationPatternCapture(u
"seat");
345 if (!seat.isEmpty()) {
349 const auto row8 = d->layout.text(8, 48, 23, 1).trimmed();
350 if (!row8.isEmpty()) {
354 return d->layout.text(9, 32, 19, 2).simplified();
361 std::vector<PriceFinder::Result> result;
363 finder.findAll(d->layout.text(13, 52, 19, 1).remove(
QLatin1Char(
'*')), result);
364 return result.
size() == 1 ? result[0].currency :
QString();
367double Rct2Ticket::price()
const
369 std::vector<PriceFinder::Result> result;
371 finder.findAll(d->layout.text(13, 52, 19, 1).remove(
QLatin1Char(
'*')), result);
372 return result.size() == 1 ? result[0].value : NAN;
375#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)
QDateTime addYears(int nyears) const const
qsizetype size() const const
QStringView mid(qsizetype start, qsizetype length) const const
QTime fromString(QStringView string, QStringView format)