Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kitinerary for openSUSE:Factory checked in at 2021-09-04 22:33:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitinerary (Old) and /work/SRC/openSUSE:Factory/.kitinerary.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitinerary" Sat Sep 4 22:33:08 2021 rev:39 rq:915917 version:21.08.1 Changes: -------- --- /work/SRC/openSUSE:Factory/kitinerary/kitinerary.changes 2021-08-16 10:07:34.015383551 +0200 +++ /work/SRC/openSUSE:Factory/.kitinerary.new.1899/kitinerary.changes 2021-09-04 22:34:55.648117517 +0200 @@ -1,0 +2,19 @@ +Wed Sep 1 12:38:31 UTC 2021 - Christophe Giboudeaux <[email protected]> + +- Update to 21.08.1 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/gear/21.08.1 +- Changes since 21.08.0: + * Add Airdo confirmation mail extractor script + * Extract SNCF Ouigo confirmation emails (kde#441361) + * Extract Ryanair PDF boarding passes + * Actually add the MAV extractor + * Add basic MAV (Hungarian state railway) domestic ticket extractor + * Add an alternative way of decoding RCT2 reservation data + * Reproduce whitespaces a bit more correctly when layouting U_TLAY text + * Let RCT2 type detection ignore whitespaces and support Hungarian IRTs + * Add a watchdog timer to interrupt long running extractor scripts + * Add Regionado pkpass extractor script + +------------------------------------------------------------------- Old: ---- kitinerary-21.08.0.tar.xz kitinerary-21.08.0.tar.xz.sig New: ---- kitinerary-21.08.1.tar.xz kitinerary-21.08.1.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitinerary.spec ++++++ --- /var/tmp/diff_new_pack.anHXVg/_old 2021-09-04 22:34:57.420119866 +0200 +++ /var/tmp/diff_new_pack.anHXVg/_new 2021-09-04 22:34:57.424119871 +0200 @@ -18,7 +18,7 @@ %bcond_without lang Name: kitinerary -Version: 21.08.0 +Version: 21.08.1 Release: 0 Summary: Data model and extraction system for travel reservations License: LGPL-2.1-or-later ++++++ kitinerary-21.08.0.tar.xz -> kitinerary-21.08.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/CMakeLists.txt new/kitinerary-21.08.1/CMakeLists.txt --- old/kitinerary-21.08.0/CMakeLists.txt 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/CMakeLists.txt 2021-08-30 18:21:55.000000000 +0200 @@ -3,7 +3,7 @@ # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16 FATAL_ERROR) -set(PIM_VERSION "5.18.0") +set(PIM_VERSION "5.18.1") project(KItinerary VERSION ${PIM_VERSION}) set(CMAKE_CXX_STANDARD 17) @@ -37,8 +37,8 @@ find_package(SharedMimeInfo 1.3 REQUIRED) endif() -set(KMIME_VERSION "5.18.0") -set(PIM_PKPASS "5.18.0") +set(KMIME_VERSION "5.18.1") +set(PIM_PKPASS "5.18.1") find_package(KF5Mime ${KMIME_VERSION} CONFIG REQUIRED) find_package(KF5CalendarCore ${KF5_MIN_VERSION} CONFIG) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/autotests/buggy.js new/kitinerary-21.08.1/autotests/buggy.js --- old/kitinerary-21.08.0/autotests/buggy.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/autotests/buggy.js 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,10 @@ +/* + SPDX-FileCopyrightText: 2021 Volker Krause <[email protected]> + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function infiniteLoop() +{ + while (true) {} +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/autotests/extractorscriptenginetest.cpp new/kitinerary-21.08.1/autotests/extractorscriptenginetest.cpp --- old/kitinerary-21.08.0/autotests/extractorscriptenginetest.cpp 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/autotests/extractorscriptenginetest.cpp 2021-08-30 18:21:55.000000000 +0200 @@ -70,7 +70,6 @@ void testArguments() { - QFETCH(QString, inputFile); QFETCH(QString, refFile); @@ -96,6 +95,22 @@ } QCOMPARE(result, refResult); } + + void testInfiniteLoop() + { + QFile in(s(SOURCE_DIR "/scriptenginedata/plain-text.txt")); + QVERIFY(in.open(QFile::ReadOnly)); + + ExtractorEngine engine; + auto root = engine.documentNodeFactory()->createNode(in.readAll()); + QVERIFY(!root.isNull()); + expandRecursive(root, &engine); + + ScriptExtractor extractor; + extractor.setScriptFileName(s(":/buggy.js")); + extractor.setScriptFunction(s("infiniteLoop")); + const auto result = extractor.extract(root, &engine).jsonLdResult(); + } }; QTEST_GUILESS_MAIN(ExtractorScriptEngineTest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/autotests/extractorscriptenginetest.qrc new/kitinerary-21.08.1/autotests/extractorscriptenginetest.qrc --- old/kitinerary-21.08.0/autotests/extractorscriptenginetest.qrc 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/autotests/extractorscriptenginetest.qrc 2021-08-30 18:21:55.000000000 +0200 @@ -4,6 +4,7 @@ --> <RCC> <qresource prefix="/"> + <file>buggy.js</file> <file>reflector.js</file> </qresource> </RCC> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/autotests/rct2/valid/irt-mav.json new/kitinerary-21.08.1/autotests/rct2/valid/irt-mav.json --- old/kitinerary-21.08.0/autotests/rct2/valid/irt-mav.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/autotests/rct2/valid/irt-mav.json 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,16 @@ +[ + { + "@context": "http://schema.org", + "@type": "Rct2Ticket", + "coachNumber": "23", + "outboundArrivalStation": "MUENCHEN HBF", + "outboundArrivalTime": "2018-08-19T14:32:00", + "outboundClass": "2", + "outboundDepartureStation": "BUDAPEST-KELETI", + "outboundDepartureTime": "2018-08-19T07:40:00", + "passengerName": "J??hn ?? Doe", + "seatNumber": "101, 102", + "trainNumber": "RJX 42", + "type": "http://schema.org/TransportReservation" + } +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/autotests/rct2/valid/irt-mav.rct2 new/kitinerary-21.08.1/autotests/rct2/valid/irt-mav.rct2 --- old/kitinerary-21.08.0/autotests/rct2/valid/irt-mav.rct2 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/autotests/rct2/valid/irt-mav.rct2 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1 @@ +U_TLAY010831RCT200410001011400015M??V-START Zrt.0018011800018MENETJEGY+HELYJEGY0052011000012J??hn ?? Doe0118012300023FAHRSCHEIN+RESERVIERUNG0152010200002020155011400016??L??HELY/SITZPL0201010800008CIV 1155060101050000519.08060701050000507:400613011500015BUDAPEST-KELETI0634011200012MUENCHEN HBF065201050000519.08065801050000514:32066801010000120703010100001*0709010100001*0713010100001*0734010100001*0754010100001*0760010100001*0768010100001*0801010300003ZUG0805010200002420811010300003RJX0815010500005WAGEN0821010200002230825010500005PLATZ0831010800008101, 1021201011500015START Europa DE1237010800008CARRIERS1301010200002011304011900020FELN??TT/ERWACHSENER1401010200002011404011900020FELN??TT/ERWACHSENER13370114000141155 1181 10801352010200003??R1358010300003HUF1362010900009*12345.001452010500005PREIS1458010300003EUR1462010900009***123.00 \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/po/fi/kitinerary.po new/kitinerary-21.08.1/po/fi/kitinerary.po --- old/kitinerary-21.08.0/po/fi/kitinerary.po 2021-08-06 02:25:14.000000000 +0200 +++ new/kitinerary-21.08.1/po/fi/kitinerary.po 2021-08-31 02:39:27.000000000 +0200 @@ -1,10 +1,10 @@ -# Tommi Nieminen <[email protected]>, 2017, 2018, 2019, 2020. +# Tommi Nieminen <[email protected]>, 2017, 2018, 2019, 2020, 2021. msgid "" msgstr "" "Project-Id-Version: desktop files\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2021-05-24 00:17+0000\n" -"PO-Revision-Date: 2020-07-29 13:51+0300\n" +"PO-Revision-Date: 2021-08-18 19:08+0300\n" "Last-Translator: Tommi Nieminen <[email protected]>\n" "Language-Team: Finnish <[email protected]>\n" "Language: fi\n" @@ -41,8 +41,7 @@ msgstr "Koneeseennousuaika: %1" #: calendarhandler.cpp:250 -#, fuzzy, kde-format -#| msgid "Departure gate: %1" +#, kde-format msgctxt "flight departure gate" msgid "Departure gate: %1" msgstr "L??ht??portti: %1" @@ -130,10 +129,9 @@ msgstr "Ravintolavaraus: %1" #: calendarhandler.cpp:464 -#, fuzzy, kde-format -#| msgid "Number Of People: %1" +#, kde-format msgid "Number of people: %1" -msgstr "Henkil??iden m????r??: %1" +msgstr "Henkil??m????r??: %1" #: calendarhandler.cpp:471 calendarhandler.cpp:503 #, kde-format @@ -141,8 +139,7 @@ msgstr "Nimell??: %1" #: calendarhandler.cpp:481 -#, fuzzy, kde-format -#| msgid "Rental Car reservation: %1" +#, kde-format msgid "Rental car reservation: %1" msgstr "Autonvuokrausvaraus: %1" @@ -152,6 +149,8 @@ "Pickup location: %1\n" "%2\n" msgstr "" +"Noutopaikka: %1\n" +"%2\n" #: calendarhandler.cpp:496 #, kde-format @@ -159,13 +158,11 @@ "Dropoff location: %1\n" "%2\n" msgstr "" +"J??tt??paikka: %1\n" +"%2\n" #: calendarhandler.cpp:525 -#, fuzzy, kde-format -#| msgid "" -#| "Reservation reference: %1\n" -#| "Under name: %2\n" -#| "PickUp location: %3" +#, kde-format msgid "" "Reservation reference: %1\n" "Under name: %2\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/po/zh_CN/kitinerary.po new/kitinerary-21.08.1/po/zh_CN/kitinerary.po --- old/kitinerary-21.08.0/po/zh_CN/kitinerary.po 2021-08-06 02:25:14.000000000 +0200 +++ new/kitinerary-21.08.1/po/zh_CN/kitinerary.po 2021-08-31 02:39:27.000000000 +0200 @@ -8,7 +8,7 @@ "Project-Id-Version: kdeorg\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2021-05-24 00:17+0000\n" -"PO-Revision-Date: 2021-07-26 13:49\n" +"PO-Revision-Date: 2021-08-30 11:45\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-21.08.0/src/cli/org.kde.kitinerary-extractor.appdata.xml new/kitinerary-21.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml --- old/kitinerary-21.08.0/src/cli/org.kde.kitinerary-extractor.appdata.xml 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml 2021-08-30 18:21:55.000000000 +0200 @@ -101,9 +101,9 @@ <binary>kitinerary-extractor</binary> </provides> <releases> + <release version="5.18.1" date="2021-09-02"/> <release version="5.18.0" date="2021-08-12"/> <release version="5.17.3" date="2021-07-08"/> <release version="5.17.2" date="2021-06-10"/> - <release version="5.17.1" date="2021-05-13"/> </releases> </component> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/engine/extractorscriptengine.cpp new/kitinerary-21.08.1/src/lib/engine/extractorscriptengine.cpp --- old/kitinerary-21.08.0/src/lib/engine/extractorscriptengine.cpp 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/src/lib/engine/extractorscriptengine.cpp 2021-08-30 18:21:55.000000000 +0200 @@ -18,20 +18,33 @@ #include <QJSEngine> #include <QJSValueIterator> #include <QScopeGuard> +#include <QThread> +#include <QTimer> using namespace KItinerary; namespace KItinerary { class ExtractorScriptEnginePrivate { public: + ~ExtractorScriptEnginePrivate(); bool loadScript(const QString &fileName); JsApi::Barcode *m_barcodeApi = nullptr; JsApi::JsonLd *m_jsonLdApi = nullptr; QJSEngine m_engine; + + QThread m_watchdogThread; + QTimer *m_watchdogTimer = nullptr; }; } +ExtractorScriptEnginePrivate::~ExtractorScriptEnginePrivate() +{ + m_watchdogTimer->deleteLater(); + m_watchdogThread.quit(); + m_watchdogThread.wait(); +} + ExtractorScriptEngine::ExtractorScriptEngine() = default; ExtractorScriptEngine::~ExtractorScriptEngine() = default; @@ -47,6 +60,13 @@ d->m_engine.globalObject().setProperty(QStringLiteral("JsonLd"), d->m_engine.newQObject(d->m_jsonLdApi)); d->m_barcodeApi = new JsApi::Barcode; d->m_engine.globalObject().setProperty(QStringLiteral("Barcode"), d->m_engine.newQObject(d->m_barcodeApi)); + + d->m_watchdogThread.start(); + d->m_watchdogTimer = new QTimer; + d->m_watchdogTimer->setInterval(std::chrono::seconds(1)); + d->m_watchdogTimer->setSingleShot(true); + d->m_watchdogTimer->moveToThread(&d->m_watchdogThread); + QObject::connect(d->m_watchdogTimer, &QTimer::timeout, &d->m_engine, [this]() { d->m_engine.setInterrupted(true); }, Qt::DirectConnection); } void ExtractorScriptEngine::setBarcodeDecoder(BarcodeDecoder *barcodeDecoder) @@ -55,10 +75,20 @@ d->m_barcodeApi->setDecoder(barcodeDecoder); } -static void printScriptError(const QJSValue &result) +// produce the same output as the JS engine error result fileName property would have +static QString fileNameToUrl(const QString &fileName) { + if (fileName.startsWith(QLatin1Char(':'))) { + return QLatin1String("qrc:/") + QStringView(fileName).mid(1); + } + return QUrl::fromLocalFile(fileName).toString(); +} + +static void printScriptError(const QJSValue &result, const QString &fileNameFallback) +{ + const auto fileName = result.property(QStringLiteral("fileName")); // don't change the formatting without adjusting KItinerary Workbench too! - qCWarning(Log).noquote().nospace() << "JS ERROR: [" << result.property(QStringLiteral("fileName")).toString() + qCWarning(Log).noquote().nospace() << "JS ERROR: [" << (fileName.isString() ? fileName.toString() : fileNameToUrl(fileNameFallback)) << "]:" << result.property(QStringLiteral("lineNumber")).toInt() << ": " << result.toString(); } @@ -78,7 +108,7 @@ auto result = m_engine.evaluate(QString::fromUtf8(f.readAll()), f.fileName()); if (result.isError()) { - printScriptError(result); + printScriptError(result, fileName); return false; } @@ -89,6 +119,13 @@ { const_cast<ExtractorScriptEngine*>(this)->ensureInitialized(); + // watchdog setup + QMetaObject::invokeMethod(d->m_watchdogTimer, qOverload<>(&QTimer::start)); + const auto watchdogStop = qScopeGuard([this]() { + QMetaObject::invokeMethod(d->m_watchdogTimer, qOverload<>(&QTimer::stop)); + }); + d->m_engine.setInterrupted(false); + if (!d->loadScript(extractor->scriptFileName())) { return {}; } @@ -117,7 +154,7 @@ const auto result = mainFunc.call(args); if (result.isError()) { - printScriptError(result); + printScriptError(result, extractor->scriptFileName()); return {}; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/airdo.js new/kitinerary-21.08.1/src/lib/scripts/airdo.js --- old/kitinerary-21.08.0/src/lib/scripts/airdo.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/airdo.js 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,44 @@ +/* + SPDX-FileCopyrightText: 2021 Volker Krause <[email protected]> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parseConfirmation(text, node) { + var flightRes = new Array(); + var idx = 0; + while (true) { + const flight = text.substr(idx).match(/\[\d+\]\n(\d{1,2} \w+)\(.*\) ([A-Z0-9]+) (\d+)\n(.*) - (.*)\n.*?(\d{1,2}:\d{2})-(\d{1,2}:\d{2})/); + if (!flight) { + break; + } + idx += flight.index + flight[0].length; + + var f = JsonLd.newFlightReservation(); + f.reservationFor.departureAirport.name = flight[4]; + f.reservationFor.arrivalAirport.name = flight[5]; + f.reservationFor.departureTime = JsonLd.toDateTime(flight[1] + flight[6], "d MMMMhh:mm", "en"); + f.reservationFor.arrivalTime = JsonLd.toDateTime(flight[1] + flight[7], "d MMMMhh:mm", "en"); + f.reservationFor.airline.iataCode = flight[2]; + f.reservationFor.flightNumber = flight[3]; + flightRes.push(f); + } + + const seats = text.match(/\[Seat Number\]([\s\S]+?)\n\[/); + if (!seats) { + return flightRes; + } + var reservations = new Array(); + for (seat of seats[1].split(/\n/)) { + const passenger = seat.match(/(.*) (\d+[A-Z])[\n\[]/); + if (!passenger) { + continue; + } + for (flight of flightRes) { + var res = JsonLd.clone(flight); + res.underName.name = passenger[1]; + res.airplaneSeat = passenger[2]; + reservations.push(res); + } + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/airdo.json new/kitinerary-21.08.1/src/lib/scripts/airdo.json --- old/kitinerary-21.08.0/src/lib/scripts/airdo.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/airdo.json 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,13 @@ +{ + "filter": [ + { + "field": "From", + "match": "@airdo.co.jp", + "mimeType": "message/rfc822", + "scope": "Parent" + } + ], + "function": "parseConfirmation", + "mimeType": "text/plain", + "script": "airdo.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/extractors.qrc new/kitinerary-21.08.1/src/lib/scripts/extractors.qrc --- old/kitinerary-21.08.0/src/lib/scripts/extractors.qrc 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/src/lib/scripts/extractors.qrc 2021-08-30 18:21:55.000000000 +0200 @@ -16,6 +16,8 @@ <file>airbaltic.js</file> <file>aircoach-ie.json</file> <file>aircoach-ie.js</file> + <file>airdo.json</file> + <file>airdo.js</file> <file>amadeus.json</file> <file>amadeus.js</file> <file>americanairlines.json</file> @@ -78,6 +80,8 @@ <file>korail.js</file> <file>lufthansa.json</file> <file>lufthansa-pkpass.js</file> + <file>mav.json</file> + <file>mav.js</file> <file>nationalexpress.json</file> <file>nationalexpress.js</file> <file>nh-hotels.json</file> @@ -90,8 +94,12 @@ <file>oebb.js</file> <file>regiojet.json</file> <file>regiojet.js</file> + <file>regiondo.json</file> + <file>regiondo.js</file> <file>renfe.json</file> <file>renfe.js</file> + <file>ryanair.json</file> + <file>ryanair.js</file> <file>sas.json</file> <file>sas-boardingpass.js</file> <file>sas-receipt.js</file> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/mav.js new/kitinerary-21.08.1/src/lib/scripts/mav.js --- old/kitinerary-21.08.0/src/lib/scripts/mav.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/mav.js 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,27 @@ +/* + SPDX-FileCopyrightText: 2021 Volker Krause <[email protected]> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parseTicket(pdf, node, triggerNode) { + var reservations = new Array(); + const text = pdf.pages[triggerNode.location].text; + var idx = 0; + while (true) { + var trip = text.substr(idx).match(/(\d{4}\.\d{2}\.\d{2})\. *(\d{2}:\d{2}) *(.*) *-> *(.*) *(\d{2}:\d{2}) *(.*) *(\d)\./); + if (!trip) { + break; + } + idx += trip.index + trip[0].length + var res = JsonLd.newTrainReservation(); + res.reservationFor.departureStation.name = trip[3]; + res.reservationFor.arrivalStation.name = trip[4]; + res.reservationFor.departureTime = JsonLd.toDateTime(trip[1] + trip[2], "yyyy.MM.ddhh:mm", "hu"); + res.reservationFor.arrivalTime = JsonLd.toDateTime(trip[1] + trip[5], "yyyy.MM.ddhh:mm", "hu"); + res.reservationFor.trainNumber = trip[6]; + res.reservedTicket.ticketedSeat.seatingType = trip[7]; + res.reservedTicket.ticketToken = "pdf417bin:" + Barcode.toBase64(triggerNode.content); + reservations.push(res); + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/mav.json new/kitinerary-21.08.1/src/lib/scripts/mav.json --- old/kitinerary-21.08.0/src/lib/scripts/mav.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/mav.json 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,12 @@ +{ + "filter": [ + { + "match": "^\\x04\\x03\\x1f\\x8b", + "mimeType": "application/octet-stream", + "scope": "Descendants" + } + ], + "function": "parseTicket", + "mimeType": "application/pdf", + "script": "mav.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/regiondo.js new/kitinerary-21.08.1/src/lib/scripts/regiondo.js --- old/kitinerary-21.08.0/src/lib/scripts/regiondo.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/regiondo.js 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,25 @@ +/* + SPDX-FileCopyrightText: 2021 Volker Krause <[email protected]> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parsePass(pass, node) { + var res = node.result[0]; + res.reservationFor.startDate = pass.field["when"].value; + res.underName = JsonLd.newObject("Person"); + res.underName.name = pass.field["buyer"].value; + + var addr = pass.field["venue"].value.match(/(.*), (.*)/); + res.reservationFor.location.name = null; + res.reservationFor.location.address = JsonLd.newObject("PostalAddress"); + res.reservationFor.location.address.addressCountry = addr[2]; + if (addr[2] == "Deutschland") { + var addr2 = addr[1].match(/(.*) (\d{5} .*) ([A-Z]{2})/); + res.reservationFor.location.address.streetAddress = addr2[1]; + res.reservationFor.location.address.addressLocality = addr2[2]; + res.reservationFor.location.address.addressRegion = addr2[3]; + } else { + res.reservationFor.location.address.streetAddress = addr[1]; + } + return res; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/regiondo.json new/kitinerary-21.08.1/src/lib/scripts/regiondo.json --- old/kitinerary-21.08.0/src/lib/scripts/regiondo.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/regiondo.json 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,13 @@ +{ + "filter": [ + { + "field": "passTypeIdentifier", + "match": "pass.de.regiondo.pass", + "mimeType": "application/vnd.apple.pkpass", + "scope": "Current" + } + ], + "function": "parsePass", + "mimeType": "application/vnd.apple.pkpass", + "script": "regiondo.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/ryanair.js new/kitinerary-21.08.1/src/lib/scripts/ryanair.js --- old/kitinerary-21.08.0/src/lib/scripts/ryanair.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/ryanair.js 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,20 @@ +/* + SPDX-FileCopyrightText: 2021 Volker Krause <[email protected]> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parseBoardingPass(pdf, node, triggerNode) { + console.log(pdf); + var res = triggerNode.result[0]; + const page = pdf.pages[triggerNode.location]; + const timesText = page.textInRect(0.5, 0.5, 1, 1); + console.log(timesText); + const times = timesText.match(/\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\d\d:\d\d\./); + res.reservationFor.boardingTime = JsonLd.toDateTime(times[2], "hh:mm", "en"); + res.reservationFor.departureTime = JsonLd.toDateTime(times[3], "hh:mm", "en"); + res.reservationFor.arrivalTime = JsonLd.toDateTime(times[4], "hh:mm", "en"); + + res.reservationFor.departureAirport.name = page.textInRect(0.5, 0.3, 0.75, 0.375); + res.reservationFor.arrivalAirport.name = page.textInRect(0.75, 0.3, 1, 0.375); + return res; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/ryanair.json new/kitinerary-21.08.1/src/lib/scripts/ryanair.json --- old/kitinerary-21.08.0/src/lib/scripts/ryanair.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-21.08.1/src/lib/scripts/ryanair.json 2021-08-30 18:21:55.000000000 +0200 @@ -0,0 +1,13 @@ +{ + "filter": [ + { + "field": "operatingCarrierDesignator", + "match": "FR", + "mimeType": "internal/iata-bcbp", + "scope": "Descendants" + } + ], + "function": "parseBoardingPass", + "mimeType": "application/pdf", + "script": "ryanair.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/sncf.js new/kitinerary-21.08.1/src/lib/scripts/sncf.js --- old/kitinerary-21.08.0/src/lib/scripts/sncf.js 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/src/lib/scripts/sncf.js 2021-08-30 18:21:55.000000000 +0200 @@ -266,16 +266,16 @@ return reservations; } -function parseOuigoEmail(html) +function parseOuiEmail(html) { if (html.eval('//*[@data-select="travel-summary-reference"]').length > 0) { - return parseOuigoSummary(html); + return parseOuiSummary(html); } else { - return parseOuigoConfirmation(html); + return parseOuiConfirmation(html); } } -function parseOuigoSummaryTime(htmlElem) +function parseOuiSummaryTime(htmlElem) { var timeStr = htmlElem[0].recursiveContent; var time = timeStr.match(/(\d+ [^ ]+ \d+) +[^ ]+ (\d+:\d+)/); @@ -286,7 +286,7 @@ return JsonLd.toDateTime(time[1] + time[2].replace('h', ':'), "d MMMMhh:mm", "fr"); } -function parseOuigoSummary(html) +function parseOuiSummary(html) { // TODO extract passenger names var res = JsonLd.newTrainReservation(); @@ -296,7 +296,7 @@ res.reservationFor.arrivalStation.name = destinations[0].content; res.reservationNumber = html.eval('//*[@data-select="travel-summary-reference"]')[0].content; - res.reservationFor.departureTime = parseOuigoSummaryTime(html.eval('//*[@data-select="travel-departureDate"]')); + res.reservationFor.departureTime = parseOuiSummaryTime(html.eval('//*[@data-select="travel-departureDate"]')); var trainNum = html.eval('//*[@data-select="passenger-detail-outwardFares"]//*[@class="passenger-detail__equipment"]'); if (trainNum.length == 2 || trainNum[1].content == trainNum[3].content) { @@ -313,7 +313,7 @@ var retour = JsonLd.newTrainReservation(); retour.reservationFor.departureStation.name = origins[1] ? origins[1].content : res.reservationFor.arrivalStation.name; retour.reservationFor.arrivalStation.name = destinations[1] ? destinations[1].content : res.reservationFor.departureStation.name; - retour.reservationFor.departureTime = parseOuigoSummaryTime(retourTime); + retour.reservationFor.departureTime = parseOuiSummaryTime(retourTime); trainNum = html.eval('//*[@data-select="passenger-detail-inwardFares"]//*[@class="passenger-detail__equipment"]'); if (trainNum.length == 2 || trainNum[1].content == trainNum[3].content) { retour.reservationFor.trainNumber = trainNum[0].content + " " + trainNum[1].content; @@ -322,7 +322,7 @@ return [res, retour]; } -function parseOuigoConfirmation(html) +function parseOuiConfirmation(html) { var reservations = new Array(); @@ -433,3 +433,35 @@ } return reservations; } + +function parseOuigoConfirmation(html) { + var reservations = new Array(); + const refNum = html.eval('//strong')[1].content; + const tabs = html.eval('//table//table//table[@class = "rsz_320"]'); + for (const tab of tabs) { + const text = tab.recursiveContent; + if (!text.match(/TRAJET/)) { + continue; + } + + var idx = 0; + while (true) { + const date = text.substr(idx).match(/\w+ (\d{1,2} \w+ \d{4})/); + if (!date) { + break; + } + const leg = text.substr(idx).match(/(\d{2}h\d{2})\s+(.*?)\n\s+(\d{2}h\d{2})\s+(.*?)\n\s+TRAIN N?? *(.*)\n/); + var res = JsonLd.newTrainReservation(); + res.reservationNumber = refNum; + res.reservationFor.departureTime = JsonLd.toDateTime(date[1] + leg[1], "d MMMM yyyyhh'h'mm", "fr"); + res.reservationFor.departureStation.name = leg[2]; + res.reservationFor.arrivalStation.name = leg[4]; + res.reservationFor.arrivalTime = JsonLd.toDateTime(date[1] + leg[3], "d MMMM yyyyhh'h'mm", "fr"); + res.reservationFor.trainNumber = leg[5]; + reservations.push(res); + + idx += leg[0].length + leg.index; + } + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/scripts/sncf.json new/kitinerary-21.08.1/src/lib/scripts/sncf.json --- old/kitinerary-21.08.0/src/lib/scripts/sncf.json 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/src/lib/scripts/sncf.json 2021-08-30 18:21:55.000000000 +0200 @@ -63,7 +63,7 @@ "scope": "Ancestors" } ], - "function": "parseOuigoEmail", + "function": "parseOuiEmail", "mimeType": "text/html", "script": "sncf.js" }, @@ -98,5 +98,18 @@ "function": "parseTerConfirmation", "mimeType": "text/html", "script": "sncf.js" + }, + { + "filter": [ + { + "field": "From", + "match": "[email protected]", + "mimeType": "message/rfc822", + "scope": "Ancestors" + } + ], + "function": "parseOuigoConfirmation", + "mimeType": "text/html", + "script": "sncf.js" } ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/uic9183/rct2ticket.cpp new/kitinerary-21.08.1/src/lib/uic9183/rct2ticket.cpp --- old/kitinerary-21.08.0/src/lib/uic9183/rct2ticket.cpp 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/src/lib/uic9183/rct2ticket.cpp 2021-08-30 18:21:55.000000000 +0200 @@ -10,6 +10,7 @@ #include <QDateTime> #include <QDebug> +#include <QRegularExpression> #include <cstring> @@ -22,6 +23,7 @@ public: QDate firstDayOfValidity() const; QDateTime parseTime(const QString &dateStr, const QString &timeStr) const; + QString reservationPatternCapture(QStringView name) const; Uic9183TicketLayout layout; QDateTime contextDt; @@ -67,6 +69,24 @@ return QDateTime({year, d.month(), d.day()}, t); } +static constexpr const char* res_patterns[] = { + "ZUG +(?P<train_number>\\d+) +(?P<train_category>[A-Z][A-Z0-9]+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)" +}; + +QString Rct2TicketPrivate::reservationPatternCapture(QStringView name) const +{ + const auto text = layout.text(8, 0, 72, 1); + for (const auto *pattern : res_patterns) { + QRegularExpression re{QLatin1String(pattern), QRegularExpression::CaseInsensitiveOption}; + Q_ASSERT(re.isValid()); + const auto match = re.match(text); + if (match.hasMatch()) { + return match.captured(name); + } + } + return {}; +} + // 6x "U_TLAY" // 2x version (always "01") @@ -104,12 +124,13 @@ return d->firstDayOfValidity(); } -static const struct { +static constexpr const struct { const char *name; // case folded Rct2Ticket::Type type; } rct2_ticket_type_map[] = { - { "ticket + reservation", Rct2Ticket::TransportReservation }, - { "fahrschein + reservierung", Rct2Ticket::TransportReservation }, + { "ticket+reservation", Rct2Ticket::TransportReservation }, + { "fahrschein+reservierung", Rct2Ticket::TransportReservation }, + { "menetjegy+helyjegy", Rct2Ticket::TransportReservation }, { "upgrade", Rct2Ticket::Upgrade }, { "aufpreis", Rct2Ticket::Upgrade }, { "ticket", Rct2Ticket::Transport }, @@ -125,8 +146,8 @@ // in theory: columns 15 - 18 blank, columns 19 - 51 ticket type (1-based indices!) // however, some providers overrun and also use the blank columns, so consider those too // if they are really empty, we trim them anyway. - const auto typeName1 = d->layout.text(0, 14, 38, 1).trimmed().toCaseFolded(); - const auto typeName2 = d->layout.text(1, 14, 38, 1).trimmed().toCaseFolded(); // used for alternative language type name + const auto typeName1 = d->layout.text(0, 14, 38, 1).trimmed().remove(QLatin1Char(' ')).toCaseFolded(); + const auto typeName2 = d->layout.text(1, 14, 38, 1).trimmed().remove(QLatin1Char(' ')).toCaseFolded(); // used for alternative language type name // prefer exact matches for (auto it = std::begin(rct2_ticket_type_map); it != std::end(rct2_ticket_type_map); ++it) { @@ -150,12 +171,12 @@ QDateTime Rct2Ticket::outboundDepartureTime() const { - return d->parseTime(d->layout.text(6, 1, 5, 1), d->layout.text(6, 7, 5, 1)); + return d->parseTime(d->layout.text(6, 1, 5, 1).trimmed(), d->layout.text(6, 7, 5, 1).trimmed()); } QDateTime Rct2Ticket::outboundArrivalTime() const { - return d->parseTime(d->layout.text(6, 52, 5, 1), d->layout.text(6, 58, 5, 1)); + return d->parseTime(d->layout.text(6, 52, 5, 1).trimmed(), d->layout.text(6, 58, 5, 1).trimmed()); } QString Rct2Ticket::outboundDepartureStation() const @@ -185,8 +206,13 @@ { const auto t = type(); if (t == Reservation || t == TransportReservation || t == Upgrade) { + auto num = d->reservationPatternCapture(u"train_number"); + if (!num.isEmpty()) { + return d->reservationPatternCapture(u"train_category") + QLatin1Char(' ') + num; + } + const auto cat = d->layout.text(8, 13, 3, 1).trimmed(); - auto num = d->layout.text(8, 7, 5, 1).trimmed(); + num = d->layout.text(8, 7, 5, 1).trimmed(); // check for train number bleeding into our left neighbour field (happens e.g. on ??BB IRT/RES tickets) if (num.isEmpty() || num.at(0).isDigit()) { @@ -213,7 +239,8 @@ { const auto t = type(); if (t == Reservation || t == TransportReservation) { - return d->layout.text(8, 26, 3, 1).trimmed(); + const auto coach = d->reservationPatternCapture(u"coach"); + return coach.isEmpty() ? d->layout.text(8, 26, 3, 1).trimmed() : coach; } return {}; } @@ -222,6 +249,11 @@ { const auto t = type(); if (t == Reservation || t == TransportReservation) { + const auto seat = d->reservationPatternCapture(u"seat"); + if (!seat.isEmpty()) { + return seat; + } + const auto row8 = d->layout.text(8, 48, 23, 1).trimmed(); if (!row8.isEmpty()) { return row8; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-21.08.0/src/lib/uic9183/uic9183ticketlayout.cpp new/kitinerary-21.08.1/src/lib/uic9183/uic9183ticketlayout.cpp --- old/kitinerary-21.08.0/src/lib/uic9183/uic9183ticketlayout.cpp 2021-08-05 00:09:33.000000000 +0200 +++ new/kitinerary-21.08.1/src/lib/uic9183/uic9183ticketlayout.cpp 2021-08-30 18:21:55.000000000 +0200 @@ -178,7 +178,8 @@ if (offset >= 0) { s[f.row() + i - row] += lines.at(i).mid(offset).left(width); } else { - s[f.row() + i - row] += lines.at(i); // TODO left padding by offset, truncate by width + offset + s[f.row() + i - row] += QString(-offset, QLatin1Char(' ')); + s[f.row() + i - row] += lines.at(i); // TODO truncate by width + offset } } }
