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

Reply via email to