From 0775a27d1d2e33bc4b537402ce070cdaaf79d7d9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 1 Jun 2018 11:12:54 +0200 Subject: [PATCH 01/10] WIP parse remarks --- parse/remark.js | 85 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/parse/remark.js b/parse/remark.js index 0b8c7a52..46ce80ff 100644 --- a/parse/remark.js +++ b/parse/remark.js @@ -1,8 +1,91 @@ 'use strict' +const hints = Object.assign(Object.create(null), { + fb: { + type: 'hint', + code: 'bicycle-con', + text: 'bicycles conveyed' + }, + k2: { + type: 'hint', + code: '2nd-class-only', + text: '2. class only' + }, + eh: { + type: 'hint', + code: 'boarding-ramp', + text: 'vehicle-mounted boarding ramp available' + }, + wv: { + type: 'hint', + code: 'wifi', + text: 'WiFi available' + }, + sn: { + type: 'hint', + code: 'snacks', + text: 'snacks available for purchase' + }, + bf: { + type: 'hint', + code: 'barrier-free', + text: 'barrier-free' + }, + bt: { + type: 'hint', + code: 'on-board-bistro', + text: 'Bordbistro available' + }, + br: { + type: 'hint', + code: 'on-board-restaurant', + text: 'Bordrestaurant available' + }, + ki: { + type: 'hint', + code: 'childrens-area', + text: `children's area available` + }, + ls: { + type: 'hint', + code: 'power-sockets', + text: 'power sockets available' + }, + ev: { + type: 'hint', + code: 'replacement-service', + text: 'replacement service' + }, + kl: { + type: 'hint', + code: 'air-conditioned', + text: 'air-conditioned vehicle' + } +}) + // todo: is passing in profile necessary? const parseRemark = (profile, r) => { - return null // todo + // todo: U "stop cancelled"? + // todo: P "journey cancelled", `r.sIdx`? + // todo: C + if (t.type === 'L') { + return { + type: 'status', + code: 'alternative-trip', + text: r.txtN, + journeyId: r.jid + } + } + if (t.type === 'A') return hints[r.code && r.code.trim()] || null + if (t.type === 'R') { + // todo: how can we identify the individual types? + return { + type: 'status', + code: r.code, + text: r.txtN || '' + } + } + return null } module.exports = parseRemark From f3609ebd800e3e9a99ed1bd1e0c717ab48bf363c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 15:41:00 +0200 Subject: [PATCH 02/10] WIP parse remarks --- parse/remark.js | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/parse/remark.js b/parse/remark.js index 46ce80ff..c0ebdd8f 100644 --- a/parse/remark.js +++ b/parse/remark.js @@ -3,9 +3,14 @@ const hints = Object.assign(Object.create(null), { fb: { type: 'hint', - code: 'bicycle-con', + code: 'bicycle-conveyance', text: 'bicycles conveyed' }, + nf: { + type: 'hint', + code: 'no-bicycle-conveyance', + text: 'bicycles not conveyed' + }, k2: { type: 'hint', code: '2nd-class-only', @@ -31,6 +36,11 @@ const hints = Object.assign(Object.create(null), { code: 'barrier-free', text: 'barrier-free' }, + rg: { + type: 'hint', + code: 'barrier-free-vehicle', + text: 'barrier-free vehicle' + }, bt: { type: 'hint', code: 'on-board-bistro', @@ -66,9 +76,18 @@ const hints = Object.assign(Object.create(null), { // todo: is passing in profile necessary? const parseRemark = (profile, r) => { // todo: U "stop cancelled"? - // todo: P "journey cancelled", `r.sIdx`? // todo: C - if (t.type === 'L') { + + // todo: find sth more reliable than this + if (r.type === 'P' && r.txtN.toLowerCase().trim() === 'journey cancelled') { + return { + type: 'status', + code: 'journey-cancelled', + text: r.txtN + // todo: `r.sIdx` + } + } + if (r.type === 'L') { return { type: 'status', code: 'alternative-trip', @@ -76,8 +95,10 @@ const parseRemark = (profile, r) => { journeyId: r.jid } } - if (t.type === 'A') return hints[r.code && r.code.trim()] || null - if (t.type === 'R') { + if (r.type === 'A') { + return hints[r.code && r.code.trim().toLowerCase()] || null + } + if (r.type === 'R') { // todo: how can we identify the individual types? return { type: 'status', From d2257c26ffd315d89159ccc1d95d11949b9fe6e3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 16:00:28 +0200 Subject: [PATCH 03/10] rename d.remarks -> d.hints, parseRemark -> parseHint HAFAS returns notes for journey legs, stopovers and departures. There are two kinds of notes: "remarks" (in `remL`) and HAFAS Information Manager (HIM) notes (in `himL`). The former describe the regular operating situation, e.g. "bicycles allows", whereas the latter describe cancellations, construction work, etc. The planned naming scheme for hafas-client: - hints: notes from `remL` for regular operation - warnings: notes from `himL` for cancellations, construction, etc - remarks: both "notes" and "warnings" This commit prepares the new naming scheme by renaming the existing parsing logic of `remL` to "hints". Follow-up commits will add `parseWarning`. --- index.js | 8 ++++---- lib/default-profile.js | 4 ++-- lib/request.js | 2 +- lib/validate-profile.js | 2 +- p/db/index.js | 4 ++-- p/nahsh/index.js | 8 ++++---- p/oebb/index.js | 4 ++-- p/vbb/index.js | 8 ++++---- parse/departure.js | 6 +++--- parse/find-remark.js | 18 ++++++++++++++++++ parse/{remark.js => hint.js} | 26 +++++++++++++------------- parse/index.js | 2 +- parse/journey-leg.js | 12 ++++-------- parse/journey.js | 4 ++-- parse/movement.js | 4 ++-- parse/stopover.js | 2 +- 16 files changed, 64 insertions(+), 50 deletions(-) create mode 100644 parse/find-remark.js rename parse/{remark.js => hint.js} (81%) diff --git a/index.js b/index.js index 1db64568..ff474687 100644 --- a/index.js +++ b/index.js @@ -51,7 +51,7 @@ const createClient = (profile, request = _request) => { }) .then((d) => { if (!Array.isArray(d.jnyL)) return [] // todo: throw err? - const parse = profile.parseDeparture(profile, d.locations, d.lines, d.remarks) + const parse = profile.parseDeparture(profile, d.locations, d.lines, d.hints) return d.jnyL.map(parse) .sort((a, b) => new Date(a.when) - new Date(b.when)) }) @@ -164,7 +164,7 @@ const createClient = (profile, request = _request) => { if (!Array.isArray(d.outConL)) return [] const polylines = opt.polylines && d.common.polyL || [] - const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks, polylines) + const parse = profile.parseJourney(profile, d.locations, d.lines, d.hints, polylines) if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB @@ -310,7 +310,7 @@ const createClient = (profile, request = _request) => { }) .then((d) => { const polylines = opt.polyline && d.common.polyL || [] - const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.remarks, polylines) + const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.hints, polylines) const leg = { // pretend the leg is contained in a journey type: 'JNY', @@ -363,7 +363,7 @@ const createClient = (profile, request = _request) => { if (!Array.isArray(d.jnyL)) return [] const polylines = opt.polyline && d.common.polyL || [] - const parse = profile.parseMovement(profile, d.locations, d.lines, d.remarks, polylines) + const parse = profile.parseMovement(profile, d.locations, d.lines, d.hints, polylines) return d.jnyL.map(parse) }) } diff --git a/lib/default-profile.js b/lib/default-profile.js index 57d7eb36..d4d0ffcb 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -10,7 +10,7 @@ const parsePolyline = require('../parse/polyline') const parseMovement = require('../parse/movement') const parseNearby = require('../parse/nearby') const parseOperator = require('../parse/operator') -const parseRemark = require('../parse/remark') +const parseHint = require('../parse/hint') const parseStopover = require('../parse/stopover') const formatAddress = require('../format/address') @@ -47,7 +47,7 @@ const defaultProfile = { parseMovement, parseNearby, parseOperator, - parseRemark, + parseHint, parseStopover, formatAddress, diff --git a/lib/request.js b/lib/request.js index 3cde8fbb..4dddaad5 100644 --- a/lib/request.js +++ b/lib/request.js @@ -81,7 +81,7 @@ const request = (profile, data) => { const c = d.common || {} if (Array.isArray(c.remL)) { - d.remarks = c.remL.map(rem => profile.parseRemark(profile, rem)) + d.hints = c.remL.map(hint => profile.parseHint(profile, hint)) } if (Array.isArray(c.opL)) { d.operators = c.opL.map(op => profile.parseOperator(profile, op)) diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 88e3d0c1..267ab0e7 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -20,7 +20,7 @@ const types = { parseMovement: 'function', parseNearby: 'function', parseOperator: 'function', - parseRemark: 'function', + parseHint: 'function', parseStopover: 'function', formatAddress: 'function', diff --git a/p/db/index.js b/p/db/index.js index 773f8ab3..126f5f5e 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -34,8 +34,8 @@ const transformJourneysQuery = (query, opt) => { return query } -const createParseJourney = (profile, stations, lines, remarks, polylines) => { - const parseJourney = _createParseJourney(profile, stations, lines, remarks, polylines) +const createParseJourney = (profile, stations, lines, hints, polylines) => { + const parseJourney = _createParseJourney(profile, stations, lines, hints, polylines) // todo: j.sotRating, j.conSubscr, j.isSotCon, j.showARSLink, k.sotCtxt // todo: j.conSubscr, j.showARSLink, j.useableTime diff --git a/p/nahsh/index.js b/p/nahsh/index.js index 28d1a08d..bc691687 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -36,8 +36,8 @@ const parseLocation = (profile, l, lines) => { return res } -const createParseJourney = (profile, stations, lines, remarks) => { - const parseJourney = _createParseJourney(profile, stations, lines, remarks) +const createParseJourney = (profile, stations, lines, hints) => { + const parseJourney = _createParseJourney(profile, stations, lines, hints) const parseJourneyWithTickets = (j) => { const res = parseJourney(j) @@ -78,8 +78,8 @@ const createParseJourney = (profile, stations, lines, remarks) => { return parseJourneyWithTickets } -const createParseMovement = (profile, locations, lines, remarks) => { - const _parseMovement = _createParseMovement(profile, locations, lines, remarks) +const createParseMovement = (profile, locations, lines, hints) => { + const _parseMovement = _createParseMovement(profile, locations, lines, hints) const parseMovement = (m) => { const res = _parseMovement(m) // filter out empty nextStops entries diff --git a/p/oebb/index.js b/p/oebb/index.js index 99496761..c334e482 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -44,8 +44,8 @@ const parseLocation = (profile, l, lines) => { return res } -const createParseMovement = (profile, locations, lines, remarks) => { - const _parseMovement = _createParseMovement(profile, locations, lines, remarks) +const createParseMovement = (profile, locations, lines, hints) => { + const _parseMovement = _createParseMovement(profile, locations, lines, hints) const parseMovement = (m) => { const res = _parseMovement(m) // filter out POIs diff --git a/p/vbb/index.js b/p/vbb/index.js index 1366e49f..e293a6a1 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -56,8 +56,8 @@ const parseLocation = (profile, l, lines) => { return res } -const createParseJourney = (profile, stations, lines, remarks, polylines) => { - const parseJourney = _createParseJourney(profile, stations, lines, remarks, polylines) +const createParseJourney = (profile, stations, lines, hints, polylines) => { + const parseJourney = _createParseJourney(profile, stations, lines, hints, polylines) const parseJourneyWithTickets = (j) => { const res = parseJourney(j) @@ -86,8 +86,8 @@ const createParseJourney = (profile, stations, lines, remarks, polylines) => { return parseJourneyWithTickets } -const createParseDeparture = (profile, stations, lines, remarks) => { - const parseDeparture = _createParseDeparture(profile, stations, lines, remarks) +const createParseDeparture = (profile, stations, lines, hints) => { + const parseDeparture = _createParseDeparture(profile, stations, lines, hints) const ringbahnClockwise = /^ringbahn s\s?41$/i const ringbahnAnticlockwise = /^ringbahn s\s?42$/i diff --git a/parse/departure.js b/parse/departure.js index 158eb1ea..e1fb069b 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -6,8 +6,8 @@ // todo: d.stbStop.dProgType // todo: d.freq, d.freq.jnyL, see https://github.com/public-transport/hafas-client/blob/9203ed1481f08baacca41ac5e3c19bf022f01b0b/parse.js#L115 -const createParseDeparture = (profile, stations, lines, remarks) => { - const findRemark = rm => remarks[parseInt(rm.remX)] || null +const createParseDeparture = (profile, stations, lines, hints) => { + const findHint = rm => hints[parseInt(rm.remX)] || null const parseDeparture = (d) => { const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) @@ -17,7 +17,7 @@ const createParseDeparture = (profile, stations, lines, remarks) => { when: when.toISO(), direction: profile.parseStationName(d.dirTxt), line: lines[parseInt(d.prodX)] || null, - remarks: d.remL ? d.remL.map(findRemark) : [], + remarks: d.remL ? d.remL.map(findHint) : [], trip: +d.jid.split('|')[1] // todo: this seems brittle } // todo: res.trip from rawLine.prodCtx.num? diff --git a/parse/find-remark.js b/parse/find-remark.js new file mode 100644 index 00000000..c21aa8ab --- /dev/null +++ b/parse/find-remark.js @@ -0,0 +1,18 @@ +'use strict' + +// There are two kinds of notes: "remarks" (in `remL`) and HAFAS +// Information Manager (HIM) notes (in `himL`). The former describe +// the regular operating situation, e.g. "bicycles allows", whereas +// the latter describe cancellations, construction work, etc. + +// hafas-client's naming scheme: +// - hints: notes from `remL` for regular operation +// - warnings: notes from `himL` for cancellations, construction, etc +// - remarks: both "notes" and "warnings" + +const findRemark = (hints, ref) => { + // todo: `warnings[ref.himX]` + return hints[ref.remX] || null +} + +module.exports = findRemark diff --git a/parse/remark.js b/parse/hint.js similarity index 81% rename from parse/remark.js rename to parse/hint.js index c0ebdd8f..df2117ec 100644 --- a/parse/remark.js +++ b/parse/hint.js @@ -74,39 +74,39 @@ const hints = Object.assign(Object.create(null), { }) // todo: is passing in profile necessary? -const parseRemark = (profile, r) => { +const parseHint = (profile, h) => { // todo: U "stop cancelled"? // todo: C // todo: find sth more reliable than this - if (r.type === 'P' && r.txtN.toLowerCase().trim() === 'journey cancelled') { + if (h.type === 'P' && h.txtN.toLowerCase().trim() === 'journey cancelled') { return { type: 'status', code: 'journey-cancelled', - text: r.txtN - // todo: `r.sIdx` + text: h.txtN + // todo: `h.sIdx` } } - if (r.type === 'L') { + if (h.type === 'L') { return { type: 'status', code: 'alternative-trip', - text: r.txtN, - journeyId: r.jid + text: h.txtN, + journeyId: h.jid } } - if (r.type === 'A') { - return hints[r.code && r.code.trim().toLowerCase()] || null + if (h.type === 'A') { + return hints[h.code && h.code.trim().toLowerCase()] || null } - if (r.type === 'R') { + if (h.type === 'R') { // todo: how can we identify the individual types? return { type: 'status', - code: r.code, - text: r.txtN || '' + code: h.code, + text: h.txtN || '' } } return null } -module.exports = parseRemark +module.exports = parseHint diff --git a/parse/index.js b/parse/index.js index 11a5bd0f..e73bfb79 100644 --- a/parse/index.js +++ b/parse/index.js @@ -4,7 +4,7 @@ module.exports = { dateTime: require('./date-time'), location: require('./location'), line: require('./line'), - remark: require('./remark'), + hint: require('./hint'), operator: require('./operator'), stopover: require('./stopover'), journeyLeg: require('./journey-leg'), diff --git a/parse/journey-leg.js b/parse/journey-leg.js index bbc3f9da..390d6320 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -4,10 +4,7 @@ const parseDateTime = require('./date-time') const clone = obj => Object.assign({}, obj) -const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => { - // todo: finish parse/remark.js first - const applyRemark = (j, rm) => {} - +const createParseJourneyLeg = (profile, stations, lines, hints, polylines) => { // todo: pt.sDays // todo: pt.dep.dProgType, pt.arr.dProgType // todo: what is pt.jny.dirFlg? @@ -55,13 +52,12 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS if (passed && pt.jny.stopL) { - const parse = profile.parseStopover(profile, stations, lines, remarks, j.date) + const parse = profile.parseStopover(profile, stations, lines, hints, j.date) const passedStations = pt.jny.stopL.map(parse) // filter stations the train passes without stopping, as this doesn't comply with fptf (yet) res.passed = passedStations.filter((x) => !x.passBy) - } - if (Array.isArray(pt.jny.remL)) { - for (let remark of pt.jny.remL) applyRemark(j, remark) + + // todo: pt.jny.remL, j.msgL } const freq = pt.jny.freq || {} diff --git a/parse/journey.js b/parse/journey.js index 24aaf8c8..d174dd4e 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -2,8 +2,8 @@ const clone = obj => Object.assign({}, obj) -const createParseJourney = (profile, stations, lines, remarks, polylines) => { - const parseLeg = profile.parseJourneyLeg(profile, stations, lines, remarks, polylines) +const createParseJourney = (profile, stations, lines, hints, polylines) => { + const parseLeg = profile.parseJourneyLeg(profile, stations, lines, hints, polylines) // todo: c.sDays // todo: c.conSubscr diff --git a/parse/movement.js b/parse/movement.js index 8a3ab401..82176eb1 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,6 +1,6 @@ 'use strict' -const createParseMovement = (profile, locations, lines, remarks, polylines = []) => { +const createParseMovement = (profile, locations, lines, hints, polylines = []) => { // todo: what is m.dirGeo? maybe the speed? // todo: what is m.stopL? // todo: what is m.proc? wut? @@ -8,7 +8,7 @@ const createParseMovement = (profile, locations, lines, remarks, polylines = []) // todo: what is m.ani.dirGeo[n]? maybe the speed? // todo: what is m.ani.proc[n]? wut? const parseMovement = (m) => { - const pStopover = profile.parseStopover(profile, locations, lines, remarks, m.date) + const pStopover = profile.parseStopover(profile, locations, lines, hints, m.date) const res = { direction: profile.parseStationName(m.dirTxt), diff --git a/parse/stopover.js b/parse/stopover.js index 8315c814..622d3d56 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -2,7 +2,7 @@ // todo: arrivalDelay, departureDelay or only delay ? // todo: arrivalPlatform, departurePlatform -const createParseStopover = (profile, stations, lines, remarks, date) => { +const createParseStopover = (profile, stations, lines, hints, date) => { const parseStopover = (st) => { const res = { station: stations[parseInt(st.locX)] || null, From b6e37595a5609bd5c532f99b11d4b969ee17f9c2 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 16:38:54 +0200 Subject: [PATCH 04/10] remarks for journey legs --- parse/departure.js | 9 ++++++--- parse/journey-leg.js | 21 ++++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/parse/departure.js b/parse/departure.js index e1fb069b..fddef97e 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -1,5 +1,7 @@ 'use strict' +const findRemark = require('./find-remark') + // todos from public-transport/hafas-client#2 // - stdStop.dPlatfS, stdStop.dPlatfR // todo: what is d.jny.dirFlg? @@ -7,8 +9,6 @@ // todo: d.freq, d.freq.jnyL, see https://github.com/public-transport/hafas-client/blob/9203ed1481f08baacca41ac5e3c19bf022f01b0b/parse.js#L115 const createParseDeparture = (profile, stations, lines, hints) => { - const findHint = rm => hints[parseInt(rm.remX)] || null - const parseDeparture = (d) => { const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) const res = { @@ -17,7 +17,10 @@ const createParseDeparture = (profile, stations, lines, hints) => { when: when.toISO(), direction: profile.parseStationName(d.dirTxt), line: lines[parseInt(d.prodX)] || null, - remarks: d.remL ? d.remL.map(findHint) : [], + remarks: (d.remL + ? d.remL.map(ref => findRemark(hints, ref)) + : [] + ), trip: +d.jid.split('|')[1] // todo: this seems brittle } // todo: res.trip from rawLine.prodCtx.num? diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 390d6320..31c743cd 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -1,9 +1,25 @@ 'use strict' const parseDateTime = require('./date-time') +const findRemark = require('./find-remark') const clone = obj => Object.assign({}, obj) +const applyRemarksToStopovers = (stopovers, hints, refs) => { + for (let ref of refs) { + const remark = findRemark(hints, ref) + for (let i = ref.fLocX; i <= ref.tLocX; i++) { + const stopover = stopovers[i] + if (Array.isArray(stopover.remarks)) { + stopover.remarks.push(remark) + } else { + stopover.remarks = [remark] + } + } + // todo: `ref.tagL` + } +} + const createParseJourneyLeg = (profile, stations, lines, hints, polylines) => { // todo: pt.sDays // todo: pt.dep.dProgType, pt.arr.dProgType @@ -57,7 +73,10 @@ const createParseJourneyLeg = (profile, stations, lines, hints, polylines) => { // filter stations the train passes without stopping, as this doesn't comply with fptf (yet) res.passed = passedStations.filter((x) => !x.passBy) - // todo: pt.jny.remL, j.msgL + // todo: pt.jny.remL + if (Array.isArray(j.msgL)) { + applyRemarksToStopovers(passedStations, hints, j.msgL) + } } const freq = pt.jny.freq || {} From d909be3b653a9507f6c1e059bc40c4beeba7f591 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 18:46:24 +0200 Subject: [PATCH 05/10] parseWarning --- lib/default-profile.js | 2 ++ lib/request.js | 4 ++++ lib/validate-profile.js | 1 + package.json | 1 + parse/warning.js | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 43 insertions(+) create mode 100644 parse/warning.js diff --git a/lib/default-profile.js b/lib/default-profile.js index d4d0ffcb..c7dbd27b 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -11,6 +11,7 @@ const parseMovement = require('../parse/movement') const parseNearby = require('../parse/nearby') const parseOperator = require('../parse/operator') const parseHint = require('../parse/hint') +const parseWarning = require('../parse/warning') const parseStopover = require('../parse/stopover') const formatAddress = require('../format/address') @@ -48,6 +49,7 @@ const defaultProfile = { parseNearby, parseOperator, parseHint, + parseWarning, parseStopover, formatAddress, diff --git a/lib/request.js b/lib/request.js index 4dddaad5..8817d3b9 100644 --- a/lib/request.js +++ b/lib/request.js @@ -83,6 +83,10 @@ const request = (profile, data) => { if (Array.isArray(c.remL)) { d.hints = c.remL.map(hint => profile.parseHint(profile, hint)) } + d.warnings = [] + if (Array.isArray(c.himL)) { + d.warnings = c.himL.map(w => profile.parseWarning(profile, w)) + } if (Array.isArray(c.opL)) { d.operators = c.opL.map(op => profile.parseOperator(profile, op)) } diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 267ab0e7..38ed6cd8 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -21,6 +21,7 @@ const types = { parseNearby: 'function', parseOperator: 'function', parseHint: 'function', + parseWarning: 'function', parseStopover: 'function', formatAddress: 'function', diff --git a/package.json b/package.json index 7ab50742..17d200ab 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "@mapbox/polyline": "^1.0.0", + "br2nl": "^1.0.0", "capture-stack-trace": "^1.0.0", "create-hash": "^1.2.0", "fetch-ponyfill": "^6.0.0", diff --git a/parse/warning.js b/parse/warning.js new file mode 100644 index 00000000..f6e1ce2f --- /dev/null +++ b/parse/warning.js @@ -0,0 +1,35 @@ +'use strict' + +const brToNewline = require('br2nl') + +const parseDateTime = require('./date-time') + +// todo: is passing in profile necessary? +const parseWarning = (profile, w) => { + // todo: hid, act, pub, lead, tckr, icoX, fLocX, tLocX, prod, comp, + // todo: cat (1, 2), pubChL + // pubChL: + // [ { name: 'timetable', + // fDate: '20180606', + // fTime: '073000', + // tDate: '20180713', + // tTime: '030000' }, + // { name: 'export', + // fDate: '20180606', + // fTime: '073000', + // tDate: '20180713', + // tTime: '030000' } ] + + return { + type: 'warning', + summary: brToNewline(w.head), + text: brToNewline(w.text), + priority: w.prio, + category: w.cat, // todo: parse to sth meaningful + validFrom: parseDateTime(profile, w.sDate, w.sTime).toISO(), + validUntil: parseDateTime(profile, w.eDate, w.eTime).toISO(), + modified: parseDateTime(profile, w.lModDate, w.lModTime).toISO() + } +} + +module.exports = parseWarning From fa2cdc5177a3831122e64e4ba59c8d1e91ded356 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 18:48:45 +0200 Subject: [PATCH 06/10] pass warnings into parsers, apply warnings to journey legs --- index.js | 8 ++++---- parse/departure.js | 4 ++-- parse/find-remark.js | 5 ++--- parse/journey-leg.js | 10 +++++----- parse/journey.js | 4 ++-- parse/movement.js | 4 ++-- parse/stopover.js | 2 +- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index ff474687..38fab4ab 100644 --- a/index.js +++ b/index.js @@ -51,7 +51,7 @@ const createClient = (profile, request = _request) => { }) .then((d) => { if (!Array.isArray(d.jnyL)) return [] // todo: throw err? - const parse = profile.parseDeparture(profile, d.locations, d.lines, d.hints) + const parse = profile.parseDeparture(profile, d.locations, d.lines, d.hints, d.warnings) return d.jnyL.map(parse) .sort((a, b) => new Date(a.when) - new Date(b.when)) }) @@ -164,7 +164,7 @@ const createClient = (profile, request = _request) => { if (!Array.isArray(d.outConL)) return [] const polylines = opt.polylines && d.common.polyL || [] - const parse = profile.parseJourney(profile, d.locations, d.lines, d.hints, polylines) + const parse = profile.parseJourney(profile, d.locations, d.lines, d.hints, d.warnings, polylines) if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB @@ -310,7 +310,7 @@ const createClient = (profile, request = _request) => { }) .then((d) => { const polylines = opt.polyline && d.common.polyL || [] - const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.hints, polylines) + const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.hints, d.warnings, polylines) const leg = { // pretend the leg is contained in a journey type: 'JNY', @@ -363,7 +363,7 @@ const createClient = (profile, request = _request) => { if (!Array.isArray(d.jnyL)) return [] const polylines = opt.polyline && d.common.polyL || [] - const parse = profile.parseMovement(profile, d.locations, d.lines, d.hints, polylines) + const parse = profile.parseMovement(profile, d.locations, d.lines, d.hints, d.warnings, polylines) return d.jnyL.map(parse) }) } diff --git a/parse/departure.js b/parse/departure.js index fddef97e..a9a5995e 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -8,7 +8,7 @@ const findRemark = require('./find-remark') // todo: d.stbStop.dProgType // todo: d.freq, d.freq.jnyL, see https://github.com/public-transport/hafas-client/blob/9203ed1481f08baacca41ac5e3c19bf022f01b0b/parse.js#L115 -const createParseDeparture = (profile, stations, lines, hints) => { +const createParseDeparture = (profile, stations, lines, hints, warnings) => { const parseDeparture = (d) => { const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) const res = { @@ -18,7 +18,7 @@ const createParseDeparture = (profile, stations, lines, hints) => { direction: profile.parseStationName(d.dirTxt), line: lines[parseInt(d.prodX)] || null, remarks: (d.remL - ? d.remL.map(ref => findRemark(hints, ref)) + ? d.remL.map(ref => findRemark(hints, warnings, ref)) : [] ), trip: +d.jid.split('|')[1] // todo: this seems brittle diff --git a/parse/find-remark.js b/parse/find-remark.js index c21aa8ab..9bf89884 100644 --- a/parse/find-remark.js +++ b/parse/find-remark.js @@ -10,9 +10,8 @@ // - warnings: notes from `himL` for cancellations, construction, etc // - remarks: both "notes" and "warnings" -const findRemark = (hints, ref) => { - // todo: `warnings[ref.himX]` - return hints[ref.remX] || null +const findRemark = (hints, warnings, ref) => { + return warnings[ref.himX] || hints[ref.remX] || null } module.exports = findRemark diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 31c743cd..4d98b609 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -5,9 +5,9 @@ const findRemark = require('./find-remark') const clone = obj => Object.assign({}, obj) -const applyRemarksToStopovers = (stopovers, hints, refs) => { +const applyRemarksToStopovers = (stopovers, hints, warnings, refs) => { for (let ref of refs) { - const remark = findRemark(hints, ref) + const remark = findRemark(hints, warnings, ref) for (let i = ref.fLocX; i <= ref.tLocX; i++) { const stopover = stopovers[i] if (Array.isArray(stopover.remarks)) { @@ -20,7 +20,7 @@ const applyRemarksToStopovers = (stopovers, hints, refs) => { } } -const createParseJourneyLeg = (profile, stations, lines, hints, polylines) => { +const createParseJourneyLeg = (profile, stations, lines, hints, warnings, polylines) => { // todo: pt.sDays // todo: pt.dep.dProgType, pt.arr.dProgType // todo: what is pt.jny.dirFlg? @@ -68,14 +68,14 @@ const createParseJourneyLeg = (profile, stations, lines, hints, polylines) => { if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS if (passed && pt.jny.stopL) { - const parse = profile.parseStopover(profile, stations, lines, hints, j.date) + const parse = profile.parseStopover(profile, stations, lines, hints, warnings, j.date) const passedStations = pt.jny.stopL.map(parse) // filter stations the train passes without stopping, as this doesn't comply with fptf (yet) res.passed = passedStations.filter((x) => !x.passBy) // todo: pt.jny.remL if (Array.isArray(j.msgL)) { - applyRemarksToStopovers(passedStations, hints, j.msgL) + applyRemarksToStopovers(passedStations, hints, warnings, j.msgL) } } diff --git a/parse/journey.js b/parse/journey.js index d174dd4e..18cd3029 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -2,8 +2,8 @@ const clone = obj => Object.assign({}, obj) -const createParseJourney = (profile, stations, lines, hints, polylines) => { - const parseLeg = profile.parseJourneyLeg(profile, stations, lines, hints, polylines) +const createParseJourney = (profile, stations, lines, hints, warnings, polylines) => { + const parseLeg = profile.parseJourneyLeg(profile, stations, lines, hints, warnings, polylines) // todo: c.sDays // todo: c.conSubscr diff --git a/parse/movement.js b/parse/movement.js index 82176eb1..2aa48b84 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,6 +1,6 @@ 'use strict' -const createParseMovement = (profile, locations, lines, hints, polylines = []) => { +const createParseMovement = (profile, locations, lines, hints, warnings, polylines = []) => { // todo: what is m.dirGeo? maybe the speed? // todo: what is m.stopL? // todo: what is m.proc? wut? @@ -8,7 +8,7 @@ const createParseMovement = (profile, locations, lines, hints, polylines = []) = // todo: what is m.ani.dirGeo[n]? maybe the speed? // todo: what is m.ani.proc[n]? wut? const parseMovement = (m) => { - const pStopover = profile.parseStopover(profile, locations, lines, hints, m.date) + const pStopover = profile.parseStopover(profile, locations, lines, hints, warnings, m.date) const res = { direction: profile.parseStationName(m.dirTxt), diff --git a/parse/stopover.js b/parse/stopover.js index 622d3d56..c01dbc15 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -2,7 +2,7 @@ // todo: arrivalDelay, departureDelay or only delay ? // todo: arrivalPlatform, departurePlatform -const createParseStopover = (profile, stations, lines, hints, date) => { +const createParseStopover = (profile, stations, lines, hints, warnings, date) => { const parseStopover = (st) => { const res = { station: stations[parseInt(st.locX)] || null, From d3d23140fd1f4f2d1d78b2f9f7712c72ef7ce3a7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 11 Jun 2018 11:29:32 +0200 Subject: [PATCH 07/10] (more) remarks for journeys, journey legs & stopovers --- parse/journey-leg.js | 25 +++++++++++++++---------- parse/journey.js | 10 +++++++++- parse/stopover.js | 10 ++++++++++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 4d98b609..cf7928b5 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -5,16 +5,21 @@ const findRemark = require('./find-remark') const clone = obj => Object.assign({}, obj) -const applyRemarksToStopovers = (stopovers, hints, warnings, refs) => { +const applyRemarks = (leg, hints, warnings, refs) => { for (let ref of refs) { const remark = findRemark(hints, warnings, ref) - for (let i = ref.fLocX; i <= ref.tLocX; i++) { - const stopover = stopovers[i] - if (Array.isArray(stopover.remarks)) { - stopover.remarks.push(remark) - } else { - stopover.remarks = [remark] + if ('number' === typeof ref.fLocX && 'number' === typeof ref.tLocX) { + for (let i = ref.fLocX; i <= ref.tLocX; i++) { + const stopover = leg.passed[i] + if (Array.isArray(stopover.remarks)) { + stopover.remarks.push(remark) + } else { + stopover.remarks = [remark] + } } + } else { + if (Array.isArray(leg.remarks)) leg.remarks.push(remark) + else leg.remarks = [remark] } // todo: `ref.tagL` } @@ -73,9 +78,9 @@ const createParseJourneyLeg = (profile, stations, lines, hints, warnings, polyli // filter stations the train passes without stopping, as this doesn't comply with fptf (yet) res.passed = passedStations.filter((x) => !x.passBy) - // todo: pt.jny.remL - if (Array.isArray(j.msgL)) { - applyRemarksToStopovers(passedStations, hints, warnings, j.msgL) + // todo: is there a `pt.jny.remL`? + if (Array.isArray(pt.jny.msgL)) { + applyRemarks(res, hints, warnings, pt.jny.msgL) } } diff --git a/parse/journey.js b/parse/journey.js index 18cd3029..d071013f 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -1,6 +1,6 @@ 'use strict' -const clone = obj => Object.assign({}, obj) +const findRemark = require('./find-remark') const createParseJourney = (profile, stations, lines, hints, warnings, polylines) => { const parseLeg = profile.parseJourneyLeg(profile, stations, lines, hints, warnings, polylines) @@ -15,6 +15,14 @@ const createParseJourney = (profile, stations, lines, hints, warnings, polylines legs } + if (Array.isArray(j.msgL)) { + res.remarks = [] + for (let ref of j.msgL) { + const remark = findRemark(hints, warnings, ref) + if (remark) res.remarks.push(remark) + } + } + return res } diff --git a/parse/stopover.js b/parse/stopover.js index c01dbc15..5dc23e39 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -1,5 +1,7 @@ 'use strict' +const findRemark = require('./find-remark') + // todo: arrivalDelay, departureDelay or only delay ? // todo: arrivalPlatform, departurePlatform const createParseStopover = (profile, stations, lines, hints, warnings, date) => { @@ -38,6 +40,14 @@ const createParseStopover = (profile, stations, lines, hints, warnings, date) => } } + if (Array.isArray(st.msgL)) { + res.remarks = [] + for (let ref of st.msgL) { + const remark = findRemark(hints, warnings, ref) + if (remark) res.remarks.push(remark) + } + } + return res } From 3556130f6b9468196ce66e83c273081936752ff4 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 11 Jun 2018 18:41:53 +0200 Subject: [PATCH 08/10] parse more hints --- parse/hint.js | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/parse/hint.js b/parse/hint.js index df2117ec..d0f2cff8 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -75,23 +75,47 @@ const hints = Object.assign(Object.create(null), { // todo: is passing in profile necessary? const parseHint = (profile, h) => { - // todo: U "stop cancelled"? // todo: C + // todo: D code: '', txtN: 'Fire services operating close to the tracks' + + const text = h.txtN && h.txtN.trim() || '' // todo: find sth more reliable than this - if (h.type === 'P' && h.txtN.toLowerCase().trim() === 'journey cancelled') { + if (h.type === 'P' && text.toLowerCase() === 'journey cancelled') { return { type: 'status', code: 'journey-cancelled', - text: h.txtN + text // todo: `h.sIdx` } } + if (h.type === 'U' && text.toLowerCase() === 'stop cancelled') { + return { + type: 'status', + code: 'stop-cancelled', + text + } + } + // todo: + // { + // "type": "U", + // "code": "", + // "icoX": 3, + // "txtN": "entrance and exit not possible" + // } + if (h.type === 'U') { + return { + type: 'status', + code: null, // todo + text + } + } + if (h.type === 'L') { return { type: 'status', code: 'alternative-trip', - text: h.txtN, + text, journeyId: h.jid } } @@ -103,7 +127,7 @@ const parseHint = (profile, h) => { return { type: 'status', code: h.code, - text: h.txtN || '' + text } } return null From 871db25bc9c3efb99f537d3c084fe454ee974d8e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 11 Jun 2018 19:50:44 +0200 Subject: [PATCH 09/10] parse more hints, add todos --- parse/hint.js | 17 ++++++++++++++++- parse/journey-leg.js | 2 ++ parse/journey.js | 7 +++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/parse/hint.js b/parse/hint.js index d0f2cff8..a2e09cd5 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -76,10 +76,25 @@ const hints = Object.assign(Object.create(null), { // todo: is passing in profile necessary? const parseHint = (profile, h) => { // todo: C - // todo: D code: '', txtN: 'Fire services operating close to the tracks' const text = h.txtN && h.txtN.trim() || '' + if (h.type === 'M') { + return { + type: 'status', + code: h.code || null, + summary: h.txtS && h.txtS.trim() || '', + text + } + } + if (h.type === 'D') { + return { + type: 'status', + code: h.code || null, + text + } + } + // todo: find sth more reliable than this if (h.type === 'P' && text.toLowerCase() === 'journey cancelled') { return { diff --git a/parse/journey-leg.js b/parse/journey-leg.js index cf7928b5..225214d3 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -26,11 +26,13 @@ const applyRemarks = (leg, hints, warnings, refs) => { } const createParseJourneyLeg = (profile, stations, lines, hints, warnings, polylines) => { + // todo: pt.status // todo: pt.sDays // todo: pt.dep.dProgType, pt.arr.dProgType // todo: what is pt.jny.dirFlg? // todo: how does pt.freq work? // todo: what is pt.himL? + // todo: pt.planrtTS const parseJourneyLeg = (j, pt, passed = true) => { // j = journey, pt = part const dep = profile.parseDateTime(profile, j.date, pt.dep.dTimeR || pt.dep.dTimeS) const arr = profile.parseDateTime(profile, j.date, pt.arr.aTimeR || pt.arr.aTimeS) diff --git a/parse/journey.js b/parse/journey.js index d071013f..fabcf15c 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -8,6 +8,13 @@ const createParseJourney = (profile, stations, lines, hints, warnings, polylines // todo: c.sDays // todo: c.conSubscr // todo: c.trfRes x vbb-parse-ticket + // todo: c.sotRating, c.isSotCon, c.sotCtxt + // todo: c.showARSLink + // todo: c.useableTime + // todo: c.cksum + // todo: c.isNotRdbl + // todo: c.badSecRefX + // todo: c.bfATS, c.bfIOSTS const parseJourney = (j) => { const legs = j.secL.map(leg => parseLeg(j, leg)) const res = { From 1299d7f04f3e5768b0c4db0e5e0cfe6128aeafd7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 11 Jun 2018 21:37:13 +0200 Subject: [PATCH 10/10] parse more hints --- parse/hint.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/parse/hint.js b/parse/hint.js index a2e09cd5..bbc5a04e 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -70,6 +70,16 @@ const hints = Object.assign(Object.create(null), { type: 'hint', code: 'air-conditioned', text: 'air-conditioned vehicle' + }, + r0: { + type: 'hint', + code: 'upward-escalator', + text: 'upward escalator' + }, + au: { + type: 'hint', + code: 'elevator', + text: 'elevator available' } }) @@ -121,7 +131,7 @@ const parseHint = (profile, h) => { if (h.type === 'U') { return { type: 'status', - code: null, // todo + code: h.code || null, text } }