Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kitinerary for openSUSE:Factory checked in at 2023-05-12 20:33:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitinerary (Old) and /work/SRC/openSUSE:Factory/.kitinerary.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitinerary" Fri May 12 20:33:25 2023 rev:61 rq:1086341 version:23.04.1 Changes: -------- --- /work/SRC/openSUSE:Factory/kitinerary/kitinerary.changes 2023-04-23 22:47:08.898604579 +0200 +++ /work/SRC/openSUSE:Factory/.kitinerary.new.1533/kitinerary.changes 2023-05-12 20:33:55.904864334 +0200 @@ -1,0 +2,9 @@ +Tue May 9 10:47:06 UTC 2023 - Christophe Marin <christo...@krop.fr> + +- Update to 23.04.1 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/gear/23.04.1/ +- Too many changes to list here. + +------------------------------------------------------------------- Old: ---- kitinerary-23.04.0.tar.xz kitinerary-23.04.0.tar.xz.sig New: ---- kitinerary-23.04.1.tar.xz kitinerary-23.04.1.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitinerary.spec ++++++ --- /var/tmp/diff_new_pack.DWs2CA/_old 2023-05-12 20:33:56.504866340 +0200 +++ /var/tmp/diff_new_pack.DWs2CA/_new 2023-05-12 20:33:56.508866353 +0200 @@ -19,7 +19,7 @@ %define libname libKPimItinerary5 %bcond_without released Name: kitinerary -Version: 23.04.0 +Version: 23.04.1 Release: 0 Summary: Data model and extraction system for travel reservations License: LGPL-2.1-or-later ++++++ kitinerary-23.04.0.tar.xz -> kitinerary-23.04.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/CMakeLists.txt new/kitinerary-23.04.1/CMakeLists.txt --- old/kitinerary-23.04.0/CMakeLists.txt 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/CMakeLists.txt 2023-05-06 10:59:18.000000000 +0200 @@ -3,7 +3,7 @@ # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16 FATAL_ERROR) -set(PIM_VERSION "5.23.0") +set(PIM_VERSION "5.23.1") project(KItinerary VERSION ${PIM_VERSION}) set(KF_MIN_VERSION "5.91.0") @@ -48,8 +48,8 @@ find_package(SharedMimeInfo 1.3 REQUIRED) endif() -set(KMIME_VERSION "5.23.0") -set(PIM_PKPASS "5.23.0") +set(KMIME_VERSION "5.23.1") +set(PIM_PKPASS "5.23.1") find_package(KPim${KF_MAJOR_VERSION}Mime ${KMIME_VERSION} CONFIG REQUIRED) find_package(KPimPkPass ${PIM_PKPASS} CONFIG REQUIRED) Binary files old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin and new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json --- old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,26 @@ +[ + { + "@context": "http://schema.org", + "@type": "Ticket", + "issuedBy": { + "@type": "Organization", + "identifier": "uic:1080", + "name": "DB AG" + }, + "name": "Deutschlandticket", + "ticketNumber": "J7EJ5KNN", + "ticketToken": "aztecbin:I1VUMDExMDgwVFQwMDUwLAIUaF474Q5HKwSfP4bJ3CWga/66E1ACFD9cK3OOzpglLvERyv9+ycn9TpbqAAAAADAzODd4nAuN93B1dDEwNDAwNTY0sDDwMnf1MvX282NAAkBZEyMDI2OgtIGJi6uLa2h8iI9jJFDYyMwgwMfR0wCk38QASBkZgICpX2JuqpWBKULE3DsnsbgYKGYG5BhBxMwCUouK9awMjODKDI3cD+/JKclMz07NLAFKGFqC1ILMNgzLz1MwMNQzMNUDuUTBwMDKwIDLKbMYLGgGFTQGCgLVWoKMA+oyNPRJzUxU8C9KT8xLNDCFi4MoM7jZQGVg5xhagHmWbokZRdmJRSWpEEmwqKG5S2ppSXFyRk5iXkpJZnJ2aklovJuPa4ShsYGhqVHSJoa2h7wHT7s3iApyNLLKtXjliwVHFi9X4Gps4Jhx6tIhnnlPzh26c2jGxIYmNYZ4hhOCSns5QrIjs+bYqJnJymx9+PSyBksDC5dKQA2DCX+3gxATQ5ey9urFs6Wdk7mLlRd7S0drLxCYqcC5hYHBNWPqzJ0JZ7sKPhhwtjs4bPut1SCk4FB1Uds04WADBwcHAAcxge4=", + "ticketedSeat": { + "@type": "Seat", + "seatingType": "2" + }, + "underName": { + "@type": "Person", + "familyName": "Organa", + "givenName": "Leia", + "name": "Leia Organa" + }, + "validFrom": "2023-05-01T00:00:00+02:00", + "validUntil": "2023-06-01T00:03:00+02:00" + } +] Binary files old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin and new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json --- old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,26 @@ +[ + { + "@context": "http://schema.org", + "@type": "Ticket", + "issuedBy": { + "@type": "Organization", + "identifier": "uic:1080", + "name": "DB AG" + }, + "name": "Deutschlandticket", + "ticketNumber": "J7EJ5KNN", + "ticketToken": "aztecbin:I1VUMDExMDgwVFQwMDUwLAIUP/mxp2V39hvTz76OYofP0x8Ijx0CFCupd/BViXc/b5hrwemuH/WXMnXRAAAAADA0MjV4nAuN93B1dDEwNDAwNTY0sDDwMnf1MvX282NAAkBZEyMDI2OgtIGJi6uLa2h8iI9jJFDY2NgoyDnEyMDA0BSILQwMgQIGBgaWbokZRdmJRSWpQGONgOosgYIGZv5F6Yl5iUAGXMjEJzUz0cAQohPoBkNzl9TSkuLkjJzEvJSSzOTs1BKg0SDlIGOBCoE8UyBlBuIYBKQWFefnaaTmaRoYA7lAV4LNBDvVDCxgChIAatADEmYG5ggRAz2Qg8AmI9SYAUUsECLGYDVmZjARQyNDsEtMQY4xNnc/vCenJDNdoSw/VwFsgx7IYoWkzGIw1wzMNTQG22EM0m/sGhoE5APNM4TYYGKpY2AQGu/m4xphaAzyZ9ImhraHvAdPuzeICnI0ssq1eOWLBUcWL1fgamzgmHHq0iGeeU/OHbpzaMbEhiY1hniGE4JKezlCsiOz5tiomcnKbH349LIGSwMLl0pADYMJf7eDEBNDl7L26sWzpZ2TuYuVF3tLR2svEJipwLmFgcE1Y+rMnQlnuwo+GHC2Ozhs+63VIKTgUHVR2zThYAMHBwcAqg6Oow==", + "ticketedSeat": { + "@type": "Seat", + "seatingType": "2" + }, + "underName": { + "@type": "Person", + "familyName": "Organa", + "givenName": "Leia", + "name": "Leia Organa" + }, + "validFrom": "2023-05-01T00:00:00+02:00", + "validUntil": "2023-06-01T00:03:00+02:00" + } +] Binary files old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin and new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json --- old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,26 @@ +[ + { + "@context": "http://schema.org", + "@type": "Ticket", + "issuedBy": { + "@type": "Organization", + "identifier": "uic:1080", + "name": "DB AG" + }, + "name": "Deutschlandticket", + "ticketNumber": "J7EJ5KNN", + "ticketToken": "aztecbin:I1VUMDExMDgwVFQwMDUwLAIUVhuRhpHjgkMGCpfK1dUqgGCqBNQCFE+Wjm/DOV5pQuW2XCnZ1gmaC1GQAAAAADAxNjN4nAGYAGf/VV9GTEVYMTMwMTUyYrIAhuENwctHgBURCIEFHoRKbxZTWXOnIAqBgAiYytLCDJ7kzsLcwpiRgIImAF8AyBEivQhUa1lqnDwmNh0cteHl0ygEgAQKJFB8ADQPi0ASAgCKIyuro5sbQ2MLcyOjSxtbK6AQmSAJtAAARWiVmblgzYpw8DAJh0BAtvsqgBIgQHrRKzVgwYAICAiTHjco", + "ticketedSeat": { + "@type": "Seat", + "seatingType": "2" + }, + "underName": { + "@type": "Person", + "familyName": "Organa", + "givenName": "Leia", + "name": "Leia Organa" + }, + "validFrom": "2023-05-01T00:00:00+02:00", + "validUntil": "2023-06-01T00:03:00+02:00" + } +] Binary files old/kitinerary-23.04.0/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin and new/kitinerary-23.04.1/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json new/kitinerary-23.04.1/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json --- old/kitinerary-23.04.0/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,17 @@ +[ + { + "@context": "http://schema.org", + "@type": "Ticket", + "name": "Deutschlandticket", + "ticketNumber": "2099643", + "ticketToken": "aztecbin:noGAcCGqbL4sBzo3/kOD7wj1EhQvyK2NciRfyGSyr10XpjR/kGwTlKxLfXpmRnDS6vft4BmcB+iiccTNWkQBqOyD/OGi8lEP2KA5vsu6fPVzVp4dpLhocyUSBfekTORtnFkZAp7srho5vOQFGWsDmZbFKs4X/WBylo9/KKh4WeoYBc+aDwAAAAAAAAAAAABWRFYRCX8hgchfN4HAOfLABJIEWvFRilfF13NDSKABrFDMrUDcfzcaNKY828sW63tyxXfYpVHFn89CBrRAy54q04/zDpd9C44NShkBAfJ18d+GMOECTHS1qOp8AD1lteIBFOzalpJeuhRYSc4m/XSERlk94RpuFWo4TLvHq57lz8fV/GlaHzwUSrdPKRUSUHnr60OBKipr75sIyT3HKLShbyHyvJ4G33KAHsCtKurbzVbeU1k4RNAVont2Pxzlpt3M/d3DuAkGsIDC0LmOXzgBgUIIREVWRFYRAhY=", + "underName": { + "@type": "Person", + "familyName": "Mustermann", + "givenName": "Max", + "name": "Max Mustermann" + }, + "validFrom": "2023-03-01T00:00:00", + "validUntil": "2023-03-31T23:59:58" + } +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/autotests/nameoptimizertest.cpp new/kitinerary-23.04.1/autotests/nameoptimizertest.cpp --- old/kitinerary-23.04.0/autotests/nameoptimizertest.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/autotests/nameoptimizertest.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -131,6 +131,21 @@ QCOMPARE(p.givenName(), expected); } + + void testSpaceExpansion() + { + Person p; + p.setFamilyName(s("THEDRAGON")); + p.setGivenName(s("KONQI")); + p = NameOptimizer::optimizeName(s("KONQI THE DRAGON"), p); + QCOMPARE(p.familyName(), s("THE DRAGON")); + + p.setFamilyName(s("THEDRAGON")); + p.setGivenName(s("KONQIDR")); + p = NameOptimizer::optimizeName(s("KONQI DR THE DRAGON"), p); + QCOMPARE(p.familyName(), s("THE DRAGON")); + QCOMPARE(p.givenName(), s("KONQI")); + } }; QTEST_GUILESS_MAIN(NameOptimizerTest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/autotests/terminalfindertest.cpp new/kitinerary-23.04.1/autotests/terminalfindertest.cpp --- old/kitinerary-23.04.0/autotests/terminalfindertest.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/autotests/terminalfindertest.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -43,6 +43,7 @@ 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("DEL6") << s(" T-3 (DEL)") << 4 << s("3"); QTest::newRow("FRA") << s(", Terminal 2 (FRA)\nMadrid (MAD)") << 12 << s("2"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/po/de/kitinerary.po new/kitinerary-23.04.1/po/de/kitinerary.po --- old/kitinerary-23.04.0/po/de/kitinerary.po 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/po/de/kitinerary.po 2023-05-06 10:59:18.000000000 +0200 @@ -13,7 +13,7 @@ "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=n != 1;\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 22.12.1\n" #: calendarhandler.cpp:160 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/po/gl/kitinerary.po new/kitinerary-23.04.1/po/gl/kitinerary.po --- old/kitinerary-23.04.0/po/gl/kitinerary.po 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/po/gl/kitinerary.po 2023-05-06 10:59:18.000000000 +0200 @@ -1,13 +1,13 @@ # Copyright (C) YEAR This_file_is_part_of_KDE # This file is distributed under the same license as the PACKAGE package. -# Adrian Chaves <adr...@chaves.io>, 2018, 2019, 2020. +# Adrian Chaves <adr...@chaves.io>, 2018, 2019, 2020, 2023. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-07-20 00:46+0000\n" -"PO-Revision-Date: 2020-02-16 23:04+0100\n" +"PO-Revision-Date: 2023-04-30 11:00+0200\n" "Last-Translator: Adrián Chaves (Gallaecio) <adr...@chaves.io>\n" "Language-Team: Galician <proxe...@trasno.gal>\n" "Language: gl\n" @@ -15,14 +15,13 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Lokalize 19.08.3\n" +"X-Generator: Lokalize 23.04.0\n" #: calendarhandler.cpp:160 -#, fuzzy, kde-format -#| msgid "Coach: %1" +#, kde-format msgctxt "canceled train/flight/loading reservation" msgid "Canceled: %1" -msgstr "Coche: %1" +msgstr "Cancelada: %1" #: calendarhandler.cpp:186 #, kde-format @@ -45,8 +44,7 @@ msgstr "Hora de embarque: %1" #: calendarhandler.cpp:219 -#, fuzzy, kde-format -#| msgid "Departure gate: %1" +#, kde-format msgctxt "flight departure gate" msgid "Departure gate: %1" msgstr "Porta de saÃda: %1" @@ -94,15 +92,14 @@ msgstr "Bus %1 de %2 a %3" #: calendarhandler.cpp:306 -#, fuzzy, kde-format -#| msgid "Train %1 from %2 to %3" +#, kde-format msgid "Ferry from %1 to %2" -msgstr "Tren %1 de %2 a %3" +msgstr "Transbordador de %1 a %2" #: calendarhandler.cpp:319 #, kde-format msgid "Ticket number: %1" -msgstr "" +msgstr "Número de tÃcket: %1" #: calendarhandler.cpp:329 #, kde-format @@ -112,28 +109,27 @@ #: calendarhandler.cpp:340 #, kde-format msgid "Check-in: %1" -msgstr "" +msgstr "Entrada: %1" #: calendarhandler.cpp:343 #, kde-format msgid "Check-out: %1" -msgstr "" +msgstr "SaÃda: %1" #: calendarhandler.cpp:346 #, kde-format msgid "Phone: %1" -msgstr "" +msgstr "Teléfono: %1" #: calendarhandler.cpp:349 #, kde-format msgid "Email: %1" -msgstr "" +msgstr "Correo electrónico: %1" #: calendarhandler.cpp:352 -#, fuzzy, kde-format -#| msgid "Seat: %1" +#, kde-format msgid "Website: %1" -msgstr "Asento: %1" +msgstr "Sitio web: %1" #: calendarhandler.cpp:395 #, kde-format @@ -146,8 +142,7 @@ msgstr "Reserva de restaurante: %1" #: calendarhandler.cpp:456 -#, fuzzy, kde-format -#| msgid "Number Of People: %1" +#, kde-format msgid "Number of people: %1" msgstr "Número de persoas: %1" @@ -157,8 +152,7 @@ msgstr "A nome de: %1" #: calendarhandler.cpp:473 -#, fuzzy, kde-format -#| msgid "Rental Car reservation: %1" +#, kde-format msgid "Rental car reservation: %1" msgstr "Reserva de coche de alugamento: %1" @@ -168,6 +162,8 @@ "Pickup location: %1\n" "%2\n" msgstr "" +"Lugar de recollida: %1\n" +"%2\n" #: calendarhandler.cpp:488 #, kde-format @@ -175,13 +171,11 @@ "Dropoff location: %1\n" "%2\n" msgstr "" +"Lugar de chegada: %1\n" +"%2\n" #: calendarhandler.cpp:517 -#, 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-23.04.0/po/zh_CN/kitinerary.po new/kitinerary-23.04.1/po/zh_CN/kitinerary.po --- old/kitinerary-23.04.0/po/zh_CN/kitinerary.po 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/po/zh_CN/kitinerary.po 2023-05-06 10:59:18.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-20 00:46+0000\n" -"PO-Revision-Date: 2023-04-10 14:12\n" +"PO-Revision-Date: 2023-04-29 08:36\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-23.04.0/src/cli/org.kde.kitinerary-extractor.appdata.xml new/kitinerary-23.04.1/src/cli/org.kde.kitinerary-extractor.appdata.xml --- old/kitinerary-23.04.0/src/cli/org.kde.kitinerary-extractor.appdata.xml 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/cli/org.kde.kitinerary-extractor.appdata.xml 2023-05-06 10:59:18.000000000 +0200 @@ -116,9 +116,9 @@ <binary>kitinerary-extractor</binary> </provides> <releases> + <release version="5.23.1" date="2023-05-11"/> <release version="5.23.0" date="2023-04-20"/> <release version="5.22.3" date="2023-03-02"/> <release version="5.22.2" date="2023-02-02"/> - <release version="5.22.1" date="2023-01-05"/> </releases> </component> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.cpp new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.cpp --- old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -79,6 +79,24 @@ return d.addDays(type1DepartureDate()); } +QDate SSBv3Ticket::type2ValidFrom(const QDateTime &contextDate) const +{ + if (ticketTypeCode() != SSBv3Ticket::NRT) { + return {}; + } + + return issueDate(contextDate).addDays(type2FirstDayOfValidity()); +} + +QDate SSBv3Ticket::type2ValidUntil(const QDateTime& contextDate) const +{ + if (ticketTypeCode() != SSBv3Ticket::NRT) { + return {}; + } + + return issueDate(contextDate).addDays(type2LastDayOfValidity()); +} + QByteArray SSBv3Ticket::rawData() const { return m_data; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.h new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.h --- old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.h 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.h 2023-05-06 10:59:18.000000000 +0200 @@ -115,6 +115,10 @@ Q_INVOKABLE QDate issueDate(const QDateTime &contextDate = QDateTime::currentDateTime()) const; /** Departure day for type 1 (IRT/RES/BOA) tickets. */ Q_INVOKABLE QDate type1DepartureDay(const QDateTime &contextDate = QDateTime::currentDateTime()) const; + /** First day of validity for type 2 (NRT) tickets. */ + Q_INVOKABLE QDate type2ValidFrom(const QDateTime &contextDate = QDateTime::currentDateTime()) const; + /** Last day of validity for type 2 (NRT) tickets. */ + Q_INVOKABLE QDate type2ValidUntil(const QDateTime &contextDate = QDateTime::currentDateTime()) const; /** Raw barcode data. */ QByteArray rawData() const; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/extractorpostprocessor.cpp new/kitinerary-23.04.1/src/lib/extractorpostprocessor.cpp --- old/kitinerary-23.04.0/src/lib/extractorpostprocessor.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/extractorpostprocessor.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -266,6 +266,13 @@ trip.setArrivalTime(processTrainTripTime(trip.arrivalTime(), trip.departureDay(), trip.arrivalStation())); trip.setTrainNumber(trip.trainNumber().simplified()); trip.setTrainName(trip.trainName().simplified()); + + // arrival less than a day before departure is an indication of the extractor failing to detect day rollover + const auto duration = trip.departureTime().secsTo(trip.arrivalTime()); + if (duration < 0 && duration > -3600*24 && trip.departureTime().timeSpec() == trip.arrivalTime().timeSpec()) { + trip.setArrivalTime(trip.arrivalTime().addDays(1)); + } + return trip; } @@ -379,7 +386,7 @@ if (res.reservationFor().isValid()) { res.setReservationFor(processBoatTrip(res.reservationFor().value<BoatTrip>())); } - return res; + return processReservation(res); } BoatTrip ExtractorPostprocessorPrivate::processBoatTrip(BoatTrip trip) const diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/flightpostprocessor.cpp new/kitinerary-23.04.1/src/lib/flightpostprocessor.cpp --- old/kitinerary-23.04.0/src/lib/flightpostprocessor.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/flightpostprocessor.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -98,7 +98,7 @@ dt.setDate(flight.departureDay()); } - if (dt.timeSpec() == Qt::TimeZone || codes.empty()) { + if ((dt.timeSpec() == Qt::TimeZone && dt.timeZone() != QTimeZone::utc()) || codes.empty()) { return dt; } @@ -115,7 +115,7 @@ if (dt.timeSpec() == Qt::OffsetFromUTC || dt.timeSpec() == Qt::LocalTime) { dt.setTimeSpec(Qt::TimeZone); dt.setTimeZone(tz); - } else if (dt.timeSpec() == Qt::UTC) { + } else if (dt.timeSpec() == Qt::UTC || (dt.timeSpec() == Qt::TimeZone && dt.timeZone() == QTimeZone::utc())) { dt = dt.toTimeZone(tz); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/pdf/pdfbarcodeutil.cpp new/kitinerary-23.04.1/src/lib/pdf/pdfbarcodeutil.cpp --- old/kitinerary-23.04.0/src/lib/pdf/pdfbarcodeutil.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/pdf/pdfbarcodeutil.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -20,7 +20,7 @@ // vector path complexity limits MinPathElementCount2D = 400, MaxPathElementCount2D = 5400, - MinPathElementCount1D = 200, + MinPathElementCount1D = 150, MaxPathElementCount1D = 400, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.cpp new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.cpp --- old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -5,6 +5,7 @@ */ #include "eradocumentprocessor.h" +#include "logging.h" #include "era/elbticket.h" #include "era/ssbticketreader.h" @@ -12,6 +13,12 @@ #include "era/ssbv2ticket.h" #include "era/ssbv3ticket.h" +#include <KItinerary/ExtractorResult> + +#include <KItinerary/Reservation> +#include <KItinerary/Ticket> +#include <KItinerary/TrainTrip> + using namespace KItinerary; bool ElbDocumentProcessor::canHandleData(const QByteArray &encodedData, [[maybe_unused]] QStringView fileName) const @@ -39,3 +46,64 @@ node.setContent(SSBTicketReader::read(encodedData)); return node; } + +TrainStation makeStation(int idType, const QString &alphaId, int numericId) +{ + TrainStation station; + if (idType == 0 && numericId > 10'00000 && numericId < 99'99999) { + station.setIdentifier(QLatin1String("uic:") + QString::number(numericId)); + station.setName(QString::number(numericId)); + } else if (idType == 1 && alphaId.size() == 5 && std::all_of(alphaId.begin(), alphaId.end(), [](QChar c) { return c.isUpper(); })) { + // TODO is the identifier type defined in that case?? + station.setName(alphaId); + } + return station; +} + +void SsbDocumentProcessor::preExtract(ExtractorDocumentNode &node, [[maybe_unused]] const ExtractorEngine *engine) const +{ + if (node.isA<SSBv3Ticket>()) { + const auto ssb = node.content<SSBv3Ticket>(); + + Seat seat; + seat.setSeatingType(QString::number(ssb.classOfTravel())); + Ticket ticket; + ticket.setTicketToken(QLatin1String("aztecbin:") + QString::fromLatin1(ssb.rawData().toBase64())); + + Organization issuer; + issuer.setIdentifier(QLatin1String("uic:") + QString::number(ssb.issuerCode())); // left pad with 0 to 4 digets? + TrainTrip trip; + trip.setProvider(issuer); + + TrainReservation res; + res.setReservationNumber(ssb.tcn()); + ticket.setTicketNumber(ssb.tcn()); + + switch (ssb.ticketTypeCode()) { + case SSBv3Ticket::IRT_RES_BOA: + trip.setDepartureDay(ssb.type1DepartureDay(node.contextDateTime())); + trip.setTrainNumber(ssb.type1TrainNumber()); + seat.setSeatSection(QString::number(ssb.type1CoachNumber())); + seat.setSeatNumber(ssb.type1SeatNumber()); + trip.setDepartureStation(makeStation(ssb.type1StationCodeNumericOrAlpha(), ssb.type1DepartureStationAlpha(), ssb.type1DepartureStationNum())); + trip.setDepartureStation(makeStation(ssb.type1StationCodeNumericOrAlpha(), ssb.type1ArrivalStationAlpha(), ssb.type1ArrivalStationNum())); + break; + case SSBv3Ticket::NRT: + trip.setDepartureStation(makeStation(ssb.type2StationCodeNumericOrAlpha(), ssb.type2DepartureStationAlpha(), ssb.type2DepartureStationNum())); + trip.setArrivalStation(makeStation(ssb.type2StationCodeNumericOrAlpha(), ssb.type2ArrivalStationAlpha(), ssb.type2ArrivalStationNum())); + ticket.setValidFrom(ssb.type2ValidFrom(node.contextDateTime()).startOfDay()); + ticket.setValidUntil({ssb.type2ValidUntil(node.contextDateTime()), {23, 59, 59}}); + trip.setDepartureDay(ssb.type2ValidFrom(node.contextDateTime())); + break; + case SSBv3Ticket::GRT: + case SSBv3Ticket::RPT: + qCWarning(Log) << "Unsupported SSB v3 ticket type:" << ssb.ticketTypeCode(); + return; + } + + res.setReservationFor(trip); + ticket.setTicketedSeat(seat); + res.setReservedTicket(ticket); + node.addResult(QVector<QVariant>{QVariant::fromValue(res)}); + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.h new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.h --- old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.h 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.h 2023-05-06 10:59:18.000000000 +0200 @@ -24,6 +24,7 @@ public: bool canHandleData(const QByteArray &encodedData, QStringView fileName) const override; ExtractorDocumentNode createNodeFromData(const QByteArray &encodedData) const override; + void preExtract(ExtractorDocumentNode &node, const ExtractorEngine *engine) const override; }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/processors/uic9183documentprocessor.cpp new/kitinerary-23.04.1/src/lib/processors/uic9183documentprocessor.cpp --- old/kitinerary-23.04.0/src/lib/processors/uic9183documentprocessor.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/processors/uic9183documentprocessor.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -9,6 +9,7 @@ #include <KItinerary/ExtractorResult> #include <KItinerary/JsonLdDocument> #include <KItinerary/Uic9183Parser> +#include <KItinerary/Uic9183TicketLayout> #include <KItinerary/Rct2Ticket> #include <era/fcbticket.h> #include <uic9183/uic9183head.h> @@ -24,6 +25,7 @@ Uic9183DocumentProcessor::Uic9183DocumentProcessor() { + qRegisterMetaType<KItinerary::Uic9183TicketLayoutField>(); qRegisterMetaType<KItinerary::Vendor0080BLOrderBlock>(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/processors/vdvdocumentprocessor.cpp new/kitinerary-23.04.1/src/lib/processors/vdvdocumentprocessor.cpp --- old/kitinerary-23.04.0/src/lib/processors/vdvdocumentprocessor.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/processors/vdvdocumentprocessor.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -7,15 +7,14 @@ #include "vdvdocumentprocessor.h" #include <KItinerary/ExtractorResult> -#include <KItinerary/JsonLdDocument> +#include <KItinerary/Ticket> #include <KItinerary/VdvTicket> +#include <KItinerary/VdvTicketContent> #include <KItinerary/VdvTicketParser> #include <KLocalizedString> #include <QByteArray> -#include <QJsonArray> -#include <QJsonObject> using namespace KItinerary; @@ -40,32 +39,34 @@ { const auto vdv = node.content<VdvTicket>(); - QJsonObject seat; - seat.insert(QStringLiteral("@type"), QLatin1String("Seat")); + Seat seat; switch (vdv.serviceClass()) { + case VdvTicket::UnknownClass: + break; case VdvTicket::FirstClass: case VdvTicket::FirstClassUpgrade: - seat.insert(QStringLiteral("seatingType"), QStringLiteral("1")); + seat.setSeatingType(QStringLiteral("1")); break; case VdvTicket::SecondClass: - seat.insert(QStringLiteral("seatingType"), QStringLiteral("2")); + seat.setSeatingType(QStringLiteral("2")); break; default: break; } - QJsonObject ticket; - ticket.insert(QStringLiteral("@type"), QLatin1String("Ticket")); - ticket.insert(QStringLiteral("ticketToken"), QString(QLatin1String("aztecbin:") + QString::fromLatin1(vdv.rawData().toBase64()))); - ticket.insert(QStringLiteral("ticketedSeat"), seat); + Ticket ticket; + ticket.setTicketToken(QLatin1String("aztecbin:") + QString::fromLatin1(vdv.rawData().toBase64())); + ticket.setTicketedSeat(seat); if (vdv.serviceClass() == VdvTicket::FirstClassUpgrade) { - ticket.insert(QStringLiteral("name"), i18n("Upgrade")); + ticket.setName(i18n("Upgrade")); + } else if (const auto hdr = vdv.header(); hdr && hdr->productId == 9999) { + ticket.setName(QLatin1String("Deutschlandticket")); } else { - ticket.insert(QStringLiteral("name"), i18n("Ticket")); + ticket.setName(i18n("Ticket")); } - ticket.insert(QStringLiteral("ticketNumber"), vdv.ticketNumber()); - ticket.insert(QStringLiteral("validFrom"), JsonLdDocument::toJsonValue(vdv.beginDateTime())); - ticket.insert(QStringLiteral("validUntil"), JsonLdDocument::toJsonValue(vdv.endDateTime())); - ticket.insert(QStringLiteral("underName"), JsonLdDocument::toJson(vdv.person())); - node.addResult(QJsonArray({ticket})); + ticket.setTicketNumber(vdv.ticketNumber()); + ticket.setValidFrom(vdv.beginDateTime()); + ticket.setValidUntil(vdv.endDateTime()); + ticket.setUnderName(vdv.person()); + node.addResult(QVector<QVariant>({ticket})); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/atpi.js new/kitinerary-23.04.1/src/lib/scripts/atpi.js --- old/kitinerary-23.04.0/src/lib/scripts/atpi.js 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/atpi.js 2023-05-06 10:59:18.000000000 +0200 @@ -3,6 +3,20 @@ SPDX-License-Identifier: LGPL-2.0-or-later */ +function parseTravellerFromEvent(event, res) +{ + let reservations = []; + const travData = event.description.match(/Traveller\n\n(\d\. .* \/ .*\n)+/); + for (trav of travData[1].trim().split(/\n/)) { + const name = trav.match(/\d\. (.*) \/ (.*)/); + let r = JsonLd.clone(res); + r.underName.familyName = name[1]; + r.underName.givenName = name[2]; + reservations.push(r); + } + return reservations; +} + function parseFlightEvent(event) { let res = JsonLd.newFlightReservation(); const summary = event.summary.match(/([A-Z0-9]{2}) +(\d{1,4}) ([A-Z]{3})\/([A-Z]{3})/); @@ -20,7 +34,7 @@ res.reservationFor.arrivalAirport.name = arr[1]; res.reservationFor.arrivalTerminal = arr[2]; res.reservationNumber = event.description.match(/Airline reference: (.*)/)[1]; - return res; + return parseTravellerFromEvent(event, res); } function parseHotelEvent(event) { @@ -34,6 +48,5 @@ res.reservationFor.address.postalCode = addr[4].match(/[A-Z]{2}/) ? addr[3] : addr[4]; res.reservationFor.address.addressCountry = addr[4].match(/[A-Z]{2}/) ? addr[4] : addr[3]; res.reservationFor.telephone = '+' + addr[5]; - console.log(event); - return res; + return parseTravellerFromEvent(event, res); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.js new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.js --- old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.js 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,37 @@ +/* + SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parseConfirmation(html) { + const text = html.root.recursiveContent; + + let res = JsonLd.newLodgingReservation(); + res.reservationNumber = text.match(/Buchungsnummer: (.*)/)[1]; + const hotel = text.match(/Hotel\n(.*)\n(.*)\n(.*) - (.*) - (.*)/i); + res.reservationFor.name = hotel[1]; + res.reservationFor.address.streetAddress = hotel[2]; + res.reservationFor.address.postalCode = hotel[3]; + res.reservationFor.address.addressLocality = hotel[4]; + res.reservationFor.address.addressCountry = hotel[5]; + const dates = text.match(/(\d\d\/\d\d\/\d{4}) \(.* (\d\d).*\)\n[\s\S]+\n(\d\d\/\d\d\/\d{4}) \(.* (\d\d).*\)/); + res.checkinTime = JsonLd.toDateTime(dates[1] + ' ' + dates[2], 'dd/MM/yyyy hh', 'de'); + res.checkoutTime = JsonLd.toDateTime(dates[3] + ' ' + dates[4], 'dd/MM/yyyy hh', 'de'); + res.reservationFor.telephone = html.root.eval('//a[starts-with(@href, "tel:")]')[0].content; + res.reservationFor.email = html.root.eval('//a[starts-with(@href, "mailto:")]')[0].content; + res.modifyReservationUrl = html.root.eval('//a[contains(@href, "my-booking")]')[0].attribute('href'); + + let reservations = []; + let idx = 0; + while (true) { + const p = text.substr(idx).match(/(.*)\n.*\n.* x \d+\n.*â¬\n/); + if (!p) + break; + idx += p.index + p[0].length; + let r = JsonLd.clone(res); + r.underName.name = p[1]; + reservations.push(r); + } + + return reservations.length ? reservations : res; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.json new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.json --- old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,13 @@ +{ + "filter": [ + { + "field": "From", + "match": "nore...@email.hotel-bb.com", + "mimeType": "message/rfc822", + "scope": "Ancestors" + } + ], + "function": "parseConfirmation", + "mimeType": "text/html", + "script": "bb-hotel.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bestwestern.js new/kitinerary-23.04.1/src/lib/scripts/bestwestern.js --- old/kitinerary-23.04.0/src/lib/scripts/bestwestern.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/bestwestern.js 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,20 @@ +/* + SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parseConfirmation(html) { + let res = JsonLd.newLodgingReservation(); + const text = html.root.recursiveContent; + const addr = text.match(/Hoteladresse:\n(.*)\n(.*)\n(.*)\n\s+(.*)\n/); + res.reservationFor.name = addr[1]; + res.reservationFor.address.streetAddress = addr[2]; + res.reservationFor.address.postalCode = addr[3]; + res.reservationFor.address.addressLocality = addr[4]; + const dt = text.match(/Anreise\/Abreise: .* (\d\d\.\d\d\.\d{4})\n\s+\/\s*\n.* (\d\d\.\d\d\.\d{4})/); + res.checkinTime = JsonLd.toDateTime(dt[1], "dd.MM.yyyy", "de"); + res.checkoutTime = JsonLd.toDateTime(dt[2], "dd.MM.yyyy", "de"); + res.reservationNumber = text.match(/Buchungsnummer: (.*)\n/)[1]; + res.cancelReservationUrl = html.eval('//a[contains(@href,"showBookingCancelPage")]')[0].attribute('href'); + return res; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bestwestern.json new/kitinerary-23.04.1/src/lib/scripts/bestwestern.json --- old/kitinerary-23.04.0/src/lib/scripts/bestwestern.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/bestwestern.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,13 @@ +{ + "filter": [ + { + "field": "From", + "match": "@bestwestern\\.", + "mimeType": "message/rfc822", + "scope": "Ancestors" + } + ], + "function": "parseConfirmation", + "mimeType": "text/html", + "script": "bestwestern.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/czechrailways.js new/kitinerary-23.04.1/src/lib/scripts/czechrailways.js --- old/kitinerary-23.04.0/src/lib/scripts/czechrailways.js 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/czechrailways.js 2023-05-06 10:59:18.000000000 +0200 @@ -157,17 +157,20 @@ let idx = 0; let reservations = []; while (true) { - // TODO this doesn't consider seat reservations yet, lacking corresponding samples - let leg = text.substr(idx).match(/\n(.*?) +(\d\d\.\d\d\.) +(\d\d:\d\d) +(.*?)\n(.*?) +(\d\d.\d\d.) +(\d\d:\d\d)/); + let leg = text.substr(idx).match(/\n(.*?) +(\d\d\.\d\d\.) +(\d\d:\d\d) +(.*?)(?: +(\d+) +(\d+))?\n(.*?) +(\d\d.\d\d.) +(\d\d:\d\d)/); if (!leg) { break; } let res = JsonLd.newTrainReservation(); res.reservationFor.departureStation.name = leg[1]; res.reservationFor.departureTime = JsonLd.toDateTime(leg[2] + leg[3], 'dd.MM.hh:mm', 'cz'); - res.reservationFor.arrivalStation.name = leg[5]; - res.reservationFor.arrivalTime = JsonLd.toDateTime(leg[6] + leg[7], 'dd.MM.hh:mm', 'cz'); + res.reservationFor.arrivalStation.name = leg[7]; + res.reservationFor.arrivalTime = JsonLd.toDateTime(leg[8] + leg[9], 'dd.MM.hh:mm', 'cz'); res.reservationFor.trainNumber = leg[4]; + if (leg[5] && leg[6]) { + res.reservedTicket.ticketedSeat.seatSection = leg[5]; + res.reservedTicket.ticketedSeat.seatNumber = leg[6]; + } if (triggerNode.result[0]['@type'] == 'TrainReservation') { res = JsonLd.apply(triggerNode.result[0], res); @@ -183,3 +186,27 @@ return reservations; } + +function parseEvent(event) { + let reservations = []; + let idx = 0; + while (true) { + const leg = event.description.substr(idx).match(/(.*)\n(.*) (\d\d):(\d\d)\n(.*) (\d\d):(\d\d)\n.* (\d+),.* (\d+)/); + if (!leg) + break; + idx += leg.index + leg[0].length; + + let res = JsonLd.newTrainReservation(); + res.reservationFor.trainNumber = leg[1]; + res.reservationFor.departureStation.name = leg[2]; + res.reservationFor.departureTime = new Date(event.dtStart); + res.reservationFor.departureTime.setHours(leg[3], leg[4]); + res.reservationFor.arrivalStation.name = leg[5]; + res.reservationFor.arrivalTime = new Date(event.dtStart); + res.reservationFor.arrivalTime.setHours(leg[6], leg[7]); + res.reservedTicket.ticketedSeat.seatSection = leg[8]; + res.reservedTicket.ticketedSeat.seatNumber = leg[9]; + reservations.push(res); + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/czechrailways.json new/kitinerary-23.04.1/src/lib/scripts/czechrailways.json --- old/kitinerary-23.04.0/src/lib/scripts/czechrailways.json 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/czechrailways.json 2023-05-06 10:59:18.000000000 +0200 @@ -18,4 +18,17 @@ "function": "parsePdfTicket", "mimeType": "application/pdf", "script": "czechrailways.js" +}, +{ + "filter": [ + { + "field": "uid", + "match": "@www\\.cd\\.cz$", + "mimeType": "internal/event", + "scope": "Current" + } + ], + "function": "parseEvent", + "mimeType": "internal/event", + "script": "czechrailways.js" }] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.js new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.js --- old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.js 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.js 2023-05-06 10:59:18.000000000 +0200 @@ -367,3 +367,24 @@ return res; } + +function parseDBRegioBusUic(uic, node) +{ + let ticket = node.result[0]; + if (uic.ticketLayout.type != 'PLAI' || ticket.name !== '') + return; + + ticket.name = uic.ticketLayout.firstField.text; + for (let f = uic.ticketLayout.firstField; f && !f.isNull; f = f.next) { + const validFrom = f.text.match(/Erster Gültigkeitstag: (.*)/); + if (validFrom) { + ticket.validFrom = JsonLd.toDateTime(validFrom[1], 'dd.MM.yyyy', 'de'); + } + const name = f.text.match(/Name Fahrtberechtigter: (.*)/); + if (name) { + ticket.underName = JsonLd.newObject('Person'); + ticket.underName.name = name[1]; + } + } + return ticket; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.json new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.json --- old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.json 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.json 2023-05-06 10:59:18.000000000 +0200 @@ -94,5 +94,18 @@ "function": "parseEvent", "mimeType": "internal/event", "script": "deutschebahn.js" + }, + { + "filter": [ + { + "field": "carrierId", + "match": "2080", + "mimeType": "internal/uic9183", + "scope": "Current" + } + ], + "function": "parseDBRegioBusUic", + "mimeType": "internal/uic9183", + "script": "deutschebahn.js" } ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/extractors.qrc new/kitinerary-23.04.1/src/lib/scripts/extractors.qrc --- old/kitinerary-23.04.0/src/lib/scripts/extractors.qrc 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/extractors.qrc 2023-05-06 10:59:18.000000000 +0200 @@ -34,6 +34,10 @@ <file>baeder-suite.js</file> <file>bateliers-arcachon.json</file> <file>bateliers-arcachon.js</file> + <file>bb-hotel.json</file> + <file>bb-hotel.js</file> + <file>bestwestern.json</file> + <file>bestwestern.js</file> <file>blablacar-bus.json</file> <file>blablacar-bus.js</file> <file>booking.json</file> @@ -82,10 +86,14 @@ <file>fjordline.js</file> <file>flixbus.json</file> <file>flixbus.js</file> + <file>gepard-express.json</file> + <file>gepard-express.js</file> <file>gnv.it.json</file> <file>gnv.it.js</file> <file>gomus.json</file> <file>gomus.js</file> + <file>grimaldi-lines.json</file> + <file>grimaldi-lines.js</file> <file>gwr.json</file> <file>gwr.js</file> <file>hertz.js</file> @@ -203,6 +211,8 @@ <file>vr.fi.js</file> <file>vueling.json</file> <file>vueling.js</file> + <file>westbahn.json</file> + <file>westbahn.js</file> <file>whyline.json</file> <file>whyline.js</file> <file>zssk.json</file> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/flixbus.js new/kitinerary-23.04.1/src/lib/scripts/flixbus.js --- old/kitinerary-23.04.0/src/lib/scripts/flixbus.js 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/flixbus.js 2023-05-06 10:59:18.000000000 +0200 @@ -22,8 +22,11 @@ function parseDate(year, baseDate, overrideDate, time) { + baseDate = baseDate.replace('.', ''); + if (overrideDate) + overrideDate = overrideDate.replace('.', ''); const s = (overrideDate ? overrideDate.trim() : baseDate) + ' ' + year + ' ' + time; - return JsonLd.toDateTime(s, 'd MMM yyyy hh:mm', ['en', 'fr', 'pl', 'nl']); + return JsonLd.toDateTime(s, 'd MMM yyyy hh:mm', ['en', 'fr', 'pl', 'nl', 'de']); } function parseLocation(place, addr1, addr2, links) @@ -44,7 +47,7 @@ const text = page.textInRect(0.0, 0.05, 0.5, 0.5); const links = page.linksInRect(0.0, 0.0, 0.5, 0.5); const resNum = triggerNode.content.match(/pdfqr\/(\d+)\//)[1]; - const date = text.match(/^\S+,? (\d+ \S+) (\d{4})\n/); + const date = text.match(/^\S+,? (\d+\.? \S+) (\d{4})\n/); const timeColumn = page.textInRect(0.0, 0.1, 0.125, 0.5); const stationColumn = page.textInRect(0.125, 0.1, 0.5, 0.5); @@ -54,7 +57,7 @@ let reservations = []; while (true) { const times = timeColumn.substr(idxTime).match(/(\d\d:\d\d)\n([^:]*?\n)?([^:]*?\n)?(\d\d:\d\d)/); - const stations = stationColumn.substr(idxStations).match(/(.*)\n[î¤»î¥ ]+(.*)(?:\n|,\n +(.*)\n).*(?:Bus|Autobus) +(.*)\n.*(?:Direction|à destination de|Kierunek|richting) (.*)\n(.*)\n(?:[î¤»î¥ ]+(.*?)(?:\n|,\n +(.*)\n))?/); + const stations = stationColumn.substr(idxStations).match(/(.*)\n[î¤»î¥ î¥¯]+(.*)(?:\n|,\n +(.*)\n).*(?:Bus|Autobus) +(.*)\n.*(?:Direction|à destination de|Kierunek|richting|Richtung) (.*)\n(.*)\n(?:[î¤»î¥ î¥¯]+(.*?)(?:\n|,\n +(.*)\n))?/); if (!times || !stations) { break; } @@ -69,7 +72,7 @@ res.reservationFor.departureBusStop.name = stations[1]; parseLocation(res.reservationFor.departureBusStop, stations[2], stations[3], links); - res.reservationFor.busNumber = stations[4]; + res.reservationFor.busNumber = stations[4].match(/(.*?)(?: |$)/)[1]; res.reservationFor.busName = stations[5]; res.reservationFor.arrivalTime = parseDate(date[2], date[1], times[3] ? times[3] : times[2], times[4]); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/gepard-express.js new/kitinerary-23.04.1/src/lib/scripts/gepard-express.js --- old/kitinerary-23.04.0/src/lib/scripts/gepard-express.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/gepard-express.js 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,28 @@ +/* + SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parsePdf(pdf, node, triggerNode) { + const text = pdf.pages[triggerNode.location].text; + const legs = text.split(/ +(?=.* ->)/).slice(1); + let reservations = []; + for (leg of legs) { + let res = JsonLd.newBusReservation(); + const stops = leg.match(/(.*) -> (.*)/); + res.reservationFor.departureBusStop.name = stops[1]; + res.reservationFor.arrivalBusStop.name = stops[2]; + const times = leg.match(/ (\d{1,2}\.\d{1,2}\.\d{4} \d\d:\d\d)\n[\s\S]+ (\d{1,2}\.\d{1,2}\.\d{4} \d\d:\d\d)\n/); + res.reservationFor.departureTime = JsonLd.toDateTime(times[1], 'dd.M.yyyy hh:mm', 'cz'); + res.reservationFor.arrivalTime = JsonLd.toDateTime(times[2], 'dd.M.yyyy hh:mm', 'cz'); + res.reservationFor.departurePlatform = leg.match(/Platform +(.*)/)[1]; + res.reservationNumber = triggerNode.content.match(/s:4:"code";s:\d+:"(.*?)"/)[1]; + res.underName.name = triggerNode.content.match(/s:8:"fullName";s:\d+:"(.*?)"/)[1]; + res.reservedTicket.ticketToken = 'qrcode:' + triggerNode.content; + reservations.push(res); + const seat = leg.match(/(?:Seat|sedadla:) +(.*?) \(/); + if (seat) + res.reservedTicket.ticketedSeat.seatNumber = seat[1]; + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/gepard-express.json new/kitinerary-23.04.1/src/lib/scripts/gepard-express.json --- old/kitinerary-23.04.0/src/lib/scripts/gepard-express.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/gepard-express.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,12 @@ +{ + "filter": [ + { + "match": "^a:7:{s:", + "mimeType": "text/plain", + "scope": "Descendants" + } + ], + "function": "parsePdf", + "mimeType": "application/pdf", + "script": "gepard-express.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.js new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.js --- old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.js 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,33 @@ +/* + SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parsePdfTicket(pdf, node, triggerNode) { + const text = pdf.pages[triggerNode.location].text; + let res = JsonLd.newBoatReservation(); + const dep = text.match(/Departure: (.*) +(\d{2}\/\d{2}\/\d{2} \d{2}:\d{2})/); + res.reservationFor.departureBoatTerminal.name = dep[1]; + res.reservationFor.departureTime = JsonLd.toDateTime(dep[2], 'dd/MM/yy hh:mm', 'it'); + const arr = text.match(/Arrival: (.*) +(\d{2}\/\d{2}\/\d{2} \d{2}:\d{2})/); + res.reservationFor.arrivalBoatTerminal.name = arr[1]; + res.reservationFor.arrivalTime = JsonLd.toDateTime(arr[2], 'dd/MM/yy hh:mm', 'it'); + res.reservationNumber = triggerNode.content; + res.reservedTicket.ticketToken = 'barcode128:' + triggerNode.content; + + let idx = 0; + let reservations = [res]; + while (true) { + const pas = text.substr(idx).match(/ +(.*?) +[MF] +\d\d-\d\d-\d{4}\n/); + if (!pas) + break; + idx += pas.index + pas[0].length; + let r = JsonLd.clone(res); + const name = pas[1].split(/ +/); + r.underName.familyName = name[0]; + r.underName.givenName = name[1]; + // TODO: per-passenger ticket tokens + reservations.push(r); + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.json new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.json --- old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,12 @@ +{ + "filter": [ + { + "match": "^G\\d{9}$", + "mimeType": "text/plain", + "scope": "Descendants" + } + ], + "function": "parsePdfTicket", + "mimeType": "application/pdf", + "script": "grimaldi-lines.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/sncb.js new/kitinerary-23.04.1/src/lib/scripts/sncb.js --- old/kitinerary-23.04.0/src/lib/scripts/sncb.js 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/sncb.js 2023-05-06 10:59:18.000000000 +0200 @@ -6,14 +6,15 @@ function parsePage(page) { var res = JsonLd.newTrainReservation(); - res.reservationFor.departureStation.name = page.text.match(/De:\s+(.*)\n/)[1]; - res.reservationFor.arrivalStation.name = page.text.match(/A:\s+(.*)\n/)[1]; - var date = page.text.match(/Date du voyage:\s+(\d\d)\/(\d\d)\/(\d{4})\n/); + res.reservationFor.departureStation.name = page.text.match(/(?:De|From):\s+(.*)\n/)[1]; + res.reservationFor.arrivalStation.name = page.text.match(/(?:A|TO):\s+(.*)\n/)[1]; + var date = page.text.match(/(?:Date du voyage|Travel date):\s+(\d\d)\/(\d\d)\/(\d{4})\n/); res.reservationFor.departureDay = date[3] + "-" + date[2] + "-" + date[1]; - res.underName.givenName = page.text.match(/Prénom:\s+(.*)\n/)[1]; - res.underName.familyName = page.text.match(/Nom:\s+(.*)\n/)[1]; - res.reservedTicket.ticketedSeat.seatingType = page.text.match(/Classe:\s+(.*)\n/)[1]; + res.underName.givenName = page.text.match(/(?:Prénom|Firstname):\s+(.*)\n/)[1]; + res.underName.familyName = page.text.match(/(?:Nom|Lastname):\s+(.*)\n/)[1]; + res.reservedTicket.ticketedSeat.seatingType = page.text.match(/Classe?:\s+(.*)\n/)[1]; res.reservedTicket.ticketToken = "barcode128:" + page.text.match(/\s+([A-Z\d-]{15})\n/)[1]; + res.reservedTicket.name = page.text.match(/(?:Type de billet|Ticket type):\s+(.*)\n/)[1]; return res; } @@ -25,3 +26,27 @@ } return reservations; } + +function parseInternationalPdf(pdf, node, triggerNode) { + const page = pdf.pages[triggerNode.location].text; + let reservations = []; + let idx = 0; + while (true) { + const leg = page.substr(idx).match(/(\d\d\/\d\d\/\d{4}) (\d\d:\d\d) (.*?) +-> +(\d\d:\d\d) (.*?) +(.*?) +(.*?) +(.*)\n/); + if (!leg) { + break; + } + idx += leg.index + leg[0].length; + + let res = JsonLd.clone(triggerNode.result[0]); + res.reservationFor.departureTime = JsonLd.toDateTime(leg[1] + ' ' + leg[2], 'dd/MM/yyyy hh:mm', 'nl'); + res.reservationFor.departureStation.name = leg[3]; + res.reservationFor.arrivalTime = JsonLd.toDateTime(leg[1] + ' ' + leg[4], 'dd/MM/yyyy hh:mm', 'nl'); + res.reservationFor.arrivalStation.name = leg[5]; + res.reservationFor.trainNumber = leg[6]; + res.reservedTicket.ticketedSeat.seatSection = leg[7]; + res.reservedTicket.ticketedSeat.seatNumber = leg[8]; + reservations.push(res); + } + return reservations; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/sncb.json new/kitinerary-23.04.1/src/lib/scripts/sncb.json --- old/kitinerary-23.04.0/src/lib/scripts/sncb.json 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/sncb.json 2023-05-06 10:59:18.000000000 +0200 @@ -1,13 +1,33 @@ -{ - "filter": [ - { - "field": "From", - "match": "ticketonl...@b-rail.be", - "mimeType": "message/rfc822", - "scope": "Ancestors" - } - ], - "function": "parsePdf", - "script": "sncb.js", - "mimeType": "application/pdf" -} +[ + { + "filter": [ + { + "field": "From", + "match": "ticketonl...@b-rail.be", + "mimeType": "message/rfc822", + "scope": "Ancestors" + }, + { + "match": "^[A-Z][A-Z0-9]{14}$", + "mimeType": "text/plain", + "scope": "Descendants" + } + ], + "function": "parsePdf", + "script": "sncb.js", + "mimeType": "application/pdf" + }, + { + "filter": [ + { + "field": "carrierId", + "match": "1088", + "mimeType": "internal/uic9183", + "scope": "Descendants" + } + ], + "function": "parseInternationalPdf", + "mimeType": "application/pdf", + "script": "sncb.js" + } +] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/vueling.js new/kitinerary-23.04.1/src/lib/scripts/vueling.js --- old/kitinerary-23.04.0/src/lib/scripts/vueling.js 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/scripts/vueling.js 2023-05-06 10:59:18.000000000 +0200 @@ -49,5 +49,10 @@ const times = topRight.match(/(\d{2}:\d{2}) (?:H|Uhr) +(\d{2}:\d{2}) (?:H|Uhr)/); res.reservationFor.departureTime = JsonLd.toDateTime(times[1], "hh:mm", "en"); res.reservationFor.arrivalTime = JsonLd.toDateTime(times[2], "hh:mm", "en"); + const topLeft = page.textInRect(0.0, 0.0, 0.5, 1.0); + const grp = topLeft.match(/(?:Group|Grupo|Gruppe) +(\d)/i); + if (grp) { + res.boardingGroup = grp[1]; + } return res; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/westbahn.js new/kitinerary-23.04.1/src/lib/scripts/westbahn.js --- old/kitinerary-23.04.0/src/lib/scripts/westbahn.js 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/westbahn.js 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,32 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org> + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +function parseTicket(pdf, node, triggerNode) { + const page = pdf.pages[triggerNode.location]; + const leftCol = page.textInRect(0.0, 0.0, 0.5, 1.0); + const rightCol = page.textInRect(0.5, 0.0, 1.0, 1.0); + let res = JsonLd.newTrainReservation(); + res.reservedTicket.name = rightCol.match(/TICKET\n(.*)/)[1]; + res.reservedTicket.ticketToken = 'qrcode:' + triggerNode.content; + res.reservedTicket.ticketNumber = rightCol.match(/TICKET NUMMER\n(.*)/)[1]; + res.reservationNumber = rightCol.match(/TICKET CODE\n(.*)/)[1]; + const leg = rightCol.match(/VERBINDUNG\n(.*) . (.*)/); + res.reservationFor.departureStation.name = leg[1]; + res.reservationFor.arrivalStation.name = leg[2]; + const date = rightCol.match(/GÃLTIGKEIT\n.*(\d\d\.\d\d.\d{4})/)[1]; + res.reservationFor.departureDay = JsonLd.toDateTime(date, 'dd.MM.yyyy', 'de'); + const seat = rightCol.match(/Wagen (\d+) .* Sitz (.*)/); + if (seat) { + res.reservedTicket.ticketedSeat.seatSection = seat[1]; + res.reservedTicket.ticketedSeat.seatNumber = seat[2]; + } + const train = leftCol.match(/(\d\d:\d\d) +(\d\d:\d\d)\n.*\n.* +(\S.*?) +/); + if (train) { + res.reservationFor.trainNumber = train[3]; + res.reservationFor.departureTime = JsonLd.toDateTime(date + ' ' + train[1], 'dd.MM.yyyy hh:mm', 'de'); + res.reservationFor.arrivalTime = JsonLd.toDateTime(date + ' ' + train[2], 'dd.MM.yyyy hh:mm', 'de'); + } + return res; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/westbahn.json new/kitinerary-23.04.1/src/lib/scripts/westbahn.json --- old/kitinerary-23.04.0/src/lib/scripts/westbahn.json 1970-01-01 01:00:00.000000000 +0100 +++ new/kitinerary-23.04.1/src/lib/scripts/westbahn.json 2023-05-06 10:59:18.000000000 +0200 @@ -0,0 +1,12 @@ +{ + "filter": [ + { + "match": "^https://westbahn.at/t/", + "mimeType": "text/plain", + "scope": "Descendants" + } + ], + "function": "parseTicket", + "mimeType": "application/pdf", + "script": "westbahn.js" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/sortutil.cpp new/kitinerary-23.04.1/src/lib/sortutil.cpp --- old/kitinerary-23.04.0/src/lib/sortutil.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/sortutil.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -151,6 +151,13 @@ bool SortUtil::isBefore(const QVariant &lhs, const QVariant &rhs) { if (startDateTime(lhs) == startDateTime(rhs) && lhs.userType() == rhs.userType() && JsonLd::canConvert<Reservation>(lhs)) { + // sort by end date if available next, e.g. for overlapping hotel bookings + const auto lhsEndDt = endDateTime(lhs); + const auto rhsEndDt = endDateTime(rhs); + if (JsonLd::isA<LodgingReservation>(lhs) && lhsEndDt.isValid() && rhsEndDt.isValid() && lhsEndDt != rhsEndDt) { + return lhsEndDt < rhsEndDt; + } + // for multi-traveler reservations, sort by traveler name to achieve a stable result const auto lhsRes = JsonLd::convert<Reservation>(lhs); const auto rhsRes = JsonLd::convert<Reservation>(rhs); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/text/nameoptimizer.cpp new/kitinerary-23.04.1/src/lib/text/nameoptimizer.cpp --- old/kitinerary-23.04.0/src/lib/text/nameoptimizer.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/text/nameoptimizer.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -57,6 +57,37 @@ return std::any_of(std::begin(name_prefixes), std::end(name_prefixes), [s](const char *prefix) { return s == QLatin1String(prefix); }); } +static QStringView stripNamePrefix(QStringView s) +{ + for (auto prefix : name_prefixes) { + QLatin1String p(prefix); + if (s.endsWith(p) && s.size() > p.size() && s[s.size() - p.size() - 1] == QLatin1Char(' ')) { + return s.left(s.size() - p.size() - 1); + } + } + + return s; +} + +static bool isSameChar(QChar c1, QChar c2) +{ + if (c1 == c2) { + return true; + } + + if (c1.decompositionTag() != c2.decompositionTag()) { + if (c1.decompositionTag() == QChar::Canonical) { + c1 = c1.decomposition().at(0); + } + if (c2.decompositionTag() == QChar::Canonical) { + c2 = c2.decomposition().at(0); + } + return c1 == c2; + } + + return false; +} + QString NameOptimizer::optimizeNameString(const QString &text, const QString &name) { if (name.size() < 2) { @@ -65,41 +96,33 @@ for (int i = 0; i < text.size(); ++i) { bool mismatch = false; - auto nameLen = name.size(); - for (int j = 0; j < name.size(); ++j) { + int nameLen = 0; + for (int j = 0; j < name.size(); ++j, ++nameLen) { // reached the end of text - if (i + j >= text.size()) { + if (i + nameLen >= text.size()) { // remainder is either a prefix placed as suffix (see below), or we are unsuccessful - if (isNamePrefix(QStringView(name).mid(j))) { - nameLen = j; - } else { - return name; + if (!isNamePrefix(QStringView(name).mid(j))) { + mismatch = true; } break; } - auto c1 = text.at(i+j).toCaseFolded(); + auto c1 = text.at(i+nameLen).toCaseFolded(); auto c2 = name.at(j).toCaseFolded(); - if (c1 == c2) { + if (isSameChar(c1, c2)) { continue; } - if (c1.decompositionTag() != c2.decompositionTag()) { - if (c1.decompositionTag() == QChar::Canonical) { - c1 = c1.decomposition().at(0); - } - if (c2.decompositionTag() == QChar::Canonical) { - c2 = c2.decomposition().at(0); - } - if (c1 == c2) { - continue; - } + // expand spaces missing in name + if (nameLen > 0 && c1 == QLatin1Char(' ') && (i + nameLen + 1) < text.size() && isSameChar(text.at(i+nameLen+1).toCaseFolded(), c2)) { + ++nameLen; + continue; } // mismatch: check if the remainder is a name prefix (yes, those also occur frequently as suffixes of name parts in IATA BCBP for example) if (isNamePrefix(QStringView(name).mid(j))) { - nameLen = QStringView(name).left(j).trimmed().size(); + nameLen = QStringView(name).left(nameLen).trimmed().size(); break; } @@ -118,8 +141,9 @@ continue; } - if (StringUtil::betterString(QStringView(text).mid(i, nameLen), name) != name) { - return text.mid(i, nameLen); + const auto betterName = QStringView(text).mid(i, nameLen).trimmed(); + if (StringUtil::betterString(betterName, name) != name) { + return stripNamePrefix(betterName).toString(); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/text/terminalfinder.cpp new/kitinerary-23.04.1/src/lib/text/terminalfinder.cpp --- old/kitinerary-23.04.0/src/lib/text/terminalfinder.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/text/terminalfinder.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -17,7 +17,7 @@ { " ?\\((?: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 }, { " ?\\(T(?<name>\\d[A-Z]?)\\)", QRegularExpression::NoPatternOption }, }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/uic9183/rct2ticket.cpp new/kitinerary-23.04.1/src/lib/uic9183/rct2ticket.cpp --- old/kitinerary-23.04.0/src/lib/uic9183/rct2ticket.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/uic9183/rct2ticket.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -72,7 +72,8 @@ } 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, ]+)" + "ZUG +(?P<train_number>\\d+) +(?P<train_category>[A-Z][A-Z0-9]+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)", + "ZUG +(?P<train_number>\\d+) +WAGEN +(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)", }; QString Rct2TicketPrivate::reservationPatternCapture(QStringView name) const diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/uic9183/uic9183parser.cpp new/kitinerary-23.04.1/src/lib/uic9183/uic9183parser.cpp --- old/kitinerary-23.04.0/src/lib/uic9183/uic9183parser.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/uic9183/uic9183parser.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -158,8 +158,8 @@ if (issuerId == 80 && key.size() == 13 && key.endsWith(QLatin1String("0101"))) { return key.left(9); // DB domestic part of an international order } - if (issuerId == 1184 && key.size() == 9 && key.at(7) == QLatin1Char('_') && key.at(8).isDigit()) { - return key.left(7); // NS + if ((issuerId == 1088 || issuerId == 1184) && key.size() == 9 && key.at(7) == QLatin1Char('_') && key.at(8).isDigit()) { + return key.left(7); // SNCB and NS } return key; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/uic9183/uic9183ticketlayout.h new/kitinerary-23.04.1/src/lib/uic9183/uic9183ticketlayout.h --- old/kitinerary-23.04.0/src/lib/uic9183/uic9183ticketlayout.h 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/uic9183/uic9183ticketlayout.h 2023-05-06 10:59:18.000000000 +0200 @@ -36,6 +36,8 @@ UIC_NUM_PROPERTY(size, 9 + m_offset, 4) UIC_STR_PROPERTY(text, 13 + m_offset, size()) + Q_PROPERTY(bool isNull READ isNull STORED false) + Q_PROPERTY(KItinerary::Uic9183TicketLayoutField next READ next STORED false) public: Uic9183TicketLayoutField(); /** Create a new U_TLAY field starting at @p offset in @p block. @@ -64,7 +66,7 @@ /** Ticket type (e.g. RCT2). */ Q_PROPERTY(QString type READ type) Q_PROPERTY(QSize size READ size) - + Q_PROPERTY(KItinerary::Uic9183TicketLayoutField firstField READ firstField STORED false) public: Uic9183TicketLayout(); /** Parse U_TLAY ticket layout block in @p data. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/vdv/vdvticket.cpp new/kitinerary-23.04.1/src/lib/vdv/vdvticket.cpp --- old/kitinerary-23.04.0/src/lib/vdv/vdvticket.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/lib/vdv/vdvticket.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -78,8 +78,14 @@ offset += prodTransactionBlock.size(); offset += sizeof(VdvTicketIssueData); - // 0 padding to reach at least 111 bytes - offset += std::max(111 - offset - (int)sizeof(VdvTicketTrailer), 0); + // 0 padding to reach the minimum size + // 111 in the specs known to us, but seems to vary in practice, so be flexible and + // just align with the input end + if (data.size() < offset + (int)sizeof(VdvTicketTrailer)) { + qCWarning(Log) << "Ticket data too small for VDV ticket trailer" << offset; + return; + } + offset = data.size() - (int)sizeof(VdvTicketTrailer); const auto trailer = reinterpret_cast<const VdvTicketTrailer*>(data.constData() + offset); if (memcmp(trailer->identifier, "VDV", 3) != 0) { @@ -216,10 +222,8 @@ const VdvTicketTrailer* VdvTicket::trailer() const { - auto offset = sizeof(VdvTicketHeader) + productData().size() - + sizeof(VdvTicketCommonTransactionData) + productSpecificTransactionData().size() - + sizeof(VdvTicketIssueData); - offset += std::max<int>(111 - offset - sizeof(VdvTicketTrailer), 0); // padding to 111 bytes + // see above + const auto offset = d->m_data.size() - (int)sizeof(VdvTicketTrailer); return d->m_data.isEmpty() ? nullptr : reinterpret_cast<const VdvTicketTrailer*>(d->m_data.constData() + offset); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitinerary-23.04.0/src/tools/ticket-barcode-dump.cpp new/kitinerary-23.04.1/src/tools/ticket-barcode-dump.cpp --- old/kitinerary-23.04.0/src/tools/ticket-barcode-dump.cpp 2023-04-13 17:08:26.000000000 +0200 +++ new/kitinerary-23.04.1/src/tools/ticket-barcode-dump.cpp 2023-05-06 10:59:18.000000000 +0200 @@ -95,10 +95,19 @@ std::cout << prop.name() << ": " << qPrintable(value.toString()) << std::endl; } - if (ticket.ticketTypeCode() == SSBv3Ticket::IRT_RES_BOA) { - std::cout << std::endl; - std::cout << "Issuing day: " << qPrintable(ticket.issueDate().toString(Qt::ISODate)) << std::endl; - std::cout << "Departure day: " << qPrintable(ticket.type1DepartureDay().toString(Qt::ISODate)) << std::endl; + std::cout << std::endl; + std::cout << "Issuing day: " << qPrintable(ticket.issueDate().toString(Qt::ISODate)) << std::endl; + switch (ticket.ticketTypeCode()) { + case SSBv3Ticket::IRT_RES_BOA: + std::cout << "Departure day: " << qPrintable(ticket.type1DepartureDay().toString(Qt::ISODate)) << std::endl; + break; + case SSBv3Ticket::NRT: + std::cout << "Valid from: " << qPrintable(ticket.type2ValidFrom().toString(Qt::ISODate)) << std::endl; + std::cout << "Valid until: " << qPrintable(ticket.type2ValidUntil().toString(Qt::ISODate)) << std::endl; + break; + case SSBv3Ticket::GRT: + case SSBv3Ticket::RPT: + break; } }