diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-no-seat-single.json b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-no-seat-single.json new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-no-seat-single.json @@ -0,0 +1,3 @@ +[ +] + diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-no-seat-single.txt b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-no-seat-single.txt new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-no-seat-single.txt @@ -0,0 +1,36 @@ +! 1154 + JÍZDENKA + eTiket Osob: 1 + Z/DE/VON @ DO/A/NACH TŘÍDA/ + CL./KL. + 01/01 00:00 Lysá n.Labem @ Praha Masarykovo n. 02/01 06:00 2 + * * * @ * * * * + Přes: Čelákovice Km: 35 + IN 25 jednosměrná + Cena 4 Kč +Doklad je nepřenosný a platí vždy pouze ve spojení s osobním průkazem cestujícího, jehož jméno je uvedeno na jízdence. +Jízdu je nutno nastoupit v 1. den platnosti +Daňový doklad (informace o ceně) +Summary / Rechnung +Položka Tar. cena Body Cena DPH +Jízdenka 1 43 Kč 4 Kč 15% +Uplatněné věrnostní body -200 +Celkem 4 Kč +Datum vystavení/datum platby: 1.1.2017 16:44 Platba: KARTOU/CC +Prodejce: České dráhy, a.s. 30002 Číslo obj.: 13580 282 00 +Praha 1, Nábřeží L.Svobody 1222, PSČ 110 15 DIČ: CZ70994226 +Jízdní řád +Timetable / Fahrplan +Stanice Odj. / Příj. x w Místo / Seat / Sitzplatz +Lysá n.Labem 01.01. 16:55 Os 9424 Údaje pro kontrolu +Praha Masarykovo n. 01.01. 17:33 Angaben für Kontrolle + Doklad číslo / Beleg Nr.: +Jízdenku lze použít po stejné trase i pro jiné vlakové spojení v tentýž den platnosti. *2606-208 +Rezervace místa platí pouze pro uvedené vlaky. Jméno / Name: + VRÁTIL Daniel + Kód transakce: + VM5MZ3 + Kód nepřehýbejte! / Do not fold the barcode! + Barcode nicht knicken! +Reklama +Šťastnou cestu vlakem přejí České dráhy, Váš osobní dopravce. Strana 1/1 diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-return.json b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-return.json new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-return.json @@ -0,0 +1,52 @@ +[ + { + "@type": "TrainReservation", + "reservationFor": { + "@type": "TrainTrip", + "arrivalStation": { + "@type": "TrainStation", + "name": "Brno hl.n." + }, + "arrivalTime": "2017-12-31T16:20:00", + "departureStation": { + "@type": "TrainStation", + "name": "Praha hl.n." + }, + "departureTime": "2017-12-31T13:51:00", + "trainNumber": "EC 173" + }, + "reservedTicket": { + "@type": "Ticket", + "ticketedSeat": { + "@type": "Seat", + "seatNumber": "71", + "seatSection": "262" + } + } + }, + { + "@type": "TrainReservation", + "reservationFor": { + "@type": "TrainTrip", + "arrivalStation": { + "@type": "TrainStation", + "name": "Praha hl.n." + }, + "arrivalTime": "2018-01-01T13:07:00", + "departureStation": { + "@type": "TrainStation", + "name": "Brno hl.n." + }, + "departureTime": "2018-01-01T10:38:00", + "trainNumber": "rj 72" + }, + "reservedTicket": { + "@type": "Ticket", + "ticketedSeat": { + "@type": "Seat", + "seatNumber": "51", + "seatSection": "27" + } + } + } +] diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-return.txt b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-return.txt new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-return.txt @@ -0,0 +1,41 @@ +! 1154 + JÍZDENKA A MÍSTENKA + eTiket Osob: 1 + Z/DE/VON @ DO/A/NACH TŘÍDA/ + CL./KL. + 31/12 13:51 Praha hl.n. @ Brno hl.n. * * 1 + * * Brno hl.n. @ Praha hl.n. 01/01 24:00 1 + Přes: PhaLb,Kolín,Pard.Hl,Ústí/Or,ČTřeb Km: 255 + Rezervace (místo) + IN 25 zpáteční + Cena 662 Kč +Doklad je nepřenosný a platí vždy pouze ve spojení s osobním průkazem cestujícího, jehož jméno je uvedeno na jízdence. +Jízdu je nutno nastoupit v 1. den platnosti, nejdříve však v 13:51 hod. +Daňový doklad (informace o ceně) +Summary / Rechnung +Položka Tar. cena Body Cena DPH +Jízdenka 1 662 Kč 66 662 Kč 15% +Rezervace 2 0 Kč 0 Kč +Celkem 662 Kč +Datum vystavení/datum platby: 30.12.2017 20:27 Platba: KARTOU/CC +Prodejce: České dráhy, a.s. 30002 Číslo obj.: 20361 103 00 +Praha 1, Nábřeží L.Svobody 1222, PSČ 110 15 DIČ: CZ70994226 +Jízdní řád a rezervace pro cestu TAM +Timetable and Reservations – Outward Journey / Fahrplan und Reservierung Hinfahrt +Stanice Odj. / Příj. x w Místo / Seat / Sitzplatz Údaje pro kontrolu +Praha hl.n. 31.12. 13:51 EC 173 262 71 Angaben für Kontrolle +Brno hl.n. 31.12. 16:20 Doklad číslo / Beleg Nr.: + *8576-222 + Jméno / Name: +Jízdní řád a rezervace pro cestu ZPĚT +Timetable and Reservations – Return Journey / Fahrplan und Reservierung Rückfahrt VRÁTIL Daniel +Stanice Odj. / Příj. x w Místo / Seat / Sitzplatz +Brno hl.n. 01.01. 10:38 rj 72 27 51 Kód transakce: +Praha hl.n. 01.01. 13:07 + GIG2L1 +Jízdenku lze použít po stejné trase i pro pozdější vlakové spojení v době její platnosti. +Rezervace místa platí pouze pro uvedené vlaky. Kód nepřehýbejte! / Do not fold the barcode! + Barcode nicht knicken! +Reklama +Šťastnou cestu vlakem přejí České dráhy, Váš osobní dopravce. Strana 1/1 + diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-single.json b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-single.json new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-single.json @@ -0,0 +1,28 @@ +[ + { + "@type": "TrainReservation", + "reservationFor": { + "@type": "TrainTrip", + "arrivalStation": { + "@type": "TrainStation", + "name": "Praha hl.n." + }, + "arrivalTime": "2017-07-09T17:06:00", + "departureStation": { + "@type": "TrainStation", + "name": "Brno dolní nádraží" + }, + "departureTime": "2017-07-09T14:34:00", + "trainNumber": "RJ 76" + }, + "reservationNumber": "FXI305", + "reservedTicket": { + "@type": "Ticket", + "ticketedSeat": { + "@type": "Seat", + "seatNumber": "91", + "seatSection": "26" + } + } + } +] diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-single.txt b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-single.txt new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_one-leg-single.txt @@ -0,0 +1,37 @@ +! 1154 + JÍZDENKA A MÍSTENKA + eTiket Osob: 1 + Z/DE/VON @ DO/A/NACH TŘÍDA/ + CL./KL. + 09/07 00:00 Brno hl.n./dolní n. @ Praha hl.n. 10/07 24:00 1 + * * * @ * * * * + Přes: ČTřeb,Ústí/Or,Pard.Hl,Kolín Km: 255 + Rezervace (místo) + IN 25 jednosměrná + Cena 341 Kč +Doklad je nepřenosný a platí vždy pouze ve spojení s osobním průkazem cestujícího, jehož jméno je uvedeno na jízdence. +Jízdu je nutno nastoupit v 1. den platnosti +Daňový doklad (informace o ceně) +Summary / Rechnung +Položka Tar. cena Body Cena DPH +Jízdenka 1 341 Kč 34 341 Kč 15% +Rezervace 1 0 Kč 0 Kč +Celkem 341 Kč +Datum vystavení/datum platby: 9.7.2017 14:14 Platba: KARTOU/CC +Prodejce: České dráhy, a.s. 30002 Číslo obj.: 16502 055 00 +Praha 1, Nábřeží L.Svobody 1222, PSČ 110 15 DIČ: CZ70994226 +Jízdní řád a rezervace +Timetable and Reservations / Fahrplan und Reservierung +Stanice Odj. / Příj. x w Místo / Seat / Sitzplatz +Brno dolní nádraží 09.07. 14:34 RJ 76 26 91 Údaje pro kontrolu +Praha hl.n. 09.07. 17:06 Angaben für Kontrolle + Doklad číslo / Beleg Nr.: +Jízdenku lze použít po stejné trase i pro jiné vlakové spojení v tentýž den platnosti. *9740-315 +Rezervace místa platí pouze pro uvedené vlaky. Jméno / Name: + VRÁTIL Daniel + Kód transakce: + FXI305 + Kód nepřehýbejte! / Do not fold the barcode! + Barcode nicht knicken! +Reklama +Šťastnou cestu vlakem přejí České dráhy, Váš osobní dopravce. Strana 1/1 diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_two-leg-single.json b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_two-leg-single.json new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_two-leg-single.json @@ -0,0 +1,46 @@ +[ + { + "@type": "TrainReservation", + "reservationFor": { + "@type": "TrainTrip", + "arrivalStation": { + "@type": "TrainStation", + "name": "Hradec Králové hl.n." + }, + "arrivalTime": "2017-08-09T10:51:00", + "departureStation": { + "@type": "TrainStation", + "name": "Praha hl.n." + }, + "departureTime": "2017-08-09T09:07:00", + "trainNumber": "R 945" + }, + "reservationNumber": "Z39230", + "reservedTicket": { + "@type": "Ticket", + "ticketedSeat": { + "@type": "Seat", + "seatNumber": "115", + "seatSection": "372" + } + } + }, + { + "@type": "TrainReservation", + "reservationFor": { + "@type": "TrainTrip", + "arrivalStation": { + "@type": "TrainStation", + "name": "Jaroměř" + }, + "arrivalTime": "2017-08-09T11:16:00", + "departureStation": { + "@type": "TrainStation", + "name": "Hradec Králové hl.n." + }, + "departureTime": "2017-08-09T11:02:00", + "trainNumber": "Sp 1786" + }, + "reservationNumber": "Z39230" + } +] diff --git a/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_two-leg-single.txt b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_two-leg-single.txt new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/autotests/unstructureddata/czechrailways_two-leg-single.txt @@ -0,0 +1,39 @@ +! 1154 + JÍZDENKA A MÍSTENKA + eTiket Osob: 1 + Z/DE/VON @ DO/A/NACH TŘÍDA/ + CL./KL. + 09/08 00:00 Praha hl.n. @ Jaroměř 10/08 24:00 2 + * * * @ * * * * + Přes: Lysá/L,NymbH,Chlum/Ci,HradKrHl Km: 133 + Rezervace (místo) + IN 25 jednosměrná + Cena 141 Kč +Doklad je nepřenosný a platí vždy pouze ve spojení s osobním průkazem cestujícího, jehož jméno je uvedeno na jízdence. +Jízdu je nutno nastoupit v 1. den platnosti +Daňový doklad (informace o ceně) +Summary / Rechnung +Položka Tar. cena Body Cena DPH +Jízdenka 1 141 Kč 14 141 Kč 15% +Rezervace 1 0 Kč 0 Kč +Celkem 141 Kč +Datum vystavení/datum platby: 7.8.2017 13:18 Platba: KARTOU/CC +Prodejce: České dráhy, a.s. 30000 Číslo obj.: 16996 165 00 +Praha 1, Nábřeží L.Svobody 1222, PSČ 110 15 DIČ: CZ70994226 +Jízdní řád a rezervace +Timetable and Reservations / Fahrplan und Reservierung +Stanice Odj. / Příj. x w Místo / Seat / Sitzplatz +Praha hl.n. 09.08. 09:07 R 945 372 115 Údaje pro kontrolu +Hradec Králové hl.n. 09.08. 10:51 Angaben für Kontrolle + Doklad číslo / Beleg Nr.: +Hradec Králové hl.n. 09.08. 11:02 Sp 1786 *1011-383 +Jaroměř 09.08. 11:16 Jméno / Name: + VRÁTIL Daniel +Jízdenku lze použít po stejné trase i pro jiné vlakové spojení v tentýž den platnosti. +Rezervace místa platí pouze pro uvedené vlaky. + Kód transakce: + Z39230 + Kód nepřehýbejte! / Do not fold the barcode! + Barcode nicht knicken! +Reklama +Šťastnou cestu vlakem přejí České dráhy, Váš osobní dopravce. Strana 1/1 diff --git a/plugins/messageviewer/bodypartformatter/semantic/extractors/czechrailways.js b/plugins/messageviewer/bodypartformatter/semantic/extractors/czechrailways.js new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/semantic/extractors/czechrailways.js @@ -0,0 +1,156 @@ +/* + Copyright (c) 2017 Volker Krause + Copyright (c) 2018 Daniel Vrátil + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +function isHeaderOrFooter(line) { + return line.search(/(Jízdní řád( a rezervace)?|Jízdenku lze použít po stejné trase|Jízdní doklad zakoupený u obchodníka)/) >= 0; +} + +function createSeat(res) +{ + if (!res.reservedTicket) + res.reservedTicket = JsonLd.newObject("Ticket"); + if (!res.reservedTicket.ticketedSeat) + res.reservedTicket.ticketedSeat = JsonLd.newObject("Seat"); +} + +function parseSeat(res, text) { + var coach = text.match(/(\s+)(\d+)/); + var idx = 0; + if (coach) { + createSeat(res); + res.reservedTicket.ticketedSeat.seatSection = coach[2]; + idx = coach.index + coach[1].length + coach[2].length; + } + var seat = text.substr(idx).match(/\s+(\d+)/); + if (seat) { + createSeat(res); + res.reservedTicket.ticketedSeat.seatNumber = seat[1]; + } +} + +// There's no validity year anywhere in the ticket, so we take the purchase date and +// if the trip month and day are after the purchase month and day we assume the +// the ticket will become valid the same year it was purchased, otherwise we assume +// the ticket is for next year. +// This fails when you buy the ticket more than a year ahead of the trip, but I doubt +// you can even do that with Czech Railways... +function detectYear(tripDate, purchaseDate) +{ + var tripDay = tripDate[1]; + var tripMonth = tripDate[2]; + var purchaseDay = purchaseDate[1]; + var purchaseMonth = purchaseDate[2]; + var purchaseYear = purchaseDate[3]; + + if ((purchaseMonth < tripMonth) || + (purchaseMonth == tripMonth) && (purchaseDay <= tripDay)) { + return purchaseYear; + } else { + return parseInt(purchaseYear) + 1; + } +} + +function parseDeparture(res, line, purchaseDate) { + res.reservationFor.departureStation = JsonLd.newObject("TrainStation"); + var station = line.match(/^(.+?) /); + if (!station) + return; + var idx = station.index + station[0].length; + res.reservationFor.departureStation.name = station[1]; + var dt = line.substr(idx).match(/([0-9]{2})\.([0-9]{2})\. ([0-9]{2}:[0-9]{2})/); + if (dt) { + idx += dt.index + dt[0].length; + res.reservationFor.departureTime = JsonLd.toDateTime(dt[1] + ' ' + dt[2] + ' ' + detectYear(dt, purchaseDate) + ' ' + dt[3], "dd MM yyyy hh:mm", "cs"); + } + var trainId = line.substr(idx).match(/([a-zA-Z]+ [0-9a-zA-Z]+)/); + if (trainId) { + idx += trainId.index + trainId[0].length + res.reservationFor.trainNumber = trainId[1]; + } + parseSeat(res, line.substr(idx)); +} + +function parseArrival(res, line, purchaseDate) { + res.reservationFor.arrivalStation = JsonLd.newObject("TrainStation"); + var station = line.match(/^(.+?) /); + if (!station) + return; + var idx = station.index + station[0].length; + res.reservationFor.arrivalStation.name = station[1]; + var dt = line.substr(idx).match(/([0-9]{2})\.([0-9]{2})\. ([0-9]{2}:[0-9]{2})/); + if (dt) { + idx += dt.index + dt[0].length; + res.reservationFor.arrivalTime = JsonLd.toDateTime(dt[1] + ' ' + dt[2] + ' ' + detectYear(dt, purchaseDate) + ' ' + dt[3], "dd MM yyyy hh:mm", "cs"); + } +} + +function parseLegs(text, purchaseDate) { + var reservations = new Array(); + var lines = text.split('\n'); + var depIdx = 1, arrIdx = 2; + while (depIdx < lines.length) { + // stop when reaching the footer or the next itinerary header + if (isHeaderOrFooter(lines[depIdx])) + return reservations; + + var res = JsonLd.newObject("TrainReservation"); + res.reservationFor = JsonLd.newObject("TrainTrip"); + + arrIdx = depIdx + 1; + parseDeparture(res, lines[depIdx], purchaseDate); + parseArrival(res, lines[arrIdx], purchaseDate); + depIdx = arrIdx + 1; + // Find the next leg + while (lines[depIdx].startsWith(" ")) { + depIdx += 1; + } + + reservations.push(res); + } + + return reservations; +} + +function main(text) { + console.log(text); + var reservations = new Array(); + var pos = 0; + + var purchaseDate = text.match(/[d|D]atum platby|UZP: ([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{4})/) + + while (true) { + // find itinerary headers + var header = text.substr(pos).match(/Timetable( and Reservations)?/); + if (!header) + break; + var idx = header.index + header[0].length; + var timetableHeader = text.substr(pos + idx).match(/(Místo \/ Seat \/ Sitzplatz)/) + idx = idx + timetableHeader.index + timetableHeader[0].length; + reservations = reservations.concat(parseLegs(text.substr(pos + idx), purchaseDate)); + if (idx == 0) + break; + pos += idx + 1; + } + + var bookingRef = text.match(/Kód transakce:\s*([A-Z0-9]{6})\n/); + for (var i = 0; bookingRef && i < reservations.length; ++i) + reservations[i].reservationNumber = bookingRef[1]; + return reservations; +} diff --git a/plugins/messageviewer/bodypartformatter/semantic/extractors/czechrailways.json b/plugins/messageviewer/bodypartformatter/semantic/extractors/czechrailways.json new file mode 100644 --- /dev/null +++ b/plugins/messageviewer/bodypartformatter/semantic/extractors/czechrailways.json @@ -0,0 +1,7 @@ +{ + "filter": [ + { "header": "From", "match": "info@cd.cz" }, + { "header": "From", "match": "eshop@cd.cz" } + ], + "script": "czechrailways.js" +} diff --git a/plugins/messageviewer/bodypartformatter/semantic/extractors/extractors.qrc b/plugins/messageviewer/bodypartformatter/semantic/extractors/extractors.qrc --- a/plugins/messageviewer/bodypartformatter/semantic/extractors/extractors.qrc +++ b/plugins/messageviewer/bodypartformatter/semantic/extractors/extractors.qrc @@ -4,6 +4,8 @@ amadeus.js brusselsairlines.json brusselsairlines.js + czechrailways.json + czechrailways.js deutschebahn.json deutschebahn.js eurowings.json