From 81c9411cfea122dd081b414988f8d25ff8a0352b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 12 Dec 2017 03:28:54 +0100 Subject: [PATCH] deal with some todos, add more --- index.js | 17 ++++++++--------- lib/validate-profile.js | 5 +++++ p/db/index.js | 2 ++ p/db/modes.js | 13 +++++++++++++ p/vbb/index.js | 24 +++++++++++++++++++++++- p/vbb/modes.js | 10 ++++++++++ package.json | 1 + parse/journey-part.js | 26 +++++++------------------- parse/line.js | 5 ++--- test/util.js | 18 +++++++----------- 10 files changed, 78 insertions(+), 43 deletions(-) diff --git a/index.js b/index.js index d60098dc..4e4eaad5 100644 --- a/index.js +++ b/index.js @@ -73,14 +73,13 @@ const createClient = (profile) => { viaLocL: opt.via ? [opt.via] : null, arrLocL: [to], jnyFltrL: [products], + getTariff: !!opt.tickets, // todo: what is req.gisFltrL? - // todo: what are all these for? - getPT: true, - outFrwd: true, - getTariff: !!opt.tickets, - getIV: false, // walk & bike as alternatives? - getPolyline: false // shape for displaying on a map? + getPT: true, // todo: what is this? + outFrwd: true, // todo: what is this? + getIV: false, // todo: walk & bike as alternatives? + getPolyline: false // todo: shape for displaying on a map? }, opt) return request(profile, { @@ -193,7 +192,8 @@ const createClient = (profile) => { opt = Object.assign({ results: 256, // maximum number of vehicles duration: 30, // compute frames for the next n seconds - frames: 3 // nr of frames to compute + frames: 3, // nr of frames to compute + products: null // optionally an object of booleans }, opt || {}) opt.when = opt.when || new Date() @@ -211,8 +211,7 @@ const createClient = (profile) => { perStep: Math.round(durationPerStep), ageOfReport: true, // todo: what is this? jnyFltrL: [ - // todo: use `profile.formatProducts(opt.products || {})` - {type: 'PROD', mode: 'INC', value: '127'} + profile.formatProducts(opt.products || {}) ], trainPosMode: 'CALC' // todo: what is this? what about realtime? } diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 7d8b595b..7d875c74 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -7,6 +7,8 @@ const types = { transformReqBody: 'function', transformJourneysQuery: 'function', + products: 'object', + parseDateTime: 'function', parseDeparture: 'function', parseJourneyPart: 'function', @@ -37,6 +39,9 @@ const validateProfile = (profile) => { if (type !== typeof profile[key]) { throw new Error(`profile.${key} must be a ${type}.`) } + if (type === 'object' && profile[key] === null) { + throw new Error(`profile.${key} must not be null.`) + } } } diff --git a/p/db/index.js b/p/db/index.js index 9217e7a2..d42a7b97 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -144,6 +144,8 @@ const dbProfile = { transformReq, transformJourneysQuery, + products: modes.allProducts, + // todo: parseLocation parseLine, parseProducts: createParseBitmask(modes.bitmasks), diff --git a/p/db/modes.js b/p/db/modes.js index 9e91d505..7ee4b8f5 100644 --- a/p/db/modes.js +++ b/p/db/modes.js @@ -91,4 +91,17 @@ m.bitmasks[128] = m.subway m.bitmasks[256] = m.tram m.bitmasks[512] = m.taxi +m.allProducts = [ + m.nationalExp, + m.national, + m.regionalExp, + m.regional, + m.suburban, + m.bus, + m.ferry, + m.subway, + m.tram, + m.taxi +] + module.exports = m diff --git a/p/vbb/index.js b/p/vbb/index.js index 9fe551b6..700c9226 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -4,10 +4,12 @@ const shorten = require('vbb-short-station-name') const {to12Digit, to9Digit} = require('vbb-translate-ids') const parseLineName = require('vbb-parse-line') const parseTicket = require('vbb-parse-ticket') +const getStations = require('vbb-stations') const _parseLine = require('../../parse/line') const _parseLocation = require('../../parse/location') const _createParseJourney = require('../../parse/journey') +const _createParseStopover = require('../../parse/stopover') const _formatStation = require('../../format/station') const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') @@ -53,7 +55,10 @@ const parseLocation = (profile, l) => { if (res.type === 'station') { res.name = shorten(res.name) res.id = to12Digit(res.id) - // todo: https://github.com/derhuerst/vbb-hafas/blob/1c64bfe42422e2648b21016d233c808460250308/lib/parse.js#L67-L75 + if (!res.location.latitude || !res.location.longitude) { + const [s] = getStations(res.id) + if (s) Object.assign(res.location, s.coordinates) + } } return res } @@ -88,6 +93,20 @@ const createParseJourney = (profile, stations, lines, remarks) => { return parseJourneyWithTickets } +const createParseStopover = (profile, stations, lines, remarks, connection) => { + const parseStopover = _createParseStopover(profile, stations, lines, remarks, connection) + + const parseStopoverWithShorten = (st) => { + const res = parseStopover(st) + if (res.station && res.station.name) { + res.station.name = shorten(res.station.name) + } + return res + } + + return parseStopoverWithShorten +} + const isIBNR = /^\d{9,}$/ const formatStation = (id) => { if (!isIBNR.test(id)) throw new Error('station ID must be an IBNR.') @@ -119,11 +138,14 @@ const vbbProfile = { endpoint: 'https://fahrinfo.vbb.de/bin/mgate.exe', transformReqBody, + products: modes.allProducts, + parseStationName: shorten, parseLocation, parseLine, parseProducts: createParseBitmask(modes.bitmasks), parseJourney: createParseJourney, + parseStopover: createParseStopover, formatStation, formatProducts, diff --git a/p/vbb/modes.js b/p/vbb/modes.js index 65adc5be..388462b3 100644 --- a/p/vbb/modes.js +++ b/p/vbb/modes.js @@ -95,6 +95,16 @@ m.categories = [ m.unknown ] +m.allProducts = [ + m.suburban, + m.subway, + m.tram, + m.bus, + m.ferry, + m.express, + m.regional +] + // m.parseCategory = (category) => { // return m.categories[parseInt(category)] || m.unknown // } diff --git a/package.json b/package.json index 9dd80ca3..fe28f309 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "slugg": "^1.2.0", "vbb-parse-ticket": "^0.2.1", "vbb-short-station-name": "^0.4.0", + "vbb-stations": "^5.8.0", "vbb-translate-ids": "^3.1.0" }, "devDependencies": { diff --git a/parse/journey-part.js b/parse/journey-part.js index a2468067..198c5fc6 100644 --- a/parse/journey-part.js +++ b/parse/journey-part.js @@ -5,22 +5,6 @@ const parseDateTime = require('./date-time') const clone = obj => Object.assign({}, obj) const createParseJourneyPart = (profile, stations, lines, remarks) => { - const parseStopover = (j, st) => { - const res = { - station: stations[parseInt(st.locX)] - } - if (st.aTimeR || st.aTimeS) { - const arr = parseDateTime(profile, j.date, st.aTimeR || st.aTimeS) - res.arrival = arr.toISO() - } - if (st.dTimeR || st.dTimeS) { - const dep = parseDateTime(profile, j.date, st.dTimeR || st.dTimeS) - res.departure = dep.toISO() - } - - return res - } - // todo: finish parse/remark.js first const applyRemark = (j, rm) => {} @@ -28,6 +12,7 @@ const createParseJourneyPart = (profile, stations, lines, remarks) => { // todo: pt.dep.dProgType, pt.arr.dProgType // todo: what is pt.jny.dirFlg? // todo: how does pt.freq work? + // todo: what is pt.himL? const parseJourneyPart = (j, pt) => { // 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) @@ -46,7 +31,9 @@ const createParseJourneyPart = (profile, stations, lines, remarks) => { if (pt.type === 'WALK') { res.mode = 'walking' + res.public = true } else if (pt.type === 'JNY') { + // todo: pull `public` value from `profile.products` res.id = pt.jny.jid res.line = lines[parseInt(pt.jny.prodX)] || null res.direction = profile.parseStationName(pt.jny.dirTxt) @@ -55,7 +42,8 @@ const createParseJourneyPart = (profile, stations, lines, remarks) => { if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS if (pt.jny.stopL) { - res.passed = pt.jny.stopL.map(stopover => parseStopover(j, stopover)) + const parse = profile.parseStopover(profile, stations, lines, remarks, j) + res.passed = pt.jny.stopL.map(parse) } if (Array.isArray(pt.jny.remL)) { for (let remark of pt.jny.remL) applyRemark(j, remark) @@ -63,8 +51,8 @@ const createParseJourneyPart = (profile, stations, lines, remarks) => { if (pt.jny.freq && pt.jny.freq.jnyL) { const parseAlternative = (a) => { - // todo: realtime - const when = profile.parseDateTime(profile, j.date, a.stopL[0].dTimeS) + const t = a.stopL[0].dTimeS || a.stopL[0].dTimeR + const when = profile.parseDateTime(profile, j.date, t) return { line: lines[parseInt(a.prodX)] || null, when: when.toISO() diff --git a/parse/line.js b/parse/line.js index af999382..e8163875 100644 --- a/parse/line.js +++ b/parse/line.js @@ -1,8 +1,7 @@ 'use strict' -// todo: what is p.number vs p.line? -// todo: what is p.icoX? -// todo: what is p.oprX? +// todo: are p.number and p.line ever different? +// todo: operator from p.oprX? const parseLine = (profile, p) => { if (!p) return null // todo: handle this upstream const res = { diff --git a/test/util.js b/test/util.js index 343a1f36..ced875da 100644 --- a/test/util.js +++ b/test/util.js @@ -24,12 +24,12 @@ const assertValidPoi = (t, p) => { t.equal(typeof p.address, 'string') t.ok(p.address) } - assertValidLocation(t, p, true) // todo: do POIs always have coords? + assertValidLocation(t, p, true) } const assertValidAddress = (t, a) => { t.equal(typeof a.address, 'string') - assertValidLocation(t, a, true) // todo: do addresses always have coords? + assertValidLocation(t, a, true) } const assertValidLocation = (t, l, coordsOptional = false) => { @@ -58,18 +58,15 @@ const assertValidLocation = (t, l, coordsOptional = false) => { } } -// todo: https://github.com/public-transport/friendly-public-transport-format/tree/babf2b82947ab0e655a4a0e1cbee6b5519af9172/spec#modes -const isValidMode = (m) => { - return m === 'walking' || - m === 'train' || - m === 'bus' || - m === 'ferry' -} +const validLineModes = [ + 'train', 'bus', 'ferry', 'taxi', 'gondola', 'aircraft', + 'car', 'bicycle', 'walking' +] const assertValidLine = (t, l) => { t.equal(l.type, 'line') t.equal(typeof l.name, 'string') - t.ok(isValidMode(l.mode), 'invalid mode ' + l.mode) + t.ok(validLineModes.includes(l.mode), 'invalid mode ' + l.mode) t.equal(typeof l.product, 'string') t.equal(l.public, true) } @@ -140,7 +137,6 @@ module.exports = { assertValidPoi, assertValidAddress, assertValidLocation, - isValidMode, assertValidLine, isValidDateTime, assertValidStopover,