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(); });
45 const auto dtStr =
QStringView(f).
mid(std::distance(f.begin(), it));
46 auto dt =
QDate::fromString(dtStr.left(10).toString(), QStringLiteral(
"dd.MM.yyyy"));
52 if (dt.year() < 2000) {
53 dt.setDate(dt.year() + 100, dt.month(), dt.day());
75 const auto validDt = firstDayOfValidity();
76 const auto baseDate = validDt.isValid() ? validDt : contextDt.
date();
77 auto dt =
QDateTime({baseDate.year(), d.month(), d.day()}, t);
78 if (dt.isValid() && dt.date() < baseDate) {
84static constexpr const char* res_patterns[] = {
85 "ZUG +(?P<train_number>\\d+) +(?P<train_category>[A-Z][A-Z0-9]+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)",
86 "ZUG +(?P<train_number>\\d+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)",
91 const auto text = layout.
text(8, 0, 72, 1);
92 for (
const auto *pattern : res_patterns) {
95 Q_ASSERT(re.isValid());
96 const auto match = re.match(text);
97 if (
match.hasMatch()) {
98 return match.captured(name);
111Rct2Ticket::Rct2Ticket()
112 : d(new Rct2TicketPrivate)
117 : d(new Rct2TicketPrivate)
122Rct2Ticket::Rct2Ticket(
const Rct2Ticket&) =
default;
123Rct2Ticket::~Rct2Ticket() =
default;
129 return d->layout.isValid() && (d->layout.type() ==
"RCT2"_L1 || d->layout.type() ==
"RTC2"_L1);
134 d->contextDt = contextDt;
137QDate Rct2Ticket::firstDayOfValidity()
const
139 return d->firstDayOfValidity();
142static constexpr const struct {
145} rct2_ticket_type_map[] = {
171 const auto typeName1 = d->layout.text(0, 14, 38, 1).trimmed().remove(
QLatin1Char(
' ')).toCaseFolded();
172 const auto typeName2 = d->layout.text(1, 14, 38, 1).trimmed().remove(
QLatin1Char(
' ')).toCaseFolded();
175 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
181 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
189 for (
const auto &f : d->layout.fields(0, 14, 38, 2)) {
190 for (
auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) {
204 return d->layout.text(0, 18, 19, 1);
208 if (d->layout.text(0, 15, 3, 1).trimmed().isEmpty()) {
209 const auto s = d->layout.text(0, 18, 33, 1).trimmed();
210 return s.isEmpty() ? d->layout.text(1, 18, 33, 1).trimmed() : s;
214 return d->layout.text(0, 0, 52, 1).trimmed();
219 const auto name = d->layout.text(0, 52, 19, 1).trimmed();
221 return std::any_of(name.begin(), name.end(), [](
QChar c) { return c.isDigit(); }) ?
QString() : name;
226 return d->parseTime(d->layout.text(6, 1, 5, 1).trimmed(), d->layout.text(6, 7, 5, 1).trimmed());
231 auto dt = d->parseTime(d->layout.text(6, 52, 5, 1).trimmed(), d->layout.text(6, 58, 5, 1).trimmed());
232 if (dt.isValid() && dt < outboundDepartureTime()) {
241 if (std::all_of(s.
begin(), s.
end(), [](
QChar c) { return c == QLatin1Char(
'*'); })) {
247QString Rct2Ticket::outboundDepartureStation()
const
254 const auto fields = d->layout.containedFields(6, 13, 17, 1);
255 if (fields.size() == 1) {
256 return rct2Clean(fields[0].text().trimmed());
258 return rct2Clean(d->layout.text(6, 12, 18, 1).trimmed());
261QString Rct2Ticket::outboundArrivalStation()
const
263 return type() !=
RailPass ? rct2Clean(d->layout.text(6, 34, 17, 1).trimmed()) :
QString();
268 return rct2Clean(d->layout.text(6, 66, 5, 1).trimmed());
273 return d->parseTime(d->layout.text(7, 1, 5, 1).trimmed(), d->layout.text(7, 7, 5, 1).trimmed());
278 auto dt = d->parseTime(d->layout.text(7, 52, 5, 1).trimmed(), d->layout.text(7, 58, 5, 1).trimmed());
279 if (dt.isValid() && dt < returnDepartureTime()) {
285QString Rct2Ticket::returnDepartureStation()
const
288 return type() !=
RailPass ? rct2Clean(d->layout.text(7, 12, 18, 1).trimmed()) :
QString();
291QString Rct2Ticket::returnArrivalStation()
const
293 return type() !=
RailPass ? rct2Clean(d->layout.text(7, 34, 17, 1).trimmed()) :
QString();
298 return rct2Clean(d->layout.text(7, 66, 5, 1).trimmed());
303 const auto t = type();
305 auto num = d->reservationPatternCapture(u
"train_number");
306 if (!num.isEmpty()) {
307 return d->reservationPatternCapture(u
"train_category") +
QLatin1Char(
' ') + num;
310 const auto cat = d->layout.text(8, 13, 3, 1).trimmed();
311 num = d->layout.text(8, 7, 5, 1).trimmed();
314 if (num.isEmpty() || num.at(0).isDigit()) {
315 const auto numPrefix = d->layout.text(8, 1, 6, 1);
316 for (
int i = numPrefix.size() - 1; i >= 0; --i) {
317 if (numPrefix.at(i).isDigit()) {
318 num.prepend(numPrefix.at(i));
326 if (!cat.isEmpty()) {
336 const auto t = type();
338 const auto coach = d->reservationPatternCapture(u
"coach");
339 return coach.isEmpty() ? d->layout.text(8, 26, 3, 1).trimmed() : coach;
346 const auto t = type();
348 const auto seat = d->reservationPatternCapture(u
"seat");
349 if (!seat.isEmpty()) {
353 const auto row8 = d->layout.text(8, 48, 23, 1).trimmed();
354 if (!row8.isEmpty()) {
358 return d->layout.text(9, 32, 19, 2).simplified();
365 std::vector<PriceFinder::Result> result;
367 finder.findAll(d->layout.text(13, 52, 19, 1).remove(
QLatin1Char(
'*')), result);
368 return result.
size() == 1 ? result[0].currency :
QString();
371double Rct2Ticket::price()
const
373 std::vector<PriceFinder::Result> result;
375 finder.findAll(d->layout.text(13, 52, 19, 1).remove(
QLatin1Char(
'*')), result);
376 return result.size() == 1 ? result[0].value : NAN;
379#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)