Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kitinerary for openSUSE:Factory checked in at 2022-09-09 18:23:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitinerary (Old) and /work/SRC/openSUSE:Factory/.kitinerary.new.2083 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitinerary" Fri Sep 9 18:23:28 2022 rev:53 rq:1002039 version:22.08.1 Changes: -------- --- /work/SRC/openSUSE:Factory/kitinerary/kitinerary.changes 2022-08-19 17:52:24.367588265 +0200 +++ /work/SRC/openSUSE:Factory/.kitinerary.new.2083/kitinerary.changes 2022-09-09 18:23:46.848309761 +0200 @@ -1,0 +2,8 @@ +Tue Sep 6 15:19:59 UTC 2022 - Christophe Giboudeaux <christo...@krop.fr> + +- Update to 22.08.1 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/gear/22.08.1/ + +------------------------------------------------------------------- Old: ---- kitinerary-22.08.0.tar.xz kitinerary-22.08.0.tar.xz.sig New: ---- kitinerary-22.08.1.tar.xz kitinerary-22.08.1.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitinerary.spec ++++++ --- /var/tmp/diff_new_pack.4Ll9jk/_old 2022-09-09 18:23:47.336311199 +0200 +++ /var/tmp/diff_new_pack.4Ll9jk/_new 2022-09-09 18:23:47.340311211 +0200 @@ -18,7 +18,7 @@ %bcond_without released Name: kitinerary -Version: 22.08.0 +Version: 22.08.1 Release: 0 Summary: Data model and extraction system for travel reservations License: LGPL-2.1-or-later ++++++ kitinerary-22.08.0.tar.xz -> kitinerary-22.08.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/CMakeLists.txt new/kitinerary-22.08.1/CMakeLists.txt --- old/kitinerary-22.08.0/CMakeLists.txt 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/CMakeLists.txt 2022-09-03 00:41:00.000000000 +0200 @@ -3,7 +3,7 @@ # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16 FATAL_ERROR) -set(PIM_VERSION "5.21.0") +set(PIM_VERSION "5.21.1") project(KItinerary VERSION ${PIM_VERSION}) set(KF5_MIN_VERSION "5.91.0") @@ -41,8 +41,8 @@ find_package(SharedMimeInfo 1.3 REQUIRED) endif() -set(KMIME_VERSION "5.21.0") -set(PIM_PKPASS "5.21.0") +set(KMIME_VERSION "5.21.1") +set(PIM_PKPASS "5.21.1") find_package(KF5Mime ${KMIME_VERSION} CONFIG REQUIRED) find_package(KPimPkPass ${PIM_PKPASS} CONFIG REQUIRED) @@ -69,7 +69,7 @@ # check if we have private Poppler headers find_file(HAVE_POPPLER_UNSTABLE_HEADERS "OutputDev.h" PATHS ${Poppler_INCLUDE_DIRS} NO_DEFAULT_PATH) if (NOT HAVE_POPPLER_UNSTABLE_HEADERS) - message(FATAL_ERROR "Poppler was not build with ENABLE_UNSTABLE_API_ABI_HEADER!") + message(FATAL_ERROR "Poppler was not build with ENABLE_UNSTABLE_API_ABI_HEADERS!") endif() endif() string(REGEX MATCH "([0-9]+)\.0*([0-9]+)\.0*([0-9]+)" _match ${Poppler_VERSION}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/autotests/CMakeLists.txt new/kitinerary-22.08.1/autotests/CMakeLists.txt --- old/kitinerary-22.08.0/autotests/CMakeLists.txt 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/autotests/CMakeLists.txt 2022-09-03 00:41:00.000000000 +0200 @@ -30,6 +30,7 @@ ecm_add_test(htmldocumenttest.cpp LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test KPim::Itinerary) ecm_add_test(barcodedecodertest.cpp LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test KPim::Itinerary Qt${QT_MAJOR_VERSION}::Gui) ecm_add_test(pkpassextractortest.cpp LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test KPim::Itinerary KPim::PkPass) +ecm_add_test(terminalfindertest.cpp LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test KPim::Itinerary) ecm_add_test(extractorutiltest.cpp LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test KPim::Itinerary) ecm_add_test(addressparsertest.cpp LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test KPim::Itinerary KF5::Contacts) ecm_add_test(timefindertest.cpp LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test KPim::Itinerary) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/autotests/extractordata/synthetic/iata-bcbp-demo.pdf.json new/kitinerary-22.08.1/autotests/extractordata/synthetic/iata-bcbp-demo.pdf.json --- old/kitinerary-22.08.0/autotests/extractordata/synthetic/iata-bcbp-demo.pdf.json 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/autotests/extractordata/synthetic/iata-bcbp-demo.pdf.json 2022-09-03 00:41:00.000000000 +0200 @@ -36,6 +36,7 @@ "iataCode": "MXP", "name": "Milano Malpensa" }, + "arrivalTerminal": "1", "arrivalTime": { "@type": "QDateTime", "@value": "2019-09-06T17:20:00+02:00", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/autotests/terminalfindertest.cpp new/kitinerary-22.08.1/autotests/terminalfindertest.cpp --- old/kitinerary-22.08.0/autotests/terminalfindertest.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-22.08.1/autotests/terminalfindertest.cpp 2022-09-03 00:41:00.000000000 +0200 @@ -0,0 +1,65 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include <text/terminalfinder.cpp> + +#include <KItinerary/Flight> +#include <KItinerary/Place> + +#include <QTest> + +using namespace KItinerary; + +#define s(x) QStringLiteral(x) + +class TerminalFinderTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testTerminalFindingAtStart_data() + { + QTest::addColumn<QString>("input"); + QTest::addColumn<int>("len"); + QTest::addColumn<QString>("terminalName"); + + QTest::newRow("empty") << QString() << -1 << QString(); + QTest::newRow("no terminal") << s("Paris Charles de Gaulle") << -1 << QString(); + QTest::newRow("CDG 1") << s("PARIS, FR (CHARLES DE GAULLE), TERMINAL 2E") << -1 << QString(); + QTest::newRow("CDG 2") << s(" (Terminal 2D)") << 14 << s("2D"); + QTest::newRow("CDG 3") << s(" - AEROGARE 2") << 13 << s("2"); + QTest::newRow("LHR") << s("-Terminal 2") << 11 << s("2"); + QTest::newRow("MAD") << s(", TERMINAL 4S") << 13 << s("4S"); + QTest::newRow("DTW") << s(" TERMINAL EM") << 12 << s("EM"); + QTest::newRow("MRS1") << s(" - TERMINAL 1A") << 14 << s("1A"); + QTest::newRow("MRS2") << s(" - TERMINAL 1A\nmore data") << 14 << s("1A"); + QTest::newRow("GTW EN") << s(" (North Terminal)") << 17 << s("North"); + QTest::newRow("GTW DE") << s(" (Terminal Nord)") << 16 << s("Nord"); + QTest::newRow("TXL1") << s("(Terminal C)") << 12 << s("C"); + QTest::newRow("TXL2") << s("(Terminal C) something else") << 12 << s("C"); + QTest::newRow("DEL1") << s(" T2") << 3 << s("2"); + QTest::newRow("DEL2") << s(" (T3)") << 5 << s("3"); + QTest::newRow("DEL3") << s(" T2 some other text") << 3 << s("2"); + QTest::newRow("DEL4") << s(" (T3) more information") << 5 << s("3"); + QTest::newRow("DEL5") << s(" (T1C) ") << 6 << s("1C"); + QTest::newRow("FRA") << s(", Terminal 2 (FRA)\nMadrid (MAD)") << 12 << s("2"); + } + + void testTerminalFindingAtStart() + { + QFETCH(QString, input); + QFETCH(int, len); + QFETCH(QString, terminalName); + + TerminalFinder f(u"^", u"(?=\\b|\\s|$)"); + const auto res = f.find(input); + QCOMPARE(res.name, terminalName); + QCOMPARE(res.start, len == -1 ? -1 : 0); + QCOMPARE(res.end, len); + } +}; + +QTEST_GUILESS_MAIN(TerminalFinderTest) + +#include "terminalfindertest.moc" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/po/zh_CN/kitinerary.po new/kitinerary-22.08.1/po/zh_CN/kitinerary.po --- old/kitinerary-22.08.0/po/zh_CN/kitinerary.po 2022-08-12 02:13:24.000000000 +0200 +++ new/kitinerary-22.08.1/po/zh_CN/kitinerary.po 2022-09-06 02:16:52.000000000 +0200 @@ -3,7 +3,7 @@ "Project-Id-Version: kdeorg\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-07-03 00:47+0000\n" -"PO-Revision-Date: 2022-08-07 13:32\n" +"PO-Revision-Date: 2022-08-20 14:20\n" "Last-Translator: \n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/cli/org.kde.kitinerary-extractor.appdata.xml new/kitinerary-22.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml --- old/kitinerary-22.08.0/src/cli/org.kde.kitinerary-extractor.appdata.xml 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml 2022-09-03 00:41:00.000000000 +0200 @@ -110,9 +110,9 @@ <binary>kitinerary-extractor</binary> </provides> <releases> + <release version="5.21.1" date="2022-09-08"/> <release version="5.21.0" date="2022-08-18"/> <release version="5.20.3" date="2022-07-07"/> <release version="5.20.2" date="2022-06-09"/> - <release version="5.20.1" date="2022-05-12"/> </releases> </component> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/CMakeLists.txt new/kitinerary-22.08.1/src/lib/CMakeLists.txt --- old/kitinerary-22.08.0/src/lib/CMakeLists.txt 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/CMakeLists.txt 2022-09-03 00:41:00.000000000 +0200 @@ -94,6 +94,7 @@ scripts/extractors.qrc text/addressparser.cpp + text/terminalfinder.cpp text/timefinder.cpp tlv/berelement.cpp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/extractors/genericboardingpassextractor.cpp new/kitinerary-22.08.1/src/lib/extractors/genericboardingpassextractor.cpp --- old/kitinerary-22.08.0/src/lib/extractors/genericboardingpassextractor.cpp 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/extractors/genericboardingpassextractor.cpp 2022-09-03 00:41:00.000000000 +0200 @@ -13,6 +13,7 @@ #include <knowledgedb/airportdb.h> #include <knowledgedb/airportnametokenizer_p.h> #include <pdf/pdfdocument.h> +#include <text/terminalfinder_p.h> #include <text/timefinder_p.h> #include <KItinerary/ExtractorDocumentNode> @@ -109,6 +110,8 @@ ExtractorResult GenericBoardingPassExtractor::extract(const ExtractorDocumentNode &node, [[maybe_unused]] const ExtractorEngine *engine) const { + static TerminalFinder terminalFinder(u"^", u"(?=\\b|\\s|$)"); + QVector<QVariant> fullResult; const auto pdf = node.content<PdfDocument*>(); @@ -128,6 +131,7 @@ // 1 determine which airports we need to look for on the same page const auto pageNum = (*it).location().toInt(); std::unordered_map<KnowledgeDb::IataCode, QStringList> airportNames; + std::unordered_map<KnowledgeDb::IataCode, QString> terminalNames; for (auto it2 = it; it2 != bcbpNodes.end() && (*it2).location().toInt() == pageNum; ++it2) { const auto flightReservations = (*it).result().result(); for (const auto &flightRes : flightReservations) { @@ -135,10 +139,12 @@ if (!flight.departureAirport().iataCode().isEmpty()) { from = KnowledgeDb::IataCode{flight.departureAirport().iataCode()}; airportNames[from] = QStringList(); + terminalNames[from] = QString(); } if (!flight.arrivalAirport().iataCode().isEmpty()) { to = KnowledgeDb::IataCode{flight.arrivalAirport().iataCode()}; airportNames[to] = QStringList(); + terminalNames[to] = QString(); } departureDay = flight.departureDay(); } @@ -163,6 +169,13 @@ if (it2 != airportNames.end()) { qCDebug(Log) << " found candidate:" << s << iataCodes; mergeOrAppend((*it2).second, s); + + // look for a following terminal name at the position after s + const auto offset = s.size() + s.data() - pageText.data(); + const auto res = terminalFinder.find(QStringView(pageText).mid(offset)); + if (res.hasResult()) { + terminalNames[(*it2).first] = res.name; + } } } } @@ -175,9 +188,11 @@ auto airport = flight.departureAirport(); airport.setName(airportNames[KnowledgeDb::IataCode{airport.iataCode()}].join(QLatin1Char(' '))); flight.setDepartureAirport(airport); + flight.setDepartureTerminal(terminalNames[KnowledgeDb::IataCode{airport.iataCode()}]); airport = flight.arrivalAirport(); airport.setName(airportNames[KnowledgeDb::IataCode{airport.iataCode()}].join(QLatin1Char(' '))); flight.setArrivalAirport(airport); + flight.setArrivalTerminal(terminalNames[KnowledgeDb::IataCode{airport.iataCode()}]); flightRes.setReservationFor(flight); result.push_back(std::move(flightRes)); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/extractorutil.cpp new/kitinerary-22.08.1/src/lib/extractorutil.cpp --- old/kitinerary-22.08.0/src/lib/extractorutil.cpp 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/extractorutil.cpp 2022-09-03 00:41:00.000000000 +0200 @@ -5,6 +5,7 @@ */ #include "extractorutil.h" +#include "text/terminalfinder_p.h" #include <KItinerary/Flight> #include <KItinerary/Place> @@ -31,27 +32,19 @@ static std::tuple<QString, QString> splitAirportName(const QString &name) { - static QRegularExpression patterns[] = { - QRegularExpression(QStringLiteral("^(.*) \\((?:terminal|aerogare) (.*)\\)$"), QRegularExpression::CaseInsensitiveOption), - QRegularExpression(QStringLiteral("^(.*) \\((.*) (?:terminal|aerogare)\\)$"), QRegularExpression::CaseInsensitiveOption), - QRegularExpression(QStringLiteral("^(.*)[ -](?:terminal|aerogare) (.*)$"), QRegularExpression::CaseInsensitiveOption), - }; - - for (const auto &re : patterns) { - const auto match = re.match(name); - if (match.hasMatch()) { - const auto name = trimAirportName(match.capturedView(1)); - - // try to recurse, sometimes this is indeed repeated... - QString recName; - QString recTerminal; - std::tie(recName, recTerminal) = splitAirportName(name); - if (recName == name || recTerminal.isEmpty()) { - return std::make_tuple(trimAirportName(match.capturedView(1)), match.captured(2)); - } else { - return std::make_tuple(recName, recTerminal); - } - break; + static TerminalFinder finder(u"^.*", u"$"); + const auto result = finder.find(name); + if (result.hasResult()) { + const auto airportName = trimAirportName(QStringView(name).left(result.start)); + + // try to recurse, sometimes this is indeed repeated... + QString recName; + QString recTerminal; + std::tie(recName, recTerminal) = splitAirportName(airportName); + if (recName == name || recTerminal.isEmpty()) { + return std::make_tuple(airportName, result.name); + } else { + return std::make_tuple(recName, recTerminal); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/processors/pkpassdocumentprocessor.cpp new/kitinerary-22.08.1/src/lib/processors/pkpassdocumentprocessor.cpp --- old/kitinerary-22.08.0/src/lib/processors/pkpassdocumentprocessor.cpp 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/processors/pkpassdocumentprocessor.cpp 2022-09-03 00:41:00.000000000 +0200 @@ -113,12 +113,15 @@ static bool isPlausibeGate(const QString &s) { + if (s.isEmpty() || s.size() > 10 || s.count(QLatin1Char('-')) > 1 || s.count(QLatin1Char(' ')) > 2) { + return false; + } for (const auto &c : s) { - if (c.isLetter() || c.isDigit()) { - return true; + if (!c.isLetter() && !c.isDigit() && c != QLatin1Char(' ') && c != QLatin1Char(' ')) { + return false; } } - return false; + return true; } static Flight extractBoardingPass(KPkPass::Pass *pass, Flight flight) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/scripts/amtrack.js new/kitinerary-22.08.1/src/lib/scripts/amtrack.js --- old/kitinerary-22.08.0/src/lib/scripts/amtrack.js 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/scripts/amtrack.js 2022-09-03 00:41:00.000000000 +0200 @@ -10,23 +10,41 @@ var legs = new Array(); var idx = 0; while (true) { - const train = text.substr(idx).match(/TRAIN +\w.*? +(.*?) +DEPARTS\s+ARRIVES \(\w{3} (.*)\)\n\s*(\d+)\s+(\w{3} \d{1,2}), (\d{4})\n\s*(.*)?\n?\s*\d+ (.*) Seats?\n? +(\d{1,2}:\d{2} [AP]M) +(\d{1,2}:\d{2} [AP]M)/); - if (!train) { - break; + let leg = JsonLd.newTrainReservation(); + leg.reservationNumber = triggerNode.content.substr(0, 6); + leg.reservedTicket.ticketToken = 'qrcode:' + triggerNode.content; + + // format variant 1 + let train = text.substr(idx).match(/TRAIN +\w.*? +(.*?) +DEPARTS\s+ARRIVES \(\w{3} (.*)\)\n\s*(\d+)\s+(\w{3} \d{1,2}), (\d{4})\n\s*(.*)?\n?\s*\d+ (.*) Seats?\n? +(\d{1,2}:\d{2} [AP]M) +(\d{1,2}:\d{2} [AP]M)/); + if (train) { + leg.reservationFor.trainNumber = train[3]; + leg.reservationFor.departureTime = JsonLd.toDateTime(train[4] + ' ' + train[5] + ' ' + train[8], 'MMM d yyyy h:mm AP', 'en'); + leg.reservationFor.arrivalTime = JsonLd.toDateTime(train[2] + ' ' + train[5] + ' ' + train[9], 'MMM d yyyy h:mm AP', 'en'); + leg.reservedTicket.ticketedSeat.seatingType = train[7]; + + const stations = (train[1] + ' ' + (train[6] ?? '')).match(/(.*) - (.*)/); + leg.reservationFor.departureStation.name = stations[1]; + leg.reservationFor.arrivalStation.name = stations[2]; + idx += train.index + train[0].length; + legs.push(leg); + continue; } - idx += train.index + train[0].length; - var leg = JsonLd.newTrainReservation(); - leg.reservedTicket.ticketToken = 'qrcode:' + triggerNode.content; - leg.reservationFor.trainNumber = train[3]; - leg.reservationFor.departureTime = JsonLd.toDateTime(train[4] + ' ' + train[5] + ' ' + train[8], 'MMM d yyyy h:mm AP', 'en'); - leg.reservationFor.arrivalTime = JsonLd.toDateTime(train[2] + ' ' + train[5] + ' ' + train[9], 'MMM d yyyy h:mm AP', 'en'); - leg.reservedTicket.ticketedSeat.seatingType = train[7]; + // format variant 2 + train = text.substr(idx).match(/TRAIN .* DEPARTS +ARRIVES\n *(\d+) +([A-Z][a-z]{2} \d{1,2}, \d{4}) +(\d{1,2}:\d{2} [AP]M) +(\d{1,2}:\d{2} [AP]M)\n +(.*?) +(.*)\n *\d+ (.*) Seat/); + if (train) { + leg.reservationFor.trainNumber = train[1]; + leg.reservationFor.departureTime = JsonLd.toDateTime(train[2] + ' ' + train[3], 'MMM d, yyyy h:mm AP', 'en'); + leg.reservationFor.departureStation.name = train[5]; + leg.reservationFor.arrivalTime = JsonLd.toDateTime(train[2] + ' ' + train[4], 'MMM d, yyyy h:mm AP', 'en'); + leg.reservationFor.arrivalStation.name = train[6]; + leg.reservedTicket.ticketedSeat.seatingType = train[7]; + idx += train.index + train[0].length; + legs.push(leg); + continue; + } - const stations = (train[1] + ' ' + (train[6] ?? '')).match(/(.*) - (.*)/); - leg.reservationFor.departureStation.name = stations[1]; - leg.reservationFor.arrivalStation.name = stations[2]; - legs.push(leg); + break; } // station codes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/scripts/extractors.qrc new/kitinerary-22.08.1/src/lib/scripts/extractors.qrc --- old/kitinerary-22.08.0/src/lib/scripts/extractors.qrc 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/scripts/extractors.qrc 2022-09-03 00:41:00.000000000 +0200 @@ -157,6 +157,8 @@ <file>trenitalia.js</file> <file>viarail.json</file> <file>viarail.js</file> + <file>vistara.json</file> + <file>vistara.js</file> <file>vitolus.json</file> <file>vitolus.js</file> <file>vr.fi.json</file> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/scripts/flixbus.js new/kitinerary-22.08.1/src/lib/scripts/flixbus.js --- old/kitinerary-22.08.0/src/lib/scripts/flixbus.js 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/scripts/flixbus.js 2022-09-03 00:41:00.000000000 +0200 @@ -1,6 +1,5 @@ /* - SPDX-FileCopyrightText: 2019 Volker Krause <vkra...@kde.org> - + SPDX-FileCopyrightText: 2019-2022 Volker Krause <vkra...@kde.org> SPDX-License-Identifier: LGPL-2.0-or-later */ @@ -9,7 +8,7 @@ var res = node.result; for (var i = 0; i < res.length; ++i) { var ticketToken = res[i].reservedTicket.ticketToken; - res[i].reservedTicket.ticketToken = ticketToken.replace(/^https?:\/\/api\.meinfernbus\.(..)\/qrcode\/..\//, "qrCode:https://shop.flixbus.$1/pdfqr/"); + res[i].reservedTicket.ticketToken = ticketToken.replace(/^https?:\/\/api\.(?:flixbus|meinfernbus)\..{2,3}\/qrcode\/(..)\//, "qrCode:https://shop.flixbus.$1/pdfqr/"); // their schema.org annotations also claim train trips are bus trips, fix that if (res[i].reservationFor.departureBusStop.name.endsWith(" (FlixTrain)") && res[i].reservationFor.arrivalBusStop.name.endsWith(" (FlixTrain)")) { @@ -20,3 +19,59 @@ } return res; } + +function parseDate(year, baseDate, overrideDate, time) +{ + const s = (overrideDate ? overrideDate.trim() : baseDate) + ' ' + year + ' ' + time; + return JsonLd.toDateTime(s, 'd MMM yyyy hh:mm', ['en', 'fr']); +} + +function parseLocation(place, addr1, addr2) +{ + if (!addr1) + return; + if (addr2) + addr1 += ', ' + addr2; + const idx = addr1.lastIndexOf(','); + place.address.streetAddress = addr1.substring(0, idx); + place.address.addressLocality = addr1.substr(idx + 1); +} + +function parsePdfTicket(pdf, node, triggerNode) +{ + const page = pdf.pages[triggerNode.location]; + const text = page.textInRect(0.0, 0.05, 0.5, 0.5); + const resNum = triggerNode.content.match(/pdfqr\/(\d+)\//)[1]; + const date = text.match(/^\S+,? (\d+ \S+) (\d{4})\n/); + let idx = date.index + date[0].length; + let reservations = []; + while (true) { + const dep = text.substr(idx).match(/(\d\d:\d\d) +(.*)\n(\d{1,2} \S+)?(?: +??? (.*?)(?:\n|,\n +(.*)\n))?/); + if (!dep) break; + idx += dep.index + dep[0].length; + const bus = text.substr(idx).match(/^[ ???]+ Bus +(.*)\n[ ???]+(?:Direction|?? destination de) (.*)\n/); + if (!bus) break; + idx += bus.index + bus[0].length; + const arr = text.substr(idx).match(/^(\d{1,2} \S+\n)?(\d\d:\d\d) +(.*)\n(?: +??? (.*?)(?:\n|,\n +(.*)\n))?/); + if (!arr) break; + idx += arr.index + arr[0].length; + + let res = JsonLd.newBusReservation(); + res.reservationNumber = resNum; + + res.reservedTicket.ticketToken = 'qrCode:' + triggerNode.content; + res.reservationFor.departureTime = parseDate(date[2], date[1], dep[3], dep[1]); + res.reservationFor.departureBusStop.name = dep[2]; + parseLocation(res.reservationFor.departureBusStop, dep[4], dep[5]); + + res.reservationFor.busNumber = bus[1]; + res.reservationFor.busName = bus[2]; + + res.reservationFor.arrivalTime = parseDate(date[2], date[1], arr[1], arr[2]); + res.reservationFor.arrivalBusStop.name = arr[3]; + parseLocation(res.reservationFor.arrivalBusStop, arr[4], arr[5]); + + reservations.push(res); + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/scripts/flixbus.json new/kitinerary-22.08.1/src/lib/scripts/flixbus.json --- old/kitinerary-22.08.0/src/lib/scripts/flixbus.json 2022-08-11 00:20:08.000000000 +0200 +++ new/kitinerary-22.08.1/src/lib/scripts/flixbus.json 2022-09-03 00:41:00.000000000 +0200 @@ -1,7 +1,7 @@ -{ +[{ "filter": [ { - "match": "FlixMobility", + "match": "(?:FlixMobility|Flix SE)", "field": "reservationFor.busCompany.name", "mimeType": "application/ld+json" } @@ -9,4 +9,16 @@ "function": "main", "script": "flixbus.js", "mimeType": "text/html" -} +}, +{ + "filter": [ + { + "match": "^https://shop\\.(?:global\\.)?flixbus\\.[a-z]{2,3}/pdfqr/", + "mimeType": "text/plain", + "scope": "Descendants" + } + ], + "function": "parsePdfTicket", + "mimeType": "application/pdf", + "script": "flixbus.js" +}] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/scripts/vistara.js new/kitinerary-22.08.1/src/lib/scripts/vistara.js --- old/kitinerary-22.08.0/src/lib/scripts/vistara.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-22.08.1/src/lib/scripts/vistara.js 2022-09-03 00:41:00.000000000 +0200 @@ -0,0 +1,25 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parseBoardingPass(pdf, node, triggerNode) { + if (triggerNode.result.length != 1) return; + const page = pdf.pages[triggerNode.location]; + const depText = page.textInRect(0.0, 0.2, 0.5, 0.4); + const dep = depText.match(/DEPARTURE\n *(\d{4})\n *(\d{2}\w{3}\d{4})\n(.*\n)?(.*)\nFLIGHT/); + const arrText = page.textInRect(0.5, 0.2, 1.0, 0.4); + const arr = arrText.match(/ *ARRIVAL\n *(\d{4})\n *(\d{2}\w{3}\d{4})\n(.*\n)?(.*)\n *BOARDING TIME .*\n *(\d{4})/); + + let res = triggerNode.result[0]; + res.reservationFor.departureAirport.name = dep[4]; + res.reservationFor.departureTime = JsonLd.toDateTime(dep[2] + ' ' + dep[1], 'ddMMMyyyy hhmm', 'en'); + res.reservationFor.departureTerminal = dep[3]; + + res.reservationFor.arrivalAirport.name = arr[4]; + res.reservationFor.arrivalTime = JsonLd.toDateTime(arr[2] + ' ' + arr[1], 'ddMMMyyyy hhmm', 'en'); + res.reservationFor.arrivalTerminal = arr[3]; + + res.reservationFor.boardingTime = JsonLd.toDateTime(dep[2] + ' ' + arr[5], 'ddMMMyyyy hhmm', 'en'); + return res; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/scripts/vistara.json new/kitinerary-22.08.1/src/lib/scripts/vistara.json --- old/kitinerary-22.08.0/src/lib/scripts/vistara.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-22.08.1/src/lib/scripts/vistara.json 2022-09-03 00:41:00.000000000 +0200 @@ -0,0 +1,13 @@ +{ + "filter": [ + { + "field": "operatingCarrierDesignator", + "match": "UK", + "mimeType": "internal/iata-bcbp", + "scope": "Descendants" + } + ], + "function": "parseBoardingPass", + "mimeType": "application/pdf", + "script": "vistara.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/text/terminalfinder.cpp new/kitinerary-22.08.1/src/lib/text/terminalfinder.cpp --- old/kitinerary-22.08.0/src/lib/text/terminalfinder.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-22.08.1/src/lib/text/terminalfinder.cpp 2022-09-03 00:41:00.000000000 +0200 @@ -0,0 +1,51 @@ +/* + SPDX-FileCopyrightText: 2019-2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "terminalfinder_p.h" + +#include <QDebug> + +using namespace KItinerary; + +struct { + const char *pattern; + QRegularExpression::PatternOptions options; +} static constexpr const terminal_patterns[] = { + // use a named capture group for the actual name part of the terminal + { " ?\\((?:terminal|aerogare) (?<name>\\w.*)\\)", QRegularExpression::CaseInsensitiveOption }, + { " ?\\((?<name>\\w.*) (?:terminal|aerogare)\\)", QRegularExpression::CaseInsensitiveOption }, + { "(?:, | ?- ?| )(?:terminal|aerogare) (?<name>\\w.*?)", QRegularExpression::CaseInsensitiveOption }, + { " T(?<name>\\d[A-Z]?)", QRegularExpression::NoPatternOption }, + { " ?\\(T(?<name>\\d[A-Z]?)\\)", QRegularExpression::NoPatternOption }, +}; + + +TerminalFinder::TerminalFinder(QStringView frontAnchor, QStringView backAnchor) +{ + static_assert(std::tuple_size_v<decltype(m_patterns)> == std::size(terminal_patterns)); + + int i = 0; + for (const auto &pattern : terminal_patterns) { + m_patterns[i++] = QRegularExpression(frontAnchor + QLatin1String("(?<terminal>") + QLatin1String(pattern.pattern) + QLatin1String(")") + backAnchor, pattern.options); + } +} + +TerminalFinder::~TerminalFinder() = default; + +TerminalFinder::Result TerminalFinder::find(QStringView s) const +{ + for (const auto &re: m_patterns) { + const auto match = re.match(s); + if (match.hasMatch()) { + Result res; + res.start = match.capturedStart(u"terminal"); + res.end = match.capturedEnd(u"terminal"); + res.name = match.captured(u"name"); // ### can't be a QStringView, that becomes invalid as soon as match goes out of scope (not when s becomes invalid!) + return res; + } + } + return {}; +} + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-22.08.0/src/lib/text/terminalfinder_p.h new/kitinerary-22.08.1/src/lib/text/terminalfinder_p.h --- old/kitinerary-22.08.0/src/lib/text/terminalfinder_p.h 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-22.08.1/src/lib/text/terminalfinder_p.h 2022-09-03 00:41:00.000000000 +0200 @@ -0,0 +1,42 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KITINERARY_TERMINALFINDER_P_H +#define KITINERARY_TERMINALFINDER_P_H + +#include <QRegularExpression> + +#include <array> + +class QStringView; + +namespace KItinerary { + +/** Identify terminal names alongside airport names. */ +class TerminalFinder +{ +public: + /** Create a terminal finder with custom front and back anchor patterns. */ + explicit TerminalFinder(QStringView frontAnchor, QStringView backAnchor); + ~TerminalFinder(); + + struct Result { + // start and end offsets of the terminal part (not including front/back anchor patterns) + int start = -1; + int end = -1; + QString name; + + constexpr inline bool hasResult() const { return start >= 0; } + }; + + Result find(QStringView s) const; + +private: + std::array<QRegularExpression, 5> m_patterns; +}; + +} + +#endif // KITINERARY_TERMINALFINDER_P_H