From b7c1ee3b0589c455c3134d918dfffdb2de73aa53 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 14:23:27 +0100 Subject: [PATCH 001/176] new products markup --- p/db/index.js | 10 ++-- p/db/modes.js | 108 ------------------------------------- p/db/products.js | 85 +++++++++++++++++++++++++++++ p/insa/products.js | 94 +++++++++++++------------------- p/oebb/products.js | 130 ++++++++++++++++++--------------------------- p/vbb/index.js | 10 ++-- p/vbb/modes.js | 112 -------------------------------------- p/vbb/products.js | 60 +++++++++++++++++++++ 8 files changed, 242 insertions(+), 367 deletions(-) delete mode 100644 p/db/modes.js create mode 100644 p/db/products.js delete mode 100644 p/vbb/modes.js create mode 100644 p/vbb/products.js diff --git a/p/db/index.js b/p/db/index.js index d8cc5e6f..2f6870c5 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -7,10 +7,10 @@ const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') const {bike} = require('../../format/filters') -const modes = require('./modes') +const products = require('./products') const formatLoyaltyCard = require('./loyalty-cards').format -const formatBitmask = createFormatBitmask(modes) +const formatBitmask = createFormatBitmask(products) const transformReqBody = (body) => { body.client = {id: 'DB', v: '16040000', type: 'IPH', name: 'DB Navigator'} @@ -47,7 +47,7 @@ const createParseLine = (profile, operators) => { res.mode = res.product = null if ('class' in res) { - const data = modes.bitmasks[parseInt(res.class)] + const data = products.bitmasks[parseInt(res.class)] if (data) { res.mode = data.mode res.product = data.product @@ -137,11 +137,11 @@ const dbProfile = { transformReqBody, transformJourneysQuery, - products: modes.allProducts, + products: products.allProducts, // todo: parseLocation parseLine: createParseLine, - parseProducts: createParseBitmask(modes.allProducts, defaultProducts), + parseProducts: createParseBitmask(products.allProducts, defaultProducts), parseJourney: createParseJourney, formatStation, diff --git a/p/db/modes.js b/p/db/modes.js deleted file mode 100644 index dcca851f..00000000 --- a/p/db/modes.js +++ /dev/null @@ -1,108 +0,0 @@ -'use strict' - -// todo: https://gist.github.com/anonymous/d3323a5d2d6e159ed42b12afd0380434#file-haf_products-properties-L1-L95 -const m = { - nationalExp: { - bitmask: 1, - name: 'InterCityExpress', - short: 'ICE', - mode: 'train', - product: 'nationalExp' - }, - national: { - bitmask: 2, - name: 'InterCity & EuroCity', - short: 'IC/EC', - mode: 'train', - product: 'national' - }, - regionalExp: { - bitmask: 4, - name: 'RegionalExpress & InterRegio', - short: 'RE/IR', - mode: 'train', - product: 'regionalExp' - }, - regional: { - bitmask: 8, - name: 'Regio', - short: 'RB', - mode: 'train', - product: 'regional' - }, - suburban: { - bitmask: 16, - name: 'S-Bahn', - short: 'S', - mode: 'train', - product: 'suburban' - }, - bus: { - bitmask: 32, - name: 'Bus', - short: 'B', - mode: 'bus', - product: 'bus' - }, - ferry: { - bitmask: 64, - name: 'Ferry', - short: 'F', - mode: 'watercraft', - product: 'ferry' - }, - subway: { - bitmask: 128, - name: 'U-Bahn', - short: 'U', - mode: 'train', - product: 'subway' - }, - tram: { - bitmask: 256, - name: 'Tram', - short: 'T', - mode: 'tram', - product: 'tram' - }, - taxi: { - bitmask: 512, - name: 'Group Taxi', - short: 'Taxi', - mode: 'taxi', - product: 'taxi' - }, - unknown: { - bitmask: 0, - name: 'unknown', - short: '?', - product: 'unknown' - } -} - -m.bitmasks = [] -m.bitmasks[1] = m.nationalExp -m.bitmasks[2] = m.national -m.bitmasks[4] = m.regionalExp -m.bitmasks[8] = m.regional -m.bitmasks[16] = m.suburban -m.bitmasks[32] = m.bus -m.bitmasks[64] = m.ferry -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/db/products.js b/p/db/products.js new file mode 100644 index 00000000..d1e26771 --- /dev/null +++ b/p/db/products.js @@ -0,0 +1,85 @@ +'use strict' + +// todo: https://gist.github.com/anonymous/d3323a5d2d6e159ed42b12afd0380434#file-haf_products-properties-L1-L95 +module.exports = [ + { + product: 'nationalExp', + mode: 'train', + bitmasks: [1], + name: 'InterCityExpress', + short: 'ICE', + default: true + }, + { + product: 'national', + mode: 'train', + bitmasks: [2], + name: 'InterCity & EuroCity', + short: 'IC/EC', + default: true + }, + { + product: 'regionalExp', + mode: 'train', + bitmasks: [4], + name: 'RegionalExpress & InterRegio', + short: 'RE/IR', + default: true + }, + { + product: 'regional', + mode: 'train', + bitmasks: [8], + name: 'Regio', + short: 'RB', + default: true + }, + { + product: 'suburban', + mode: 'train', + bitmasks: [16], + name: 'S-Bahn', + short: 'S', + default: true + }, + { + product: 'bus', + mode: 'bus', + bitmasks: [32], + name: 'Bus', + short: 'B', + default: true + }, + { + product: 'ferry', + mode: 'watercraft', + bitmasks: [64], + name: 'Ferry', + short: 'F', + default: true + }, + { + product: 'subway', + mode: 'train', + bitmasks: [128], + name: 'U-Bahn', + short: 'U', + default: true + }, + { + product: 'tram', + mode: 'tram', + bitmasks: [256], + name: 'Tram', + short: 'T', + default: true + }, + { + product: 'taxi', + mode: 'taxi', + bitmasks: [512], + name: 'Group Taxi', + short: 'Taxi', + default: true + } +] diff --git a/p/insa/products.js b/p/insa/products.js index d6b343e5..66bd63ec 100644 --- a/p/insa/products.js +++ b/p/insa/products.js @@ -1,82 +1,60 @@ 'use strict' -// TODO Jannis R.: DRY -const p = { - nationalExp: { - bitmask: 1, +module.exports = [ + { + product: 'nationalExp', + mode: 'train', + bitmasks: [1], name: 'InterCityExpress', short: 'ICE', - mode: 'train', - product: 'nationalExp' + default: true }, - national: { - bitmask: 2, + { + product: 'national', + mode: 'train', + bitmasks: [2], name: 'InterCity & EuroCity', short: 'IC/EC', - mode: 'train', - product: 'national' + default: true }, - regional: { - bitmask: 8, + { + product: 'regional', + mode: 'train', + bitmasks: [8], name: 'RegionalExpress & RegionalBahn', short: 'RE/RB', - mode: 'train', - product: 'regional' + default: true }, - suburban: { - bitmask: 16, + { + product: 'suburban', + mode: 'train', + bitmasks: [16], name: 'S-Bahn', short: 'S', - mode: 'train', - product: 'suburban' + default: true }, - tram: { - bitmask: 32, + { + product: 'tram', + mode: 'train', + bitmasks: [32], name: 'Tram', short: 'T', - mode: 'train', - product: 'tram' + default: true }, - bus: { - bitmask: 64+128, + { + product: 'bus', + mode: 'bus', + bitmasks: [64, 128], name: 'Bus', short: 'B', - mode: 'bus', - product: 'bus' + default: true }, - tourismTrain: { - bitmask: 256, + { + product: 'tourismTrain', + mode: 'train', + bitmasks: [256], name: 'Tourism Train', short: 'TT', - mode: 'train', - product: 'tourismTrain' - }, - unknown: { - bitmask: 0, - name: 'unknown', - short: '?', - product: 'unknown' + default: true } -} - -p.bitmasks = [] -p.bitmasks[1] = p.nationalExp -p.bitmasks[2] = p.national -p.bitmasks[8] = p.regional -p.bitmasks[16] = p.suburban -p.bitmasks[32] = p.tram -p.bitmasks[64] = p.bus -p.bitmasks[128] = p.bus -p.bitmasks[256] = p.tourismTrain - -p.allProducts = [ - p.nationalExp, - p.national, - p.regional, - p.suburban, - p.tram, - p.bus, - p.tourismTrain ] - -module.exports = p diff --git a/p/oebb/products.js b/p/oebb/products.js index 646d4588..1c0fd223 100644 --- a/p/oebb/products.js +++ b/p/oebb/products.js @@ -1,112 +1,84 @@ 'use strict' -const p = { - nationalExp: { - bitmask: 1, +module.exports = [ + { + product: 'nationalExp', + mode: 'train', + bitmasks: [1], name: 'InterCityExpress & RailJet', short: 'ICE/RJ', - mode: 'train', - product: 'nationalExp' + default: true }, - national: { - bitmask: 2 + 4, + { + product: 'national', + mode: 'train', + bitmasks: [2, 4], name: 'InterCity & EuroCity', short: 'IC/EC', - mode: 'train', - product: 'national' + default: true }, - interregional: { - bitmask: 8 + 4096, + { + product: 'interregional', + mode: 'train', + bitmasks: [8, 4096], name: 'Durchgangszug & EuroNight', short: 'D/EN', - mode: 'train', - product: 'interregional' + default: true }, - regional: { - bitmask: 16, + { + product: 'regional', + mode: 'train', + bitmasks: [16], name: 'Regional & RegionalExpress', short: 'R/REX', - mode: 'train', - product: 'regional' + default: true }, - suburban: { - bitmask: 32, + { + product: 'suburban', + mode: 'train', + bitmasks: [32], name: 'S-Bahn', short: 'S', - mode: 'train', - product: 'suburban' + default: true }, - bus: { - bitmask: 64, + { + product: 'bus', + mode: 'bus', + bitmasks: [64], name: 'Bus', short: 'B', - mode: 'bus', - product: 'bus' + default: true }, - ferry: { - bitmask: 128, + { + product: 'ferry', + mode: 'watercraft', + bitmasks: [128], name: 'Ferry', short: 'F', - mode: 'watercraft', - product: 'ferry' + default: true }, - subway: { - bitmask: 256, + { + product: 'subway', + mode: 'train', + bitmasks: [256], name: 'U-Bahn', short: 'U', - mode: 'train', - product: 'subway' + default: true }, - tram: { - bitmask: 512, + { + product: 'tram', + mode: 'train', + bitmasks: [512], name: 'Tram', short: 'T', - mode: 'train', - product: 'tram' + default: true }, - onCall: { - bitmask: 2048, + { + product: 'onCall', + mode: null, // todo + bitmasks: [2048], name: 'On-call transit', short: 'on-call', - mode: null, // todo - product: 'onCall' - }, - unknown: { - bitmask: 0, - name: 'unknown', - short: '?', - product: 'unknown' + default: true } -} - -p.bitmasks = [] -p.bitmasks[1] = p.nationalExp -p.bitmasks[2] = p.national -p.bitmasks[4] = p.national -p.bitmasks[2+4] = p.national -p.bitmasks[8] = p.interregional -p.bitmasks[16] = p.regional -p.bitmasks[32] = p.suburban -p.bitmasks[64] = p.bus -p.bitmasks[128] = p.ferry -p.bitmasks[256] = p.subway -p.bitmasks[512] = p.tram -p.bitmasks[1024] = p.unknown -p.bitmasks[2048] = p.onCall -p.bitmasks[4096] = p.interregional -p.bitmasks[8+4096] = p.interregional - -p.allProducts = [ - p.nationalExp, - p.national, - p.interregional, - p.regional, - p.suburban, - p.bus, - p.ferry, - p.subway, - p.tram, - p.onCall ] - -module.exports = p diff --git a/p/vbb/index.js b/p/vbb/index.js index 3f9f9ecd..10b5e743 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -15,9 +15,9 @@ const _formatStation = require('../../format/station') const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') -const modes = require('./modes') +const products = require('./products') -const formatBitmask = createFormatBitmask(modes) +const formatBitmask = createFormatBitmask(products) const transformReqBody = (body) => { body.client = {type: 'IPA', id: 'VBB', name: 'vbbPROD', v: '4010300'} @@ -36,7 +36,7 @@ const createParseLine = (profile, operators) => { res.mode = res.product = null if ('class' in res) { - const data = modes.bitmasks[parseInt(res.class)] + const data = products.bitmasks[parseInt(res.class)] if (data) { res.mode = data.mode res.product = data.product @@ -176,12 +176,12 @@ const vbbProfile = { transformReqBody, - products: modes.allProducts, + products: products.allProducts, parseStationName: shorten, parseLocation, parseLine: createParseLine, - parseProducts: createParseBitmask(modes.allProducts, defaultProducts), + parseProducts: createParseBitmask(products.allProducts, defaultProducts), parseJourney: createParseJourney, parseDeparture: createParseDeparture, parseStopover: createParseStopover, diff --git a/p/vbb/modes.js b/p/vbb/modes.js deleted file mode 100644 index 3e771793..00000000 --- a/p/vbb/modes.js +++ /dev/null @@ -1,112 +0,0 @@ -'use strict' - -// todo: remove useless keys -const m = { - suburban: { - category: 0, - bitmask: 1, - name: 'S-Bahn', - mode: 'train', - short: 'S', - product: 'suburban' - }, - - subway: { - category: 1, - bitmask: 2, - name: 'U-Bahn', - mode: 'train', - short: 'U', - product: 'subway' - }, - - tram: { - category: 2, - bitmask: 4, - name: 'Tram', - mode: 'train', - short: 'T', - product: 'tram' - }, - - bus: { - category: 3, - bitmask: 8, - name: 'Bus', - mode: 'bus', - short: 'B', - product: 'bus' - }, - - ferry: { - category: 4, - bitmask: 16, - name: 'Fähre', - mode: 'watercraft', - short: 'F', - product: 'ferry' - }, - - express: { - category: 5, - bitmask: 32, - name: 'IC/ICE', - mode: 'train', - short: 'E', - product: 'express' - }, - - regional: { - category: 6, - bitmask: 64, - name: 'RB/RE', - mode: 'train', - short: 'R', - product: 'regional' - }, - - unknown: { - category: null, - bitmask: 0, - name: 'unknown', - mode: null, - short: '?', - product: 'unknown' - } -} - -m.bitmasks = [] -m.bitmasks[1] = m.suburban -m.bitmasks[2] = m.subway -m.bitmasks[4] = m.tram -m.bitmasks[8] = m.bus -m.bitmasks[16] = m.ferry -m.bitmasks[32] = m.express -m.bitmasks[64] = m.regional - -m.categories = [ - m.suburban, - m.subway, - m.tram, - m.bus, - m.ferry, - m.express, - m.regional, - 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 -// } - -module.exports = m diff --git a/p/vbb/products.js b/p/vbb/products.js new file mode 100644 index 00000000..678972ac --- /dev/null +++ b/p/vbb/products.js @@ -0,0 +1,60 @@ +'use strict' + +module.exports = [ + { + product: 'suburban', + mode: 'train', + bitmasks: [1], + name: 'S-Bahn', + short: 'S', + default: true + }, + { + product: 'subway', + mode: 'train', + bitmasks: [2], + name: 'U-Bahn', + short: 'U', + default: true + }, + { + product: 'tram', + mode: 'train', + bitmasks: [4], + name: 'Tram', + short: 'T', + default: true + }, + { + product: 'bus', + mode: 'bus', + bitmasks: [8], + name: 'Bus', + short: 'B', + default: true + }, + { + product: 'ferry', + mode: 'watercraft', + bitmasks: [16], + name: 'Fähre', + short: 'F', + default: true + }, + { + product: 'express', + mode: 'train', + bitmasks: [32], + name: 'IC/ICE', + short: 'E', + default: true + }, + { + product: 'regional', + mode: 'train', + bitmasks: [64], + name: 'RB/RE', + short: 'R', + default: true + } +] From ce43f15ad50181455f1efe8bfd0db304d677bc6f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 15:38:16 +0100 Subject: [PATCH 002/176] parseProducts: move factory from profiles into main code --- index.js | 4 +++- lib/validate-profile.js | 5 +++++ p/db/index.js | 2 -- p/insa/index.js | 2 -- p/oebb/index.js | 2 -- p/vbb/index.js | 2 -- parse/products-bitmask.js | 30 +++++++++++++++++++----------- 7 files changed, 27 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index 158d0151..44a296d1 100644 --- a/index.js +++ b/index.js @@ -3,8 +3,9 @@ const minBy = require('lodash/minBy') const maxBy = require('lodash/maxBy') -const validateProfile = require('./lib/validate-profile') const defaultProfile = require('./lib/default-profile') +const createParseBitmask = require('./parse/products-bitmask') +const validateProfile = require('./lib/validate-profile') const _request = require('./lib/request') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -12,6 +13,7 @@ const isNonEmptyString = str => 'string' === typeof str && str.length > 0 const createClient = (profile, request = _request) => { profile = Object.assign({}, defaultProfile, profile) + profile.parseProducts = createParseBitmask(profile) validateProfile(profile) const departures = (station, opt = {}) => { diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 19465c3f..2a8ac81b 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -47,6 +47,11 @@ const validateProfile = (profile) => { throw new Error(`profile.${key} must not be null.`) } } + + if (!Array.isArray(profile.products)) { + throw new Error('profile.products must be an array.') + } + if (profile.products.length === 0) throw new Error('profile.products is empty.') } module.exports = validateProfile diff --git a/p/db/index.js b/p/db/index.js index 2f6870c5..d6fb2708 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -3,7 +3,6 @@ const _createParseLine = require('../../parse/line') const _createParseJourney = require('../../parse/journey') const _formatStation = require('../../format/station') -const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') const {bike} = require('../../format/filters') @@ -141,7 +140,6 @@ const dbProfile = { // todo: parseLocation parseLine: createParseLine, - parseProducts: createParseBitmask(products.allProducts, defaultProducts), parseJourney: createParseJourney, formatStation, diff --git a/p/insa/index.js b/p/insa/index.js index 8b7bea1b..948da700 100644 --- a/p/insa/index.js +++ b/p/insa/index.js @@ -2,7 +2,6 @@ const _createParseLine = require('../../parse/line') const products = require('./products') -const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') const defaultProducts = { @@ -70,7 +69,6 @@ const insaProfile = { transformReqBody, products: products.allProducts, - parseProducts: createParseBitmask(products.allProducts, defaultProducts), formatProducts, parseLine: createParseLine, diff --git a/p/oebb/index.js b/p/oebb/index.js index 5196ab49..c0da7427 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -3,7 +3,6 @@ // todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L5 // todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L47-L234 -const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') const _createParseLine = require('../../parse/line') const _parseLocation = require('../../parse/location') @@ -113,7 +112,6 @@ const oebbProfile = { products: products.allProducts, - parseProducts: createParseBitmask(products.allProducts, defaultProducts), parseLine: createParseLine, parseLocation, parseMovement: createParseMovement, diff --git a/p/vbb/index.js b/p/vbb/index.js index 10b5e743..b58d819b 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -12,7 +12,6 @@ const _createParseJourney = require('../../parse/journey') const _createParseStopover = require('../../parse/stopover') const _createParseDeparture = require('../../parse/departure') const _formatStation = require('../../format/station') -const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') const products = require('./products') @@ -181,7 +180,6 @@ const vbbProfile = { parseStationName: shorten, parseLocation, parseLine: createParseLine, - parseProducts: createParseBitmask(products.allProducts, defaultProducts), parseJourney: createParseJourney, parseDeparture: createParseDeparture, parseStopover: createParseStopover, diff --git a/parse/products-bitmask.js b/parse/products-bitmask.js index 5dc5e8bc..f8522dc6 100644 --- a/parse/products-bitmask.js +++ b/parse/products-bitmask.js @@ -1,25 +1,33 @@ 'use strict' -const createParseBitmask = (allProducts, defaultProducts) => { - allProducts = allProducts.sort((p1, p2) => p2.bitmask - p1.bitmask) // desc - if (allProducts.length === 0) throw new Error('allProducts is empty.') - for (let product of allProducts) { +const createParseBitmask = (profile) => { + const defaultProducts = {} + let withBitmask = [] + for (let product of profile.products) { if ('string' !== typeof product.product) { - throw new Error('allProducts[].product must be a string.') + throw new Error('profile.products[].product must be a string.') } - if ('number' !== typeof product.bitmask) { - throw new Error(product.product + '.bitmask must be a number.') + + defaultProducts[product.product] = false + if (!Array.isArray(product.bitmasks)) { + throw new Error(product.product + '.bitmasks must be an array.') + } + for (let bitmask of product.bitmasks) { + if ('number' !== typeof bitmask) { + throw new Error(product.product + '.bitmasks[] must be a number.') + } + withBitmask.push([bitmask, product]) } } + withBitmask.sort((a, b) => b[0] - a[0]) // descending const parseBitmask = (bitmask) => { const res = Object.assign({}, defaultProducts) - for (let product of allProducts) { - if (bitmask === 0) break - if ((product.bitmask & bitmask) > 0) { + for (let [pBitmask, product] of withBitmask) { + if ((pBitmask & bitmask) > 0) { res[product.product] = true - bitmask -= product.bitmask + bitmask -= pBitmask } } From 83248785e50bb0b42776d055689f952b3fdb8219 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 16:11:32 +0100 Subject: [PATCH 003/176] formatProducts: move factory from profiles into main code and rename to `formatProductsFilter` --- format/products-bitmask.js | 16 ---------------- format/products-filter.js | 34 ++++++++++++++++++++++++++++++++++ index.js | 8 +++++--- p/db/index.js | 27 +-------------------------- p/insa/index.js | 27 --------------------------- p/oebb/index.js | 25 ------------------------- p/vbb/index.js | 22 ---------------------- 7 files changed, 40 insertions(+), 119 deletions(-) delete mode 100644 format/products-bitmask.js create mode 100644 format/products-filter.js diff --git a/format/products-bitmask.js b/format/products-bitmask.js deleted file mode 100644 index b3baab87..00000000 --- a/format/products-bitmask.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -const createFormatBitmask = (allProducts) => { - const formatBitmask = (products) => { - if(Object.keys(products).length === 0) throw new Error('products filter must not be empty') - let bitmask = 0 - for (let product in products) { - if (!allProducts[product]) throw new Error('unknown product ' + product) - if (products[product] === true) bitmask += allProducts[product].bitmask - } - return bitmask - } - return formatBitmask -} - -module.exports = createFormatBitmask diff --git a/format/products-filter.js b/format/products-filter.js new file mode 100644 index 00000000..90b1d06b --- /dev/null +++ b/format/products-filter.js @@ -0,0 +1,34 @@ +'use strict' + +const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) +const hasProp = (o, k) => Object.prototype.hasOwnProperty.call(o, k) + +const createFormatProductsFilter = (profile) => { + const byProduct = {} + const defaultProducts = {} + for (let product of profile.products) { + byProduct[product.product] = product + defaultProducts[product.product] = product.default + } + + const formatProductsFilter = (filter) => { + if (!isObj(filter)) throw new Error('products filter must be an object') + filter = Object.assign({}, defaultProducts, filter) + + let res = 0 + for (let product in filter) { + if (!hasProp(filter, product) || filter[product] !== true) continue + if (!byProduct[product]) throw new Error('unknown product ' + product) + for (let bitmask of byProduct[product].bitmasks) res += bitmask + } + + return { + type: 'PROD', + mode: 'INC', + value: res + '' + } + } + return formatProductsFilter +} + +module.exports = createFormatProductsFilter diff --git a/index.js b/index.js index 44a296d1..4ea95c84 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ const maxBy = require('lodash/maxBy') const defaultProfile = require('./lib/default-profile') const createParseBitmask = require('./parse/products-bitmask') +const createFormatProductsFilter = require('./format/products-filter') const validateProfile = require('./lib/validate-profile') const _request = require('./lib/request') @@ -14,6 +15,7 @@ const isNonEmptyString = str => 'string' === typeof str && str.length > 0 const createClient = (profile, request = _request) => { profile = Object.assign({}, defaultProfile, profile) profile.parseProducts = createParseBitmask(profile) + profile.formatProductsFilter = createFormatProductsFilter(profile) validateProfile(profile) const departures = (station, opt = {}) => { @@ -26,7 +28,7 @@ const createClient = (profile, request = _request) => { duration: 10 // show departures for the next n minutes }, opt) opt.when = opt.when || new Date() - const products = profile.formatProducts(opt.products || {}) + const products = profile.formatProductsFilter(opt.products || {}) const dir = opt.direction ? profile.formatStation(opt.direction) : null return request(profile, { @@ -92,7 +94,7 @@ const createClient = (profile, request = _request) => { opt.when = opt.when || new Date() const filters = [ - profile.formatProducts(opt.products || {}) + profile.formatProductsFilter(opt.products || {}) ] if ( opt.accessibility && @@ -319,7 +321,7 @@ const createClient = (profile, request = _request) => { perStep: Math.round(durationPerStep), ageOfReport: true, // todo: what is this? jnyFltrL: [ - profile.formatProducts(opt.products || {}) + profile.formatProductsFilter(opt.products || {}) ], trainPosMode: 'CALC' // todo: what is this? what about realtime? } diff --git a/p/db/index.js b/p/db/index.js index d6fb2708..cd79befb 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -3,14 +3,11 @@ const _createParseLine = require('../../parse/line') const _createParseJourney = require('../../parse/journey') const _formatStation = require('../../format/station') -const createFormatBitmask = require('../../format/products-bitmask') const {bike} = require('../../format/filters') const products = require('./products') const formatLoyaltyCard = require('./loyalty-cards').format -const formatBitmask = createFormatBitmask(products) - const transformReqBody = (body) => { body.client = {id: 'DB', v: '16040000', type: 'IPH', name: 'DB Navigator'} body.ext = 'DB.R15.12.a' @@ -102,27 +99,6 @@ const formatStation = (id) => { return _formatStation(id) } -const defaultProducts = { - suburban: true, - subway: true, - tram: true, - bus: true, - ferry: true, - national: true, - nationalExp: true, - regional: true, - regionalExp: true, - taxi: false -} -const formatProducts = (products) => { - products = Object.assign(Object.create(null), defaultProducts, products) - return { - type: 'PROD', - mode: 'INC', - value: formatBitmask(products) + '' - } -} - // todo: find option for absolute number of results const dbProfile = { @@ -142,8 +118,7 @@ const dbProfile = { parseLine: createParseLine, parseJourney: createParseJourney, - formatStation, - formatProducts + formatStation } module.exports = dbProfile diff --git a/p/insa/index.js b/p/insa/index.js index 948da700..11709ca1 100644 --- a/p/insa/index.js +++ b/p/insa/index.js @@ -2,18 +2,6 @@ const _createParseLine = require('../../parse/line') const products = require('./products') -const createFormatBitmask = require('../../format/products-bitmask') - -const defaultProducts = { - nationalExp: true, - national: true, - regional: true, - suburban: true, - bus: true, - tram: true, - tourismTrain: true, -} - const transformReqBody = (body) => { body.client = { @@ -50,18 +38,6 @@ const createParseLine = (profile, operators) => { return parseLineWithMode } -const formatProducts = (products) => { - products = Object.assign(Object.create(null), defaultProducts, products) - return { - type: 'PROD', - mode: 'INC', - value: formatBitmask(products) + '' - } -} - -const formatBitmask = createFormatBitmask(products) - - const insaProfile = { locale: 'de-DE', timezone: 'Europe/Berlin', @@ -69,9 +45,6 @@ const insaProfile = { transformReqBody, products: products.allProducts, - formatProducts, - - parseLine: createParseLine, journeyLeg: true, radar: true diff --git a/p/oebb/index.js b/p/oebb/index.js index c0da7427..a3c73a0d 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -3,7 +3,6 @@ // todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L5 // todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L47-L234 -const createFormatBitmask = require('../../format/products-bitmask') const _createParseLine = require('../../parse/line') const _parseLocation = require('../../parse/location') const _createParseMovement = require('../../parse/movement') @@ -81,28 +80,6 @@ const createParseMovement = (profile, locations, lines, remarks) => { return parseMovement } -const defaultProducts = { - nationalExp: true, - national: true, - interregional: true, - regional: true, - suburban: true, - bus: true, - ferry: true, - subway: true, - tram: true, - onCall: true -} -const formatBitmask = createFormatBitmask(products) -const formatProducts = (products) => { - products = Object.assign(Object.create(null), defaultProducts, products) - return { - type: 'PROD', - mode: 'INC', - value: formatBitmask(products) + '' - } -} - const oebbProfile = { locale: 'de-AT', timezone: 'Europe/Vienna', @@ -116,8 +93,6 @@ const oebbProfile = { parseLocation, parseMovement: createParseMovement, - formatProducts, - journeyLeg: true, radar: true } diff --git a/p/vbb/index.js b/p/vbb/index.js index b58d819b..cb926e9d 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -12,12 +12,9 @@ const _createParseJourney = require('../../parse/journey') const _createParseStopover = require('../../parse/stopover') const _createParseDeparture = require('../../parse/departure') const _formatStation = require('../../format/station') -const createFormatBitmask = require('../../format/products-bitmask') const products = require('./products') -const formatBitmask = createFormatBitmask(products) - const transformReqBody = (body) => { body.client = {type: 'IPA', id: 'VBB', name: 'vbbPROD', v: '4010300'} body.ext = 'VBB.1' @@ -145,24 +142,6 @@ const formatStation = (id) => { return _formatStation(id) } -const defaultProducts = { - suburban: true, - subway: true, - tram: true, - bus: true, - ferry: true, - express: true, - regional: true -} -const formatProducts = (products) => { - products = Object.assign(Object.create(null), defaultProducts, products) - return { - type: 'PROD', - mode: 'INC', - value: formatBitmask(products) + '' - } -} - const vbbProfile = { locale: 'de-DE', timezone: 'Europe/Berlin', @@ -185,7 +164,6 @@ const vbbProfile = { parseStopover: createParseStopover, formatStation, - formatProducts, journeysNumF: false, journeyLeg: true, From 08357dfa9d8f37b0e6046848173ef2574857a5eb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 17:00:06 +0100 Subject: [PATCH 004/176] parseLine: move mode & products parsing from profiles into main code --- p/db/index.js | 24 +----------------------- p/insa/index.js | 23 +---------------------- p/oebb/index.js | 24 +----------------------- p/vbb/index.js | 15 +++------------ parse/line.js | 27 ++++++++++++++++++++++++--- 5 files changed, 30 insertions(+), 83 deletions(-) diff --git a/p/db/index.js b/p/db/index.js index cd79befb..812cb94d 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -1,6 +1,5 @@ 'use strict' -const _createParseLine = require('../../parse/line') const _createParseJourney = require('../../parse/journey') const _formatStation = require('../../format/station') const {bike} = require('../../format/filters') @@ -35,26 +34,6 @@ const transformJourneysQuery = (query, opt) => { return query } -const createParseLine = (profile, operators) => { - const parseLine = _createParseLine(profile, operators) - - const parseLineWithMode = (l) => { - const res = parseLine(l) - - res.mode = res.product = null - if ('class' in res) { - const data = products.bitmasks[parseInt(res.class)] - if (data) { - res.mode = data.mode - res.product = data.product - } - } - - return res - } - return parseLineWithMode -} - const createParseJourney = (profile, stations, lines, remarks) => { const parseJourney = _createParseJourney(profile, stations, lines, remarks) @@ -112,10 +91,9 @@ const dbProfile = { transformReqBody, transformJourneysQuery, - products: products.allProducts, + products: products, // todo: parseLocation - parseLine: createParseLine, parseJourney: createParseJourney, formatStation diff --git a/p/insa/index.js b/p/insa/index.js index 11709ca1..ed562dee 100644 --- a/p/insa/index.js +++ b/p/insa/index.js @@ -1,6 +1,5 @@ 'use strict' -const _createParseLine = require('../../parse/line') const products = require('./products') const transformReqBody = (body) => { @@ -18,33 +17,13 @@ const transformReqBody = (body) => { return body } -const createParseLine = (profile, operators) => { - const parseLine = _createParseLine(profile, operators) - - const parseLineWithMode = (l) => { - const res = parseLine(l) - - res.mode = res.product = null - if ('class' in res) { - const data = products.bitmasks[parseInt(res.class)] - if (data) { - res.mode = data.mode - res.product = data.product - } - } - - return res - } - return parseLineWithMode -} - const insaProfile = { locale: 'de-DE', timezone: 'Europe/Berlin', endpoint: 'http://reiseauskunft.insa.de/bin/mgate.exe', transformReqBody, - products: products.allProducts, + products: products, journeyLeg: true, radar: true diff --git a/p/oebb/index.js b/p/oebb/index.js index a3c73a0d..99496761 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -3,7 +3,6 @@ // todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L5 // todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L47-L234 -const _createParseLine = require('../../parse/line') const _parseLocation = require('../../parse/location') const _createParseMovement = require('../../parse/movement') @@ -26,26 +25,6 @@ const transformReqBody = (body) => { return body } -const createParseLine = (profile, operators) => { - const parseLine = _createParseLine(profile, operators) - - const parseLineWithMode = (l) => { - const res = parseLine(l) - - res.mode = res.product = null - if ('class' in res) { - const data = products.bitmasks[parseInt(res.class)] - if (data) { - res.mode = data.mode - res.product = data.product - } - } - - return res - } - return parseLineWithMode -} - const parseLocation = (profile, l, lines) => { // ÖBB has some 'stations' **in austria** with no departures/products, // like station entrances, that are actually POIs. @@ -87,9 +66,8 @@ const oebbProfile = { endpoint: 'http://fahrplan.oebb.at/bin/mgate.exe', transformReqBody, - products: products.allProducts, + products: products, - parseLine: createParseLine, parseLocation, parseMovement: createParseMovement, diff --git a/p/vbb/index.js b/p/vbb/index.js index cb926e9d..534c93d6 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -27,18 +27,9 @@ const transformReqBody = (body) => { const createParseLine = (profile, operators) => { const parseLine = _createParseLine(profile, operators) - const parseLineWithMode = (l) => { + const parseLineWithMoreDetails = (l) => { const res = parseLine(l) - res.mode = res.product = null - if ('class' in res) { - const data = products.bitmasks[parseInt(res.class)] - if (data) { - res.mode = data.mode - res.product = data.product - } - } - const details = parseLineName(l.name) res.symbol = details.symbol res.nr = details.nr @@ -48,7 +39,7 @@ const createParseLine = (profile, operators) => { return res } - return parseLineWithMode + return parseLineWithMoreDetails } const parseLocation = (profile, l, lines) => { @@ -154,7 +145,7 @@ const vbbProfile = { transformReqBody, - products: products.allProducts, + products: products, parseStationName: shorten, parseLocation, diff --git a/parse/line.js b/parse/line.js index 79300c08..99e8c293 100644 --- a/parse/line.js +++ b/parse/line.js @@ -2,8 +2,23 @@ const slugg = require('slugg') -// todo: are p.number and p.line ever different? const createParseLine = (profile, operators) => { + const byBitmask = [] + for (let product of profile.products) { + if ('string' !== typeof product.product) { + throw new Error('profile.products[].product must be a string.') + } + if (!Array.isArray(product.bitmasks)) { + throw new Error(product.product + '.bitmasks must be an array.') + } + for (let bitmask of product.bitmasks) { + if ('number' !== typeof bitmask) { + throw new Error(product.product + '.bitmasks[] must be a number.') + } + byBitmask[bitmask] = product + } + } + const parseLine = (p) => { if (!p) return null // todo: handle this upstream const res = { @@ -13,8 +28,9 @@ const createParseLine = (profile, operators) => { public: true } // todo: what is p.prodCtx && p.prodCtx.num? + // todo: what is p.number? - // This is terrible, but FPTF demands an ID. Let's pray for VBB to expose an ID. + // This is terrible, but FPTF demands an ID. Let's pray for HaCon to expose an ID. // todo: find a better way if (p.line) res.id = slugg(p.line.trim()) else if (p.name) res.id = slugg(p.name.trim()) @@ -24,7 +40,12 @@ const createParseLine = (profile, operators) => { res.productCode = +p.prodCtx.catCode } - // todo: parse mode, remove from profiles + if ('class' in res) { + // todo: what if `res.class` is the sum of two bitmasks? + const data = byBitmask[parseInt(res.class)] + res.mode = data && data.mode || null + res.product = data && data.product || null + } if ('number' === typeof p.oprX) { res.operator = operators[p.oprX] || null From bbb68d86d52fe99676db5908d657fae84861a4eb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 17:03:13 +0100 Subject: [PATCH 005/176] validate products in lib/validate-profile --- lib/validate-profile.js | 16 ++++++++++++++++ parse/line.js | 9 --------- parse/products-bitmask.js | 10 ---------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 2a8ac81b..8c05ebdb 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -52,6 +52,22 @@ const validateProfile = (profile) => { throw new Error('profile.products must be an array.') } if (profile.products.length === 0) throw new Error('profile.products is empty.') + for (let product of profile.products) { + if ('string' !== typeof product.product) { + throw new Error('profile.products[].product must be a string.') + } + if ('boolean' !== typeof product.default) { + throw new Error('profile.products[].default must be a boolean.') + } + if (!Array.isArray(product.bitmasks)) { + throw new Error(product.product + '.bitmasks must be an array.') + } + for (let bitmask of product.bitmasks) { + if ('number' !== typeof bitmask) { + throw new Error(product.product + '.bitmasks[] must be a number.') + } + } + } } module.exports = validateProfile diff --git a/parse/line.js b/parse/line.js index 99e8c293..36da3f26 100644 --- a/parse/line.js +++ b/parse/line.js @@ -5,16 +5,7 @@ const slugg = require('slugg') const createParseLine = (profile, operators) => { const byBitmask = [] for (let product of profile.products) { - if ('string' !== typeof product.product) { - throw new Error('profile.products[].product must be a string.') - } - if (!Array.isArray(product.bitmasks)) { - throw new Error(product.product + '.bitmasks must be an array.') - } for (let bitmask of product.bitmasks) { - if ('number' !== typeof bitmask) { - throw new Error(product.product + '.bitmasks[] must be a number.') - } byBitmask[bitmask] = product } } diff --git a/parse/products-bitmask.js b/parse/products-bitmask.js index f8522dc6..3ebdb7d7 100644 --- a/parse/products-bitmask.js +++ b/parse/products-bitmask.js @@ -4,18 +4,8 @@ const createParseBitmask = (profile) => { const defaultProducts = {} let withBitmask = [] for (let product of profile.products) { - if ('string' !== typeof product.product) { - throw new Error('profile.products[].product must be a string.') - } - defaultProducts[product.product] = false - if (!Array.isArray(product.bitmasks)) { - throw new Error(product.product + '.bitmasks must be an array.') - } for (let bitmask of product.bitmasks) { - if ('number' !== typeof bitmask) { - throw new Error(product.product + '.bitmasks[] must be a number.') - } withBitmask.push([bitmask, product]) } } From 94bbe2361b7a405e5f781053ebbe82eab02e92cc Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 17:06:32 +0100 Subject: [PATCH 006/176] products[].product -> products[].id --- format/products-filter.js | 4 ++-- lib/validate-profile.js | 8 ++++---- p/db/products.js | 20 ++++++++++---------- p/insa/products.js | 14 +++++++------- p/oebb/products.js | 20 ++++++++++---------- p/vbb/products.js | 14 +++++++------- parse/line.js | 6 +++--- parse/products-bitmask.js | 4 ++-- test/db.js | 2 +- test/insa.js | 2 +- test/oebb.js | 2 +- 11 files changed, 48 insertions(+), 48 deletions(-) diff --git a/format/products-filter.js b/format/products-filter.js index 90b1d06b..1305f501 100644 --- a/format/products-filter.js +++ b/format/products-filter.js @@ -7,8 +7,8 @@ const createFormatProductsFilter = (profile) => { const byProduct = {} const defaultProducts = {} for (let product of profile.products) { - byProduct[product.product] = product - defaultProducts[product.product] = product.default + byProduct[product.id] = product + defaultProducts[product.id] = product.default } const formatProductsFilter = (filter) => { diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 8c05ebdb..21c86829 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -53,18 +53,18 @@ const validateProfile = (profile) => { } if (profile.products.length === 0) throw new Error('profile.products is empty.') for (let product of profile.products) { - if ('string' !== typeof product.product) { - throw new Error('profile.products[].product must be a string.') + if ('string' !== typeof product.id) { + throw new Error('profile.products[].id must be a string.') } if ('boolean' !== typeof product.default) { throw new Error('profile.products[].default must be a boolean.') } if (!Array.isArray(product.bitmasks)) { - throw new Error(product.product + '.bitmasks must be an array.') + throw new Error(product.id + '.bitmasks must be an array.') } for (let bitmask of product.bitmasks) { if ('number' !== typeof bitmask) { - throw new Error(product.product + '.bitmasks[] must be a number.') + throw new Error(product.id + '.bitmasks[] must be a number.') } } } diff --git a/p/db/products.js b/p/db/products.js index d1e26771..fa683e2b 100644 --- a/p/db/products.js +++ b/p/db/products.js @@ -3,7 +3,7 @@ // todo: https://gist.github.com/anonymous/d3323a5d2d6e159ed42b12afd0380434#file-haf_products-properties-L1-L95 module.exports = [ { - product: 'nationalExp', + id: 'nationalExp', mode: 'train', bitmasks: [1], name: 'InterCityExpress', @@ -11,7 +11,7 @@ module.exports = [ default: true }, { - product: 'national', + id: 'national', mode: 'train', bitmasks: [2], name: 'InterCity & EuroCity', @@ -19,7 +19,7 @@ module.exports = [ default: true }, { - product: 'regionalExp', + id: 'regionalExp', mode: 'train', bitmasks: [4], name: 'RegionalExpress & InterRegio', @@ -27,7 +27,7 @@ module.exports = [ default: true }, { - product: 'regional', + id: 'regional', mode: 'train', bitmasks: [8], name: 'Regio', @@ -35,7 +35,7 @@ module.exports = [ default: true }, { - product: 'suburban', + id: 'suburban', mode: 'train', bitmasks: [16], name: 'S-Bahn', @@ -43,7 +43,7 @@ module.exports = [ default: true }, { - product: 'bus', + id: 'bus', mode: 'bus', bitmasks: [32], name: 'Bus', @@ -51,7 +51,7 @@ module.exports = [ default: true }, { - product: 'ferry', + id: 'ferry', mode: 'watercraft', bitmasks: [64], name: 'Ferry', @@ -59,7 +59,7 @@ module.exports = [ default: true }, { - product: 'subway', + id: 'subway', mode: 'train', bitmasks: [128], name: 'U-Bahn', @@ -67,7 +67,7 @@ module.exports = [ default: true }, { - product: 'tram', + id: 'tram', mode: 'tram', bitmasks: [256], name: 'Tram', @@ -75,7 +75,7 @@ module.exports = [ default: true }, { - product: 'taxi', + id: 'taxi', mode: 'taxi', bitmasks: [512], name: 'Group Taxi', diff --git a/p/insa/products.js b/p/insa/products.js index 66bd63ec..485c3f23 100644 --- a/p/insa/products.js +++ b/p/insa/products.js @@ -2,7 +2,7 @@ module.exports = [ { - product: 'nationalExp', + id: 'nationalExp', mode: 'train', bitmasks: [1], name: 'InterCityExpress', @@ -10,7 +10,7 @@ module.exports = [ default: true }, { - product: 'national', + id: 'national', mode: 'train', bitmasks: [2], name: 'InterCity & EuroCity', @@ -18,7 +18,7 @@ module.exports = [ default: true }, { - product: 'regional', + id: 'regional', mode: 'train', bitmasks: [8], name: 'RegionalExpress & RegionalBahn', @@ -26,7 +26,7 @@ module.exports = [ default: true }, { - product: 'suburban', + id: 'suburban', mode: 'train', bitmasks: [16], name: 'S-Bahn', @@ -34,7 +34,7 @@ module.exports = [ default: true }, { - product: 'tram', + id: 'tram', mode: 'train', bitmasks: [32], name: 'Tram', @@ -42,7 +42,7 @@ module.exports = [ default: true }, { - product: 'bus', + id: 'bus', mode: 'bus', bitmasks: [64, 128], name: 'Bus', @@ -50,7 +50,7 @@ module.exports = [ default: true }, { - product: 'tourismTrain', + id: 'tourismTrain', mode: 'train', bitmasks: [256], name: 'Tourism Train', diff --git a/p/oebb/products.js b/p/oebb/products.js index 1c0fd223..91bcf65e 100644 --- a/p/oebb/products.js +++ b/p/oebb/products.js @@ -2,7 +2,7 @@ module.exports = [ { - product: 'nationalExp', + id: 'nationalExp', mode: 'train', bitmasks: [1], name: 'InterCityExpress & RailJet', @@ -10,7 +10,7 @@ module.exports = [ default: true }, { - product: 'national', + id: 'national', mode: 'train', bitmasks: [2, 4], name: 'InterCity & EuroCity', @@ -18,7 +18,7 @@ module.exports = [ default: true }, { - product: 'interregional', + id: 'interregional', mode: 'train', bitmasks: [8, 4096], name: 'Durchgangszug & EuroNight', @@ -26,7 +26,7 @@ module.exports = [ default: true }, { - product: 'regional', + id: 'regional', mode: 'train', bitmasks: [16], name: 'Regional & RegionalExpress', @@ -34,7 +34,7 @@ module.exports = [ default: true }, { - product: 'suburban', + id: 'suburban', mode: 'train', bitmasks: [32], name: 'S-Bahn', @@ -42,7 +42,7 @@ module.exports = [ default: true }, { - product: 'bus', + id: 'bus', mode: 'bus', bitmasks: [64], name: 'Bus', @@ -50,7 +50,7 @@ module.exports = [ default: true }, { - product: 'ferry', + id: 'ferry', mode: 'watercraft', bitmasks: [128], name: 'Ferry', @@ -58,7 +58,7 @@ module.exports = [ default: true }, { - product: 'subway', + id: 'subway', mode: 'train', bitmasks: [256], name: 'U-Bahn', @@ -66,7 +66,7 @@ module.exports = [ default: true }, { - product: 'tram', + id: 'tram', mode: 'train', bitmasks: [512], name: 'Tram', @@ -74,7 +74,7 @@ module.exports = [ default: true }, { - product: 'onCall', + id: 'onCall', mode: null, // todo bitmasks: [2048], name: 'On-call transit', diff --git a/p/vbb/products.js b/p/vbb/products.js index 678972ac..6d091247 100644 --- a/p/vbb/products.js +++ b/p/vbb/products.js @@ -2,7 +2,7 @@ module.exports = [ { - product: 'suburban', + id: 'suburban', mode: 'train', bitmasks: [1], name: 'S-Bahn', @@ -10,7 +10,7 @@ module.exports = [ default: true }, { - product: 'subway', + id: 'subway', mode: 'train', bitmasks: [2], name: 'U-Bahn', @@ -18,7 +18,7 @@ module.exports = [ default: true }, { - product: 'tram', + id: 'tram', mode: 'train', bitmasks: [4], name: 'Tram', @@ -26,7 +26,7 @@ module.exports = [ default: true }, { - product: 'bus', + id: 'bus', mode: 'bus', bitmasks: [8], name: 'Bus', @@ -34,7 +34,7 @@ module.exports = [ default: true }, { - product: 'ferry', + id: 'ferry', mode: 'watercraft', bitmasks: [16], name: 'Fähre', @@ -42,7 +42,7 @@ module.exports = [ default: true }, { - product: 'express', + id: 'express', mode: 'train', bitmasks: [32], name: 'IC/ICE', @@ -50,7 +50,7 @@ module.exports = [ default: true }, { - product: 'regional', + id: 'regional', mode: 'train', bitmasks: [64], name: 'RB/RE', diff --git a/parse/line.js b/parse/line.js index 36da3f26..fddc66e6 100644 --- a/parse/line.js +++ b/parse/line.js @@ -33,9 +33,9 @@ const createParseLine = (profile, operators) => { if ('class' in res) { // todo: what if `res.class` is the sum of two bitmasks? - const data = byBitmask[parseInt(res.class)] - res.mode = data && data.mode || null - res.product = data && data.product || null + const product = byBitmask[parseInt(res.class)] + res.mode = product && product.mode || null + res.product = product && product.id || null } if ('number' === typeof p.oprX) { diff --git a/parse/products-bitmask.js b/parse/products-bitmask.js index 3ebdb7d7..6b836904 100644 --- a/parse/products-bitmask.js +++ b/parse/products-bitmask.js @@ -4,7 +4,7 @@ const createParseBitmask = (profile) => { const defaultProducts = {} let withBitmask = [] for (let product of profile.products) { - defaultProducts[product.product] = false + defaultProducts[product.id] = false for (let bitmask of product.bitmasks) { withBitmask.push([bitmask, product]) } @@ -16,7 +16,7 @@ const createParseBitmask = (profile) => { for (let [pBitmask, product] of withBitmask) { if ((pBitmask & bitmask) > 0) { - res[product.product] = true + res[product.id] = true bitmask -= pBitmask } } diff --git a/test/db.js b/test/db.js index 62f39074..1df50fd6 100644 --- a/test/db.js +++ b/test/db.js @@ -73,7 +73,7 @@ const assertIsJungfernheide = (t, s) => { // todo: DRY with other tests const assertValidProducts = (t, p) => { for (let product of allProducts) { - product = product.product // wat + product = product.id t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') } } diff --git a/test/insa.js b/test/insa.js index 28679120..a5acfd04 100644 --- a/test/insa.js +++ b/test/insa.js @@ -58,7 +58,7 @@ const assertIsMagdeburgHbf = (t, s) => { // todo: DRY with other tests const assertValidProducts = (t, p) => { for (let product of allProducts) { - product = product.product // wat + product = product.id t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') } } diff --git a/test/oebb.js b/test/oebb.js index 7427b6db..f40d0655 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -77,7 +77,7 @@ const assertIsSalzburgHbf = (t, s) => { // todo: DRY with other tests const assertValidProducts = (t, p) => { for (let product of allProducts) { - product = product.product // wat + product = product.id t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') } } From bc51d257fd1e9ee47dd6eb94461096b2f151d02e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 17:11:21 +0100 Subject: [PATCH 007/176] adapt tests to new products markup --- test/db.js | 2 +- test/insa.js | 2 +- test/oebb.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/db.js b/test/db.js index 1df50fd6..020ae236 100644 --- a/test/db.js +++ b/test/db.js @@ -8,7 +8,7 @@ const isRoughlyEqual = require('is-roughly-equal') const co = require('./co') const createClient = require('..') const dbProfile = require('../p/db') -const {allProducts} = require('../p/db/modes') +const allProducts = require('../p/db/products') const { assertValidStation, assertValidPoi, diff --git a/test/insa.js b/test/insa.js index a5acfd04..14dd9985 100644 --- a/test/insa.js +++ b/test/insa.js @@ -8,7 +8,7 @@ const validateFptf = require('validate-fptf') const co = require('./co') const createClient = require('..') const insaProfile = require('../p/insa') -const {allProducts} = require('../p/insa/products') +const allProducts = require('../p/insa/products') const { assertValidStation, assertValidPoi, diff --git a/test/oebb.js b/test/oebb.js index f40d0655..f424d071 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -12,7 +12,7 @@ const validateLineWithoutMode = require('./validate-line-without-mode') const co = require('./co') const createClient = require('..') const oebbProfile = require('../p/oebb') -const {allProducts} = require('../p/oebb/products') +const allProducts = require('../p/oebb/products') const { assertValidStation, assertValidPoi, From 40b559f80d8b0123a2bf57f84f7fed6f8964f19b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 17:28:20 +0100 Subject: [PATCH 008/176] =?UTF-8?q?radar(n,=20w,=20s,=20e)=20->=20radar({n?= =?UTF-8?q?,=20w,=20s,=20e})=20:boom:,=20adapt=20tests=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.js | 2 +- test/insa.js | 2 +- test/oebb.js | 7 ++++++- test/vbb.js | 7 ++++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 158d0151..5a4d3be1 100644 --- a/index.js +++ b/index.js @@ -289,7 +289,7 @@ const createClient = (profile, request = _request) => { }) } - const radar = (north, west, south, east, opt) => { + const radar = ({north, west, south, east, opt}) => { if ('number' !== typeof north) throw new Error('north must be a number.') if ('number' !== typeof west) throw new Error('west must be a number.') if ('number' !== typeof south) throw new Error('south must be a number.') diff --git a/test/insa.js b/test/insa.js index 28679120..61df17fd 100644 --- a/test/insa.js +++ b/test/insa.js @@ -341,7 +341,7 @@ test('radar', co(function* (t) { const west = 11.600826 const south = 52.108486 const east = 11.651451 - const vehicles = yield client.radar(north, west, south, east, { + const vehicles = yield client.radar({north, west, south, east}, { duration: 5 * 60, when, results: 10 }) diff --git a/test/oebb.js b/test/oebb.js index 7427b6db..80e1d92e 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -450,7 +450,12 @@ test('location', co(function* (t) { })) test('radar Salzburg', co(function* (t) { - const vehicles = yield client.radar(47.827203, 13.001261, 47.773278, 13.07562, { + const vehicles = yield client.radar({ + north: 47.827203, + west: 13.001261, + south: 47.773278, + east: 13.07562 + }, { duration: 5 * 60, when }) diff --git a/test/vbb.js b/test/vbb.js index 778c432b..f2fedf6a 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -456,7 +456,12 @@ test('location', co(function* (t) { test('radar', co(function* (t) { - const vehicles = yield client.radar(52.52411, 13.41002, 52.51942, 13.41709, { + const vehicles = yield client.radar({ + north: 52.52411, + west: 13.41002, + south: 52.51942, + east: 13.41709 + }, { duration: 5 * 60, when }) From 89060afc8d25aedcecf708260124a3f40d6e2680 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Mar 2018 17:28:48 +0100 Subject: [PATCH 009/176] adapt docs and examples to 40b559f :memo: --- docs/radar.md | 9 +++++++-- p/insa/example.js | 7 ++++++- p/oebb/example.js | 7 ++++++- p/vbb/example.js | 7 ++++++- readme.md | 2 +- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/radar.md b/docs/radar.md index 281b2f77..0090b577 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -1,4 +1,4 @@ -# `radar(north, west, south, east, [opt])` +# `radar({north, west, south, east}, [opt])` Use this method to find all vehicles currently in an area. Note that it is not supported by every profile/endpoint. @@ -26,7 +26,12 @@ const vbbProfile = require('hafas-client/p/vbb') const client = createClient(vbbProfile) -client.radar(52.52411, 13.41002, 52.51942, 13.41709, {results: 5}) +client.radar({ + north: 52.52411, + west: 13.41002, + south: 52.51942, + east: 13.41709 +}, {results: 5}) .then(console.log) .catch(console.error) ``` diff --git a/p/insa/example.js b/p/insa/example.js index 054ae1f3..8cbe8679 100644 --- a/p/insa/example.js +++ b/p/insa/example.js @@ -16,7 +16,12 @@ client.journeys('008010226', '008013456', {results: 1}) // latitude: 52.148842, // longitude: 11.641705 // }, {distance: 200}) -// client.radar(52.148364, 11.600826, 52.108486, 11.651451, {results: 10}) +// client.radar({ +// north: 52.148364, +// west: 11.600826, +// south: 52.108486, +// east: 11.651451 +// }, {results: 10}) // .then(([journey]) => { // const leg = journey.legs[0] diff --git a/p/oebb/example.js b/p/oebb/example.js index 6a04412c..2dcccf39 100644 --- a/p/oebb/example.js +++ b/p/oebb/example.js @@ -11,7 +11,12 @@ client.journeys('1291501', '8100002', {results: 1}) // client.locations('Salzburg', {results: 2}) // client.location('8100173') // Graz Hbf // client.nearby(47.812851, 13.045604, {distance: 60}) -// client.radar(47.827203, 13.001261, 47.773278, 13.07562, {results: 10}) +// client.radar({ +// north: 47.827203, +// west: 13.001261, +// south: 47.773278, +// east: 13.07562 +// }, {results: 10}) .then((data) => { console.log(require('util').inspect(data, {depth: null})) diff --git a/p/vbb/example.js b/p/vbb/example.js index 079f85c3..335d83ff 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -11,7 +11,12 @@ client.journeys('900000003201', '900000024101', {results: 1}) // client.locations('Alexanderplatz', {results: 2}) // client.location('900000042101') // Spichernstr // client.nearby(52.5137344, 13.4744798, {distance: 60}) -// client.radar(52.52411, 13.41002, 52.51942, 13.41709, {results: 10}) +// client.radar({ +// north: 52.52411, +// west: 13.41002, +// south: 52.51942, +// east: 13.41709 +// }, {results: 10}) .then((data) => { console.log(require('util').inspect(data, {depth: null})) diff --git a/readme.md b/readme.md index d627004a..e5082ccb 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,7 @@ npm install hafas-client - [`locations(query, [opt])`](docs/locations.md) – find stations, POIs and addresses - [`location(id)`](docs/location.md) – get details about a location - [`nearby(location, [opt])`](docs/nearby.md) – show stations & POIs around -- [`radar(north, west, south, east, [opt])`](docs/radar.md) – find all vehicles currently in a certain area +- [`radar({north, west, south, east}, [opt])`](docs/radar.md) – find all vehicles currently in a certain area ## Usage From 83cae914a47c859ccb71e54bf1fd2d6f48748b0e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 17 Mar 2018 20:58:03 +0100 Subject: [PATCH 010/176] fix radar signature :bug: --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 5a4d3be1..76578b6f 100644 --- a/index.js +++ b/index.js @@ -289,7 +289,7 @@ const createClient = (profile, request = _request) => { }) } - const radar = ({north, west, south, east, opt}) => { + const radar = ({north, west, south, east}, opt) => { if ('number' !== typeof north) throw new Error('north must be a number.') if ('number' !== typeof west) throw new Error('west must be a number.') if ('number' !== typeof south) throw new Error('south must be a number.') From ad4d60f3ca63874fec4f490ab7428b9195fa97ed Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 18 Mar 2018 00:11:51 +0100 Subject: [PATCH 011/176] allow for overwriting parseProducts & formatProductsFilter --- index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 4ea95c84..395f4ab0 100644 --- a/index.js +++ b/index.js @@ -13,9 +13,10 @@ const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const isNonEmptyString = str => 'string' === typeof str && str.length > 0 const createClient = (profile, request = _request) => { - profile = Object.assign({}, defaultProfile, profile) - profile.parseProducts = createParseBitmask(profile) - profile.formatProductsFilter = createFormatProductsFilter(profile) + profile = Object.assign({ + parseProducts: createParseBitmask(profile), + formatProductsFilter: createFormatProductsFilter(profile) + }, defaultProfile, profile) validateProfile(profile) const departures = (station, opt = {}) => { From 854b0bd35ef75f85b5cb8eee108a3a53c01aa503 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 18 Mar 2018 00:13:44 +0100 Subject: [PATCH 012/176] homegrown isObj -> lodash/isObject --- format/products-filter.js | 3 ++- index.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/format/products-filter.js b/format/products-filter.js index 1305f501..8ee8ffca 100644 --- a/format/products-filter.js +++ b/format/products-filter.js @@ -1,6 +1,7 @@ 'use strict' -const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) +const isObj = require('lodash/isObject') + const hasProp = (o, k) => Object.prototype.hasOwnProperty.call(o, k) const createFormatProductsFilter = (profile) => { diff --git a/index.js b/index.js index 395f4ab0..70f4ae2d 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ const minBy = require('lodash/minBy') const maxBy = require('lodash/maxBy') +const isObj = require('lodash/isObject') const defaultProfile = require('./lib/default-profile') const createParseBitmask = require('./parse/products-bitmask') @@ -9,7 +10,6 @@ const createFormatProductsFilter = require('./format/products-filter') const validateProfile = require('./lib/validate-profile') const _request = require('./lib/request') -const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const isNonEmptyString = str => 'string' === typeof str && str.length > 0 const createClient = (profile, request = _request) => { From ccd2dc078346007fe91f385fc9eb364993539b85 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 18 Mar 2018 00:17:10 +0100 Subject: [PATCH 013/176] let parseProducts and formatProductsFilter get the real profile :bug: --- index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 70f4ae2d..b6c82e3a 100644 --- a/index.js +++ b/index.js @@ -12,10 +12,14 @@ const _request = require('./lib/request') const isNonEmptyString = str => 'string' === typeof str && str.length > 0 -const createClient = (profile, request = _request) => { - profile = Object.assign({ - parseProducts: createParseBitmask(profile), - formatProductsFilter: createFormatProductsFilter(profile) +const createClient = (_profile, request = _request) => { + profile = Object.assign({}, defaultProfile, profile) + if (!profile.parseProducts) { + profile.parseProducts = createParseBitmask(profile) + } + if (!profile.formatProductsFilter) { + profile.formatProductsFilter = createFormatProductsFilter(profile) + } }, defaultProfile, profile) validateProfile(profile) From 94988d98c0af6991bf861204cdb064ca600eb172 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 18 Mar 2018 00:30:54 +0100 Subject: [PATCH 014/176] yet another fix :bug: --- index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.js b/index.js index b6c82e3a..a51b805d 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ const _request = require('./lib/request') const isNonEmptyString = str => 'string' === typeof str && str.length > 0 -const createClient = (_profile, request = _request) => { +const createClient = (profile, request = _request) => { profile = Object.assign({}, defaultProfile, profile) if (!profile.parseProducts) { profile.parseProducts = createParseBitmask(profile) @@ -20,7 +20,6 @@ const createClient = (_profile, request = _request) => { if (!profile.formatProductsFilter) { profile.formatProductsFilter = createFormatProductsFilter(profile) } - }, defaultProfile, profile) validateProfile(profile) const departures = (station, opt = {}) => { From db1ad18cd5e1be41d1720324422821b6ddfb7bcf Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 19 Mar 2018 21:55:53 +0100 Subject: [PATCH 015/176] adapt writing a profile guide to #32 --- docs/writing-a-profile.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 1aca3f2e..252f31ab 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -88,37 +88,38 @@ In `hafas-client`, there's a difference between the `mode` and the `product` fie **Specify `product`s that appear in the app** you recorded requests of. For a fictional transit network, this may look like this: ```js -const products = { - commuterTrain: { - product: 'commuterTrain', +const products = [ + { + id: 'commuterTrain', mode: 'train', - bitmask: 1, + bitmasks: [16], name: 'ACME Commuter Rail', - short: 'CR' + short: 'CR', + default: true }, - metro: { - product: 'metro', + { + id: 'metro', mode: 'train', - bitmask: 2, + bitmasks: [8], name: 'Foo Bar Metro', - short: 'M' + short: 'M', + default: true } -} +] ``` Let's break this down: -- `product`: A sensible, [camelCased](https://en.wikipedia.org/wiki/Camel_case#Variations_and_synonyms), alphanumeric identifier. Use it for the key in the `products` object as well. +- `id`: A sensible, [camelCased](https://en.wikipedia.org/wiki/Camel_case#Variations_and_synonyms), alphanumeric identifier. Use it for the key in the `products` array as well. - `mode`: A [valid *Friendly Public Transport Format* `1.0.1` mode](https://github.com/public-transport/friendly-public-transport-format/blob/1.0.1/spec/readme.md#modes). -- `bitmask`: HAFAS endpoints work with a [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)#Arguments_to_functions) that toggles the individual products. the value should toggle the appropriate bit(s) in the bitmask (see below). +- `bitmasks`: HAFAS endpoints work with a [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)#Arguments_to_functions) that toggles the individual products. It should be an array of values that toggle the appropriate bit(s) in the bitmask (see below). - `name`: A short, but distinct name for the means of transport, *just precise enough in local context*, and in the local language. In Berlin, `S-Bahn-Schnellzug` would be too much, because everyone knows what `S-Bahn` means. - `short`: The shortest possible symbol that identifies the product. - -todo: `defaultProducts`, `allProducts`, `bitmasks`, add to profile +- `default`: Should the product be used for queries (e.g. journeys) by default? If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. -### Finding the right values for the `bitmask` field +### Finding the right values for the `bitmasks` field As shown in [the video](https://stuff.jannisr.de/how-to-record-hafas-requests.mp4), search for a journey and toggle off one product at a time, recording the requests. After extracting the products bitmask ([example](https://gist.github.com/derhuerst/193ef489f8aa50c2343f8bf1f2a22069#file-via-http-L34)) you will end up with values looking like these: @@ -127,8 +128,8 @@ toggles value binary subtraction bit(s) all products 31 11111 31 - 0 all but ACME Commuter Rail 15 01111 31 - 2^4 2^4 all but Foo Bar Metro 23 10111 31 - 2^3 2^3 -all but product E 30 11001 31 - 2^2 - 2^1 2^2, 2^1 -all but product F 253 11110 31 - 2^1 2^0 +all but product E 25 11001 31 - 2^2 - 2^1 2^2, 2^1 +all but product F 30 11110 31 - 2^0 2^0 ``` ## 4. Additional info From f037ddf74c15ee8b54501d2bc80649aea955c46e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 19 Mar 2018 22:01:22 +0100 Subject: [PATCH 016/176] writing a profile guide: runkit permalink --- docs/writing-a-profile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 252f31ab..8bba7cfd 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -117,7 +117,7 @@ Let's break this down: - `short`: The shortest possible symbol that identifies the product. - `default`: Should the product be used for queries (e.g. journeys) by default? -If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. +If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example/0.2.0) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. ### Finding the right values for the `bitmasks` field From 87ec80d40455a4d2a775799284a9d1c3cc13c80c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 21 Mar 2018 01:42:13 +0100 Subject: [PATCH 017/176] NAH.SH: use new products markup :bug: --- p/nahsh/index.js | 51 +------------------ p/nahsh/products.js | 117 +++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 116 deletions(-) diff --git a/p/nahsh/index.js b/p/nahsh/index.js index 92b28e5e..563f1ad2 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -1,8 +1,5 @@ 'use strict' -const createParseBitmask = require('../../parse/products-bitmask') -const createFormatBitmask = require('../../format/products-bitmask') -const _createParseLine = require('../../parse/line') const _parseLocation = require('../../parse/location') const _createParseJourney = require('../../parse/journey') @@ -38,26 +35,6 @@ const parseLocation = (profile, l, lines) => { return res } -const createParseLine = (profile, operators) => { - const parseLine = _createParseLine(profile, operators) - - const parseLineWithMode = (l) => { - const res = parseLine(l) - - res.mode = res.product = null - if ('class' in res) { - const data = products.bitmasks[parseInt(res.class)] - if (data) { - res.mode = data.mode - res.product = data.product - } - } - - return res - } - return parseLineWithMode -} - const createParseJourney = (profile, stations, lines, remarks) => { const parseJourney = _createParseJourney(profile, stations, lines, remarks) @@ -100,43 +77,17 @@ const createParseJourney = (profile, stations, lines, remarks) => { return parseJourneyWithTickets } -const defaultProducts = { - nationalExp: true, - national: true, - interregional: true, - regional: true, - suburban: true, - bus: true, - ferry: true, - subway: true, - tram: true, - onCall: true -} -const formatBitmask = createFormatBitmask(products) -const formatProducts = (products) => { - products = Object.assign(Object.create(null), defaultProducts, products) - return { - type: 'PROD', - mode: 'INC', - value: formatBitmask(products) + '' - } -} - const nahshProfile = { locale: 'de-DE', timezone: 'Europe/Berlin', endpoint: 'http://nah.sh.hafas.de/bin/mgate.exe', transformReqBody, - products: products.allProducts, + products, - parseProducts: createParseBitmask(products.allProducts, defaultProducts), - parseLine: createParseLine, parseLocation, parseJourney: createParseJourney, - formatProducts, - journeyLeg: true, radar: false // todo: see #34 } diff --git a/p/nahsh/products.js b/p/nahsh/products.js index 6d970f64..9b9f118e 100644 --- a/p/nahsh/products.js +++ b/p/nahsh/products.js @@ -1,101 +1,86 @@ 'use strict' -const p = { - nationalExp: { - bitmask: 1, +const p = [ + { + id: 'nationalExp', + mode: 'train', + bitmasks: [1], name: 'High-speed rail', short: 'ICE/HSR', - mode: 'train', - product: 'nationalExp' + default: true }, - national: { - bitmask: 2, + { + id: 'national', + mode: 'train', + bitmasks: [2], name: 'InterCity & EuroCity', short: 'IC/EC', - mode: 'train', - product: 'national' + default: true }, - interregional: { // todo: also includes EN? - bitmask: 4, + { // todo: also includes EN? + id: 'interregional', + mode: 'train', + bitmasks: [4], name: 'Interregional', short: 'IR', - mode: 'train', - product: 'interregional' + default: true }, - regional: { - bitmask: 8, + { + id: 'regional', + mode: 'train', + bitmasks: [8], name: 'Regional & RegionalExpress', short: 'RB/RE', - mode: 'train', - product: 'regional' + default: true }, - suburban: { - bitmask: 16, + { + id: 'suburban', + mode: 'train', + bitmasks: [16], name: 'S-Bahn', short: 'S', - mode: 'train', - product: 'suburban' + default: true }, - bus: { - bitmask: 32, + { + id: 'bus', + mode: 'bus', + bitmasks: [32], name: 'Bus', short: 'B', - mode: 'bus', - product: 'bus' + default: true }, - ferry: { - bitmask: 64, + { + id: 'ferry', + mode: 'watercraft', + bitmasks: [64], name: 'Ferry', short: 'F', - mode: 'watercraft', - product: 'ferry' + default: true }, - subway: { - bitmask: 128, + { + id: 'subway', + mode: 'train', + bitmasks: [128], name: 'U-Bahn', short: 'U', - mode: 'train', - product: 'subway' + default: true }, - tram: { - bitmask: 256, + { + id: 'tram', + mode: 'train', + bitmasks: [256], name: 'Tram', short: 'T', - mode: 'train', - product: 'tram' + default: true }, - onCall: { - bitmask: 512, + { + id: 'onCall', + mode: null, // todo + bitmasks: [512], name: 'On-call transit', short: 'on-call', - mode: null, // todo - product: 'onCall' + default: true } -} - -p.bitmasks = [] -p.bitmasks[1] = p.nationalExp -p.bitmasks[2] = p.national -p.bitmasks[4] = p.interregional -p.bitmasks[8] = p.regional -p.bitmasks[16] = p.suburban -p.bitmasks[32] = p.bus -p.bitmasks[64] = p.ferry -p.bitmasks[128] = p.subway -p.bitmasks[256] = p.tram -p.bitmasks[512] = p.onCall - -p.allProducts = [ - p.nationalExp, - p.national, - p.interregional, - p.regional, - p.suburban, - p.bus, - p.ferry, - p.subway, - p.tram, - p.onCall ] module.exports = p From 005f3f8e4d2305436d6b9cdbd4dab6b71a25f78b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 21 Mar 2018 01:15:43 +0100 Subject: [PATCH 018/176] =?UTF-8?q?remove=20journey.departure,=20.arrival,?= =?UTF-8?q?=20=E2=80=A6=20:boom:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes #36 --- index.js | 2 +- parse/journey.js | 20 +------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/index.js b/index.js index 96e8c9a0..75ddf42a 100644 --- a/index.js +++ b/index.js @@ -157,7 +157,7 @@ const createClient = (profile, request = _request) => { journeys.laterRef = d.outCtxScrF return journeys } - const dep = +new Date(j.departure) + const dep = +new Date(j.legs[0].departure) if (dep > latestDep) latestDep = dep } diff --git a/parse/journey.js b/parse/journey.js index b0a133e9..09155696 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -8,31 +8,13 @@ const createParseJourney = (profile, stations, lines, remarks) => { const parseLeg = createParseJourneyLeg(profile, stations, lines, remarks) // todo: c.sDays - // todo: c.dep.dProgType, c.arr.dProgType // todo: c.conSubscr // todo: c.trfRes x vbb-parse-ticket const parseJourney = (j) => { const legs = j.secL.map(leg => parseLeg(j, leg)) const res = { type: 'journey', - legs, - origin: legs[0].origin, - destination: legs[legs.length - 1].destination, - departure: legs[0].departure, - arrival: legs[legs.length - 1].arrival - } - if (legs.some(p => p.cancelled)) { - res.cancelled = true - Object.defineProperty(res, 'canceled', {value: true}) - res.departure = res.arrival = null - - const firstLeg = j.secL[0] - const dep = profile.parseDateTime(profile, j.date, firstLeg.dep.dTimeS) - res.formerScheduledDeparture = dep.toISO() - - const lastLeg = j.secL[j.secL.length - 1] - const arr = profile.parseDateTime(profile, j.date, lastLeg.arr.aTimeS) - res.formerScheduledArrival = arr.toISO() + legs } return res From 4cba773eb0f0d0c7d5a8d79c8bb4dfb237a12705 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 21 Mar 2018 01:53:15 +0100 Subject: [PATCH 019/176] adapt tests to 0e80be0 :white_check_mark: --- test/db.js | 28 ++++------------------------ test/insa.js | 16 +--------------- test/nahsh.js | 32 +++++--------------------------- test/oebb.js | 30 ++++-------------------------- test/vbb.js | 19 ++++--------------- 5 files changed, 18 insertions(+), 107 deletions(-) diff --git a/test/db.js b/test/db.js index 020ae236..53b29e34 100644 --- a/test/db.js +++ b/test/db.js @@ -109,29 +109,9 @@ test('Berlin Jungfernheide to München Hbf', co(function* (t) { for (let journey of journeys) { t.equal(journey.type, 'journey') - assertValidStation(t, journey.origin) - assertValidStationProducts(t, journey.origin.products) - if (!(yield findStation(journey.origin.id))) { - console.error('unknown station', journey.origin.id, journey.origin.name) - } - if (journey.origin.products) { - assertValidProducts(t, journey.origin.products) - } - assertValidWhen(t, journey.departure, when) - - assertValidStation(t, journey.destination) - assertValidStationProducts(t, journey.origin.products) - if (!(yield findStation(journey.origin.id))) { - console.error('unknown station', journey.destination.id, journey.destination.name) - } - if (journey.destination.products) { - assertValidProducts(t, journey.destination.products) - } - assertValidWhen(t, journey.arrival, when) - t.ok(Array.isArray(journey.legs)) t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] + const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) assertValidStationProducts(t, leg.origin.products) @@ -284,7 +264,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { let earliestDep = Infinity, latestDep = -Infinity for (let j of model) { - const dep = +new Date(j.departure) + const dep = +new Date(j.legs[0].departure) if (dep < earliestDep) earliestDep = dep else if (dep > latestDep) latestDep = dep } @@ -295,7 +275,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { earlierThan: model.earlierRef }) for (let j of earlier) { - t.ok(new Date(j.departure) < earliestDep) + t.ok(new Date(j.legs[0].departure) < earliestDep) } const later = yield client.journeys(jungfernh, münchenHbf, { @@ -304,7 +284,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { laterThan: model.laterRef }) for (let j of later) { - t.ok(new Date(j.departure) > latestDep) + t.ok(new Date(j.legs[0].departure) > latestDep) } t.end() diff --git a/test/insa.js b/test/insa.js index 3afa29ee..3830c809 100644 --- a/test/insa.js +++ b/test/insa.js @@ -77,23 +77,9 @@ test('Magdeburg Hbf to Magdeburg-Buckau', co(function*(t) { t.ok(Array.isArray(journeys)) t.ok(journeys.length > 0, 'no journeys') for (let journey of journeys) { - assertValidStation(t, journey.origin) - assertValidStationProducts(t, journey.origin.products) - if (journey.origin.products) { - assertValidProducts(t, journey.origin.products) - } - assertValidWhen(t, journey.departure, when) - - assertValidStation(t, journey.destination) - assertValidStationProducts(t, journey.origin.products) - if (journey.destination.products) { - assertValidProducts(t, journey.destination.products) - } - assertValidWhen(t, journey.arrival, when) - t.ok(Array.isArray(journey.legs)) t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] + const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) assertValidStationProducts(t, leg.origin.products) diff --git a/test/nahsh.js b/test/nahsh.js index 0b8b2b04..c125274b 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -12,7 +12,7 @@ const validateLineWithoutMode = require('./validate-line-without-mode') const co = require('./co') const createClient = require('..') const nahshProfile = require('../p/nahsh') -const {allProducts} = require('../p/nahsh/products') +const allProducts = require('../p/nahsh/products') const { assertValidStation, assertValidPoi, @@ -108,31 +108,9 @@ test('Kiel Hbf to Flensburg', co(function* (t) { for (let journey of journeys) { t.equal(journey.type, 'journey') - assertValidStation(t, journey.origin) - assertValidStationProducts(t, journey.origin.products) - // todo - // if (!(yield findStation(journey.origin.id))) { - // console.error('unknown station', journey.origin.id, journey.origin.name) - // } - if (journey.origin.products) { - assertValidProducts(t, journey.origin.products) - } - assertValidWhen(t, journey.departure, when) - - assertValidStation(t, journey.destination) - assertValidStationProducts(t, journey.origin.products) - // todo - // if (!(yield findStation(journey.origin.id))) { - // console.error('unknown station', journey.destination.id, journey.destination.name) - // } - if (journey.destination.products) { - assertValidProducts(t, journey.destination.products) - } - assertValidWhen(t, journey.arrival, when) - t.ok(Array.isArray(journey.legs)) t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] + const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) assertValidStationProducts(t, leg.origin.products) @@ -279,7 +257,7 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { let earliestDep = Infinity, latestDep = -Infinity for (let j of model) { - const dep = +new Date(j.departure) + const dep = +new Date(j.legs[0].departure) if (dep < earliestDep) earliestDep = dep else if (dep > latestDep) latestDep = dep } @@ -290,7 +268,7 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { earlierThan: model.earlierRef }) for (let j of earlier) { - t.ok(new Date(j.departure) < earliestDep) + t.ok(new Date(j.legs[0].departure) < earliestDep) } const later = yield client.journeys(kielHbf, flensburg, { @@ -299,7 +277,7 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { laterThan: model.laterRef }) for (let j of later) { - t.ok(new Date(j.departure) > latestDep) + t.ok(new Date(j.legs[0].departure) > latestDep) } t.end() diff --git a/test/oebb.js b/test/oebb.js index 319f79f6..feae6c65 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -128,31 +128,9 @@ test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) { for (let journey of journeys) { t.equal(journey.type, 'journey') - assertValidStation(t, journey.origin) - assertValidStationProducts(t, journey.origin.products) - // todo - // if (!(yield findStation(journey.origin.id))) { - // console.error('unknown station', journey.origin.id, journey.origin.name) - // } - if (journey.origin.products) { - assertValidProducts(t, journey.origin.products) - } - assertValidWhen(t, journey.departure, when) - - assertValidStation(t, journey.destination) - assertValidStationProducts(t, journey.origin.products) - // todo - // if (!(yield findStation(journey.origin.id))) { - // console.error('unknown station', journey.destination.id, journey.destination.name) - // } - if (journey.destination.products) { - assertValidProducts(t, journey.destination.products) - } - assertValidWhen(t, journey.arrival, when) - t.ok(Array.isArray(journey.legs)) t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] + const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) assertValidStationProducts(t, leg.origin.products) @@ -327,7 +305,7 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t let earliestDep = Infinity, latestDep = -Infinity for (let j of model) { - const dep = +new Date(j.departure) + const dep = +new Date(j.legs[0].departure) if (dep < earliestDep) earliestDep = dep else if (dep > latestDep) latestDep = dep } @@ -338,7 +316,7 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t earlierThan: model.earlierRef }) for (let j of earlier) { - t.ok(new Date(j.departure) < earliestDep) + t.ok(new Date(j.legs[0].departure) < earliestDep) } const later = yield client.journeys(salzburgHbf, wienWestbahnhof, { @@ -347,7 +325,7 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t laterThan: model.laterRef }) for (let j of later) { - t.ok(new Date(j.departure) > latestDep) + t.ok(new Date(j.legs[0].departure) > latestDep) } t.end() diff --git a/test/vbb.js b/test/vbb.js index f2fedf6a..c94dc9d5 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -69,20 +69,9 @@ test('journeys – station to station', co(function* (t) { for (let journey of journeys) { t.equal(journey.type, 'journey') - assertValidStation(t, journey.origin) - assertValidStationProducts(t, journey.origin.products) - t.ok(journey.origin.name.indexOf('(Berlin)') === -1) - t.strictEqual(journey.origin.id, spichernstr) - assertValidWhen(t, journey.departure, when) - - assertValidStation(t, journey.destination) - assertValidStationProducts(t, journey.destination.products) - t.strictEqual(journey.destination.id, amrumerStr) - assertValidWhen(t, journey.arrival, when) - t.ok(Array.isArray(journey.legs)) t.strictEqual(journey.legs.length, 1) - const leg = journey.legs[0] + const leg = journey.legs[0] // todo: all legs t.equal(typeof leg.id, 'string') t.ok(leg.id) @@ -192,7 +181,7 @@ test('earlier/later journeys', co(function* (t) { let earliestDep = Infinity, latestDep = -Infinity for (let j of model) { - const dep = +new Date(j.departure) + const dep = +new Date(j.legs[0].departure) if (dep < earliestDep) earliestDep = dep else if (dep > latestDep) latestDep = dep } @@ -203,7 +192,7 @@ test('earlier/later journeys', co(function* (t) { earlierThan: model.earlierRef }) for (let j of earlier) { - t.ok(new Date(j.departure) < earliestDep) + t.ok(new Date(j.legs[0].departure) < earliestDep) } const later = yield client.journeys(spichernstr, bismarckstr, { @@ -212,7 +201,7 @@ test('earlier/later journeys', co(function* (t) { laterThan: model.laterRef }) for (let j of later) { - t.ok(new Date(j.departure) > latestDep) + t.ok(new Date(j.legs[0].departure) > latestDep) } t.end() From 2430a74414e763a7e6f46034787deaa6e14a1542 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 23 Mar 2018 21:19:25 +0100 Subject: [PATCH 020/176] adapt docs to 005f3f8 :memo: --- docs/journeys.md | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/docs/journeys.md b/docs/journeys.md index 152d408d..e101cbf5 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -187,24 +187,7 @@ The response may look like this: arrival: '2017-12-17T19:17:00.000+01:00', departure: '2017-12-17T19:17:00.000+01:00' } ] - } ], - origin: { - type: 'station', - id: '900000003201', - name: 'S+U Berlin Hauptbahnhof', - location: { /* … */ }, - products: { /* … */ } - }, - departure: '2017-12-17T19:07:00.000+01:00', - destination: { - type: 'station', - id: '900000024101', - name: 'S Charlottenburg', - location: { /* … */ }, - products: { /* … */ } - }, - arrival: '2017-12-17T19:47:00.000+01:00', - arrivalDelay: 30 + } ] }, earlierRef: '…', // use with the `earlierThan` option laterRef: '…' // use with the `laterThan` option @@ -259,16 +242,16 @@ const heinrichHeineStr = '900000100008' client.journeys(hbf, heinrichHeineStr) .then((journeys) => { const lastJourney = journeys[journeys.length - 1] - console.log('departure of last journey', lastJourney.departure) + console.log('departure of last journey', lastJourney.legs[0].departure) // get later journeys return client.journeys(hbf, heinrichHeineStr, { laterThan: journeys.laterRef }) }) -.then((laterourneys) => { - const firstJourney = laterourneys[laterourneys.length - 1] - console.log('departure of first (later) journey', firstJourney.departure) +.then((laterJourneys) => { + const firstJourney = laterourneys[laterJourneys.length - 1] + console.log('departure of first (later) journey', firstJourney.legs[0].departure) }) .catch(console.error) ``` From 27085d1db075566be9dd124a521c2eecf4960443 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 24 Mar 2018 18:18:34 +0100 Subject: [PATCH 021/176] add simple profile boilerplate --- docs/profile-boilerplate.js | 44 +++++++++++++++++++++++++++++++++++++ docs/writing-a-profile.md | 2 ++ 2 files changed, 46 insertions(+) create mode 100644 docs/profile-boilerplate.js diff --git a/docs/profile-boilerplate.js b/docs/profile-boilerplate.js new file mode 100644 index 00000000..6b8ae7b0 --- /dev/null +++ b/docs/profile-boilerplate.js @@ -0,0 +1,44 @@ +'use strict' + +// see the ./writing-a-profile.md guide +const products = [ + { + id: 'nationalExp', + mode: 'train', + bitmasks: [1], + name: 'InterCityExpress', + short: 'ICE', + default: true + }, + { + id: 'national', + mode: 'train', + bitmasks: [2], + name: 'InterCity & EuroCity', + short: 'IC/EC', + default: true + } +] + +const transformReqBody = (body) => { + // get these from the recorded app requests + // body.client = { … } + // body.ver = … + // body.auth = { … } + // body.lang = … + return body +} + +const insaProfile = { + // locale: …, + // timezone: …, + // endpoint: …, + transformReqBody, + + products: products, + + journeyLeg: false, + radar: false +} + +module.exports = insaProfile diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 8bba7cfd..d05caebb 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -78,6 +78,8 @@ If you pass this profile into `hafas-client`, the `parseLine` method will overri - Add a function `transformReqBody(body)` to your profile, which assigns them to `body`. - Some profiles have a `checksum` parameter (like [here](https://gist.github.com/derhuerst/2a735268bd82a0a6779633f15dceba33#file-journey-details-1-http-L1)) or two `mic` & `mac` parameters (like [here](https://gist.github.com/derhuerst/5fa86ed5aec63645e5ae37e23e555886#file-1-http-L1)). If you see one of them in your requests, jump to [*Appendix A: checksum, mic, mac*](#appendix-a-checksum-mic-mac). Unfortunately, this is necessary to get the profile working. +You may want to use the [profile boilerplate code](profile-boilerplate.js). + ## 3. Products In `hafas-client`, there's a difference between the `mode` and the `product` field: From d96cbfb228a2ebbce5aaccbd59b420f160a8f9a8 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 29 Mar 2018 02:45:15 +0200 Subject: [PATCH 022/176] stopover: default arrival & departure fields :bug:, >= for collecting journeys --- index.js | 2 +- parse/stopover.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 75ddf42a..7d0482b7 100644 --- a/index.js +++ b/index.js @@ -153,7 +153,7 @@ const createClient = (profile, request = _request) => { j = parse(j) journeys.push(j) - if (journeys.length === opt.results) { // collected enough + if (journeys.length >= opt.results) { // collected enough journeys.laterRef = d.outCtxScrF return journeys } diff --git a/parse/stopover.js b/parse/stopover.js index 2a445bd6..86d12726 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -5,7 +5,9 @@ const createParseStopover = (profile, stations, lines, remarks, date) => { const parseStopover = (st) => { const res = { - station: stations[parseInt(st.locX)] || null + station: stations[parseInt(st.locX)] || null, + arrival: null, + departure: null } if (st.aTimeR || st.aTimeS) { const arr = profile.parseDateTime(profile, date, st.aTimeR || st.aTimeS) From 046b8c4117894bf23544a40f244f884caaf72744 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 29 Mar 2018 23:17:50 +0200 Subject: [PATCH 023/176] VBB: strip "Bus " from line names --- p/vbb/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/p/vbb/index.js b/p/vbb/index.js index 8ce5fde2..bcff49a9 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -29,7 +29,8 @@ const createParseLine = (profile, operators) => { const parseLineWithMoreDetails = (l) => { const res = parseLine(l) - const details = parseLineName(l.name) + res.name = l.name.replace(/^bus\s+/i, '') + const details = parseLineName(res.name) res.symbol = details.symbol res.nr = details.nr res.metro = details.metro From 4fd7108ec6efff6c2cf4f8b30775b1944fd2289d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 31 Mar 2018 14:03:23 +0200 Subject: [PATCH 024/176] VBB: strip "Tram " from line names --- p/vbb/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/vbb/index.js b/p/vbb/index.js index bcff49a9..8a3539cd 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -29,7 +29,7 @@ const createParseLine = (profile, operators) => { const parseLineWithMoreDetails = (l) => { const res = parseLine(l) - res.name = l.name.replace(/^bus\s+/i, '') + res.name = l.name.replace(/^(bus|tram)\s+/i, '') const details = parseLineName(res.name) res.symbol = details.symbol res.nr = details.nr From 03e5cd350d09243083a984f99e920b8f4992791e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Apr 2018 14:17:30 +0200 Subject: [PATCH 025/176] async stack traces: NODE_ENV=dev -> NODE_DEBUG=hafas-client --- lib/request.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/request.js b/lib/request.js index 96d5da5f..aaca1558 100644 --- a/lib/request.js +++ b/lib/request.js @@ -2,7 +2,7 @@ const crypto = require('crypto') let captureStackTrace = () => {} -if (process.env.NODE_ENV === 'dev') { +if (process.env.NODE_DEBUG === 'hafas-client') { captureStackTrace = require('capture-stack-trace') } const {stringify} = require('query-string') diff --git a/package.json b/package.json index 90938153..9c305030 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "vbb-stations-autocomplete": "^3.1.0" }, "scripts": { - "test": "env NODE_ENV=dev node test/index.js", + "test": "env NODE_ENV=dev NODE_DEBUG=hafas-client node test/index.js", "prepublishOnly": "npm test | tap-spec" } } From 3680f1fe94692386e85e362dee72af20bcb64f94 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Apr 2018 19:37:44 +0200 Subject: [PATCH 026/176] fix products bitmask parsing :bug: --- parse/products-bitmask.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/products-bitmask.js b/parse/products-bitmask.js index 133466c6..c1fb9b69 100644 --- a/parse/products-bitmask.js +++ b/parse/products-bitmask.js @@ -20,7 +20,7 @@ const createParseBitmask = (profile) => { bitmask -= pBitmask } else{ - res[product.product] = false + res[product.id] = false } } From 8ce89f0fe6fd7f7ad53b74d5c22571872237059f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 17 Apr 2018 18:43:42 +0200 Subject: [PATCH 027/176] formatLocation: more helpful error messages --- format/location.js | 7 ++++--- index.js | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/format/location.js b/format/location.js index 121cd13f..aa7d19d1 100644 --- a/format/location.js +++ b/format/location.js @@ -1,14 +1,15 @@ 'use strict' -const formatLocation = (profile, l) => { +const formatLocation = (profile, l, name = 'location') => { if ('string' === typeof l) return profile.formatStation(l) if ('object' === typeof l && !Array.isArray(l)) { if (l.type === 'station') return profile.formatStation(l.id) if ('string' === typeof l.id) return profile.formatPoi(l) if ('string' === typeof l.address) return profile.formatAddress(l) - throw new Error('invalid location type: ' + l.type) + if (!l.type) throw new Error(`missing ${name}.type`) + throw new Error(`invalid ${name}type: ${l.type}`) } - throw new Error('valid station, address or poi required.') + throw new Error(name + ': valid station, address or poi required.') } module.exports = formatLocation diff --git a/index.js b/index.js index 7d0482b7..7ea60620 100644 --- a/index.js +++ b/index.js @@ -57,8 +57,8 @@ const createClient = (profile, request = _request) => { } const journeys = (from, to, opt = {}) => { - from = profile.formatLocation(profile, from) - to = profile.formatLocation(profile, to) + from = profile.formatLocation(profile, from, 'from') + to = profile.formatLocation(profile, to, 'to') if (('earlierThan' in opt) && ('laterThan' in opt)) { throw new Error('opt.laterThan and opt.laterThan are mutually exclusive.') @@ -94,7 +94,7 @@ const createClient = (profile, request = _request) => { bike: false, // only bike-friendly journeys tickets: false, // return tickets? }, opt) - if (opt.via) opt.via = profile.formatLocation(profile, opt.via) + if (opt.via) opt.via = profile.formatLocation(profile, opt.via, 'opt.via') opt.when = opt.when || new Date() const filters = [ From 41b574c4681d2d9f577acbe2c57eaf23f5fbbac9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 18 Apr 2018 15:53:08 +0200 Subject: [PATCH 028/176] tests: move assertValidStationProducts into assertValidStation --- test/db.js | 40 ++++++++++--------------------------- test/insa.js | 43 ++++++++++------------------------------ test/nahsh.js | 42 ++++++++++----------------------------- test/oebb.js | 55 +++++++++++++++++---------------------------------- test/vbb.js | 26 ++++++++---------------- 5 files changed, 56 insertions(+), 150 deletions(-) diff --git a/test/db.js b/test/db.js index 53b29e34..d4e1c97d 100644 --- a/test/db.js +++ b/test/db.js @@ -10,7 +10,7 @@ const createClient = require('..') const dbProfile = require('../p/db') const allProducts = require('../p/db/products') const { - assertValidStation, + assertValidStation: _assertValidStation, assertValidPoi, assertValidAddress, assertValidLocation, @@ -21,18 +21,15 @@ const { const when = createWhen('Europe/Berlin', 'de-DE') -const assertValidStationProducts = (t, p) => { - t.ok(p) - t.equal(typeof p.nationalExp, 'boolean') - t.equal(typeof p.national, 'boolean') - t.equal(typeof p.regionalExp, 'boolean') - t.equal(typeof p.regional, 'boolean') - t.equal(typeof p.suburban, 'boolean') - t.equal(typeof p.bus, 'boolean') - t.equal(typeof p.ferry, 'boolean') - t.equal(typeof p.subway, 'boolean') - t.equal(typeof p.tram, 'boolean') - t.equal(typeof p.taxi, 'boolean') +// todo: DRY with other tests, move into lib +const assertValidStation = (t, s) => { + _assertValidStation(t, s) + t.ok(s.products) + for (let product of allProducts) { + product = product.id + const msg = `station.products[${product}] must be a boolean` + t.equal(typeof s.products[product], 'boolean', msg) + } } const findStation = (id) => new Promise((yay, nay) => { @@ -69,15 +66,6 @@ const assertIsJungfernheide = (t, s) => { t.ok(isRoughlyEqual(s.location.longitude, 13.299424, .0005)) } -// todo: DRY with assertValidStationProducts -// todo: DRY with other tests -const assertValidProducts = (t, p) => { - for (let product of allProducts) { - product = product.id - t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') - } -} - const assertValidPrice = (t, p) => { t.ok(p) if (p.amount !== null) { @@ -114,7 +102,6 @@ test('Berlin Jungfernheide to München Hbf', co(function* (t) { const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) if (!(yield findStation(leg.origin.id))) { console.error('unknown station', leg.origin.id, leg.origin.name) } @@ -122,7 +109,6 @@ test('Berlin Jungfernheide to München Hbf', co(function* (t) { t.equal(typeof leg.departurePlatform, 'string') assertValidStation(t, leg.destination) - assertValidStationProducts(t, leg.origin.products) if (!(yield findStation(leg.destination.id))) { console.error('unknown station', leg.destination.id, leg.destination.name) } @@ -152,11 +138,9 @@ test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) { const leg = journey.legs[journey.legs.length - 1] assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) if (!(yield findStation(leg.origin.id))) { console.error('unknown station', leg.origin.id, leg.origin.name) } - if (leg.origin.products) assertValidProducts(t, leg.origin.products) assertValidWhen(t, leg.departure, when) assertValidWhen(t, leg.arrival, when) @@ -181,11 +165,9 @@ test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { const leg = journey.legs[journey.legs.length - 1] assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) if (!(yield findStation(leg.origin.id))) { console.error('unknown station', leg.origin.id, leg.origin.name) } - if (leg.origin.products) assertValidProducts(t, leg.origin.products) assertValidWhen(t, leg.departure, when) assertValidWhen(t, leg.arrival, when) @@ -298,11 +280,9 @@ test('departures at Berlin Jungfernheide', co(function* (t) { t.ok(Array.isArray(deps)) for (let dep of deps) { assertValidStation(t, dep.station) - assertValidStationProducts(t, dep.station.products) if (!(yield findStation(dep.station.id))) { console.error('unknown station', dep.station.id, dep.station.name) } - if (dep.station.products) assertValidProducts(t, dep.station.products) assertValidWhen(t, dep.when, when) } diff --git a/test/insa.js b/test/insa.js index 3830c809..6192120e 100644 --- a/test/insa.js +++ b/test/insa.js @@ -10,7 +10,7 @@ const createClient = require('..') const insaProfile = require('../p/insa') const allProducts = require('../p/insa/products') const { - assertValidStation, + assertValidStation: _assertValidStation, assertValidPoi, assertValidAddress, assertValidLocation, @@ -23,15 +23,15 @@ const { const when = createWhen('Europe/Berlin', 'de-DE') -const assertValidStationProducts = (t, p) => { - t.ok(p) - t.equal(typeof p.nationalExp, 'boolean') - t.equal(typeof p.national, 'boolean') - t.equal(typeof p.regional, 'boolean') - t.equal(typeof p.suburban, 'boolean') - t.equal(typeof p.tram, 'boolean') - t.equal(typeof p.bus, 'boolean') - t.equal(typeof p.tourismTrain, 'boolean') +// todo: DRY with other tests, move into lib +const assertValidStation = (t, s, coordsOptional = false) => { + _assertValidStation(t, s, coordsOptional) + t.ok(s.products) + for (let product of allProducts) { + product = product.id + const msg = `station.products[${product}] must be a boolean` + t.equal(typeof s.products[product], 'boolean', msg) + } } const isMagdeburgHbf = s => { @@ -54,15 +54,6 @@ const assertIsMagdeburgHbf = (t, s) => { t.ok(isRoughlyEqual(s.location.longitude, 11.626891, 0.001)) } -// todo: DRY with assertValidStationProducts -// todo: DRY with other tests -const assertValidProducts = (t, p) => { - for (let product of allProducts) { - product = product.id - t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') - } -} - const test = tapePromise(tape) const client = createClient(insaProfile) @@ -82,12 +73,10 @@ test('Magdeburg Hbf to Magdeburg-Buckau', co(function*(t) { const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) assertValidWhen(t, leg.departure, when) t.equal(typeof leg.departurePlatform, 'string') assertValidStation(t, leg.destination) - assertValidStationProducts(t, leg.origin.products) assertValidWhen(t, leg.arrival, when) t.equal(typeof leg.arrivalPlatform, 'string') @@ -120,9 +109,6 @@ test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { const lastLeg = journey.legs[journey.legs.length - 1] assertValidStation(t, firstLeg.origin) - assertValidStationProducts(t, firstLeg.origin.products) - if (firstLeg.origin.products) - assertValidProducts(t, firstLeg.origin.products) assertValidWhen(t, firstLeg.departure, when) assertValidWhen(t, firstLeg.arrival, when) assertValidWhen(t, lastLeg.departure, when) @@ -168,9 +154,6 @@ test('Kloster Unser Lieben Frauen to Magdeburg Hbf', co(function*(t) { assertValidWhen(t, lastLeg.arrival, when) assertValidStation(t, lastLeg.destination) - assertValidStationProducts(t, lastLeg.destination.products) - if (lastLeg.destination.products) - assertValidProducts(t, lastLeg.destination.products) t.end() })) @@ -230,10 +213,6 @@ test('departures at Magdeburg Hbf', co(function*(t) { t.ok(Array.isArray(deps)) for (let dep of deps) { assertValidStation(t, dep.station) - assertValidStationProducts(t, dep.station.products) - if (dep.station.products) { - assertValidProducts(t, dep.station.products) - } assertValidWhen(t, dep.when, when) } @@ -366,9 +345,7 @@ test('radar', co(function* (t) { // see #28 // todo: check if this works by now assertValidStation(t, f.origin, true) - assertValidStationProducts(t, f.origin.products) assertValidStation(t, f.destination, true) - assertValidStationProducts(t, f.destination.products) t.equal(typeof f.t, 'number') } } diff --git a/test/nahsh.js b/test/nahsh.js index c125274b..6b23347a 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -14,7 +14,7 @@ const createClient = require('..') const nahshProfile = require('../p/nahsh') const allProducts = require('../p/nahsh/products') const { - assertValidStation, + assertValidStation: _assertValidStation, assertValidPoi, assertValidAddress, assertValidLocation, @@ -24,18 +24,15 @@ const { const when = createWhen('Europe/Berlin', 'de-DE') -const assertValidStationProducts = (t, p) => { - t.ok(p) - t.equal(typeof p.nationalExp, 'boolean') - t.equal(typeof p.national, 'boolean') - t.equal(typeof p.interregional, 'boolean') - t.equal(typeof p.regional, 'boolean') - t.equal(typeof p.suburban, 'boolean') - t.equal(typeof p.bus, 'boolean') - t.equal(typeof p.ferry, 'boolean') - t.equal(typeof p.subway, 'boolean') - t.equal(typeof p.tram, 'boolean') - t.equal(typeof p.onCall, 'boolean') +// todo: DRY with other tests, move into lib +const assertValidStation = (t, s, coordsOptional = false) => { + _assertValidStation(t, s, coordsOptional) + t.ok(s.products) + for (let product of allProducts) { + product = product.id + const msg = `station.products[${product}] must be a boolean` + t.equal(typeof s.products[product], 'boolean', msg) + } } const isKielHbf = (s) => { @@ -56,15 +53,6 @@ const assertIsKielHbf = (t, s) => { t.ok(isRoughlyEqual(s.location.longitude, 10.131976, .0005)) } -// todo: DRY with assertValidStationProducts -// todo: DRY with other tests -const assertValidProducts = (t, p) => { - for (let product of allProducts) { - product = product.product // wat - t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') - } -} - const assertValidPrice = (t, p) => { t.ok(p) if (p.amount !== null) { @@ -113,7 +101,6 @@ test('Kiel Hbf to Flensburg', co(function* (t) { const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) // todo // if (!(yield findStation(leg.origin.id))) { // console.error('unknown station', leg.origin.id, leg.origin.name) @@ -122,7 +109,6 @@ test('Kiel Hbf to Flensburg', co(function* (t) { t.equal(typeof leg.departurePlatform, 'string') assertValidStation(t, leg.destination) - assertValidStationProducts(t, leg.origin.products) // todo // if (!(yield findStation(leg.destination.id))) { // console.error('unknown station', leg.destination.id, leg.destination.name) @@ -158,12 +144,10 @@ test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { const lastLeg = journey.legs[journey.legs.length - 1] assertValidStation(t, firstLeg.origin) - assertValidStationProducts(t, firstLeg.origin.products) // todo // if (!(yield findStation(leg.origin.id))) { // console.error('unknown station', leg.origin.id, leg.origin.name) // } - if (firstLeg.origin.products) assertValidProducts(t, firstLeg.origin.products) assertValidWhen(t, firstLeg.departure, when) assertValidWhen(t, firstLeg.arrival, when) assertValidWhen(t, lastLeg.departure, when) @@ -206,8 +190,6 @@ test('Holstentor to Kiel Hbf', co(function* (t) { assertValidWhen(t, lastLeg.arrival, when) assertValidStation(t, lastLeg.destination) - assertValidStationProducts(t, lastLeg.destination.products) - if (lastLeg.destination.products) assertValidProducts(t, lastLeg.destination.products) // todo // if (!(yield findStation(leg.destination.id))) { // console.error('unknown station', leg.destination.id, leg.destination.name) @@ -315,12 +297,10 @@ test('departures at Kiel Hbf', co(function* (t) { t.ok(Array.isArray(deps)) for (let dep of deps) { assertValidStation(t, dep.station) - assertValidStationProducts(t, dep.station.products) // todo // if (!(yield findStation(dep.station.id))) { // console.error('unknown station', dep.station.id, dep.station.name) // } - if (dep.station.products) assertValidProducts(t, dep.station.products) assertValidWhen(t, dep.when, when) } @@ -420,9 +400,7 @@ test.skip('radar Kiel', co(function* (t) { t.ok(Array.isArray(v.frames)) for (let f of v.frames) { assertValidStation(t, f.origin, true) - assertValidStationProducts(t, f.origin.products) assertValidStation(t, f.destination, true) - assertValidStationProducts(t, f.destination.products) t.equal(typeof f.t, 'number') } } diff --git a/test/oebb.js b/test/oebb.js index feae6c65..a2a6785e 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -14,7 +14,7 @@ const createClient = require('..') const oebbProfile = require('../p/oebb') const allProducts = require('../p/oebb/products') const { - assertValidStation, + assertValidStation: _assertValidStation, assertValidPoi, assertValidAddress, assertValidLocation, @@ -24,18 +24,17 @@ const { const when = createWhen('Europe/Vienna', 'de-AT') -const assertValidStationProducts = (t, p) => { - t.ok(p) - t.equal(typeof p.nationalExp, 'boolean') - t.equal(typeof p.national, 'boolean') - t.equal(typeof p.interregional, 'boolean') - t.equal(typeof p.regional, 'boolean') - t.equal(typeof p.suburban, 'boolean') - t.equal(typeof p.bus, 'boolean') - t.equal(typeof p.ferry, 'boolean') - t.equal(typeof p.subway, 'boolean') - t.equal(typeof p.tram, 'boolean') - t.equal(typeof p.onCall, 'boolean') +// todo: DRY with other tests, move into lib +const assertValidStation = (t, s, coordsOptional = false, productsOptional = false) => { + _assertValidStation(t, s, coordsOptional) + if (s.products || !productsOptional) { + t.ok(s.products) + for (let product of allProducts) { + product = product.id + const msg = `station.products[${product}] must be a boolean` + t.equal(typeof s.products[product], 'boolean', msg) + } + } } // todo @@ -73,15 +72,6 @@ const assertIsSalzburgHbf = (t, s) => { t.ok(isRoughlyEqual(s.location.longitude, 13.045604, .0005)) } -// todo: DRY with assertValidStationProducts -// todo: DRY with other tests -const assertValidProducts = (t, p) => { - for (let product of allProducts) { - product = product.id - t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') - } -} - const assertValidPrice = (t, p) => { t.ok(p) if (p.amount !== null) { @@ -133,7 +123,6 @@ test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) { const leg = journey.legs[0] // todo: all legs assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) // todo // if (!(yield findStation(leg.origin.id))) { // console.error('unknown station', leg.origin.id, leg.origin.name) @@ -142,7 +131,6 @@ test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) { t.equal(typeof leg.departurePlatform, 'string') assertValidStation(t, leg.destination) - assertValidStationProducts(t, leg.origin.products) // todo // if (!(yield findStation(leg.destination.id))) { // console.error('unknown station', leg.destination.id, leg.destination.name) @@ -178,12 +166,10 @@ test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) { const lastLeg = journey.legs[journey.legs.length - 1] assertValidStation(t, firstLeg.origin) - assertValidStationProducts(t, firstLeg.origin.products) // todo // if (!(yield findStation(leg.origin.id))) { // console.error('unknown station', leg.origin.id, leg.origin.name) // } - if (firstLeg.origin.products) assertValidProducts(t, firstLeg.origin.products) assertValidWhen(t, firstLeg.departure, when) assertValidWhen(t, firstLeg.arrival, when) assertValidWhen(t, lastLeg.departure, when) @@ -226,8 +212,6 @@ test('Albertina to Salzburg Hbf', co(function* (t) { assertValidWhen(t, lastLeg.arrival, when) assertValidStation(t, lastLeg.destination) - assertValidStationProducts(t, lastLeg.destination.products) - if (lastLeg.destination.products) assertValidProducts(t, lastLeg.destination.products) // todo // if (!(yield findStation(leg.destination.id))) { // console.error('unknown station', leg.destination.id, leg.destination.name) @@ -363,12 +347,10 @@ test('departures at Salzburg Hbf', co(function* (t) { t.ok(Array.isArray(deps)) for (let dep of deps) { assertValidStation(t, dep.station) - assertValidStationProducts(t, dep.station.products) // todo // if (!(yield findStation(dep.station.id))) { // console.error('unknown station', dep.station.id, dep.station.name) // } - if (dep.station.products) assertValidProducts(t, dep.station.products) assertValidWhen(t, dep.when, when) } @@ -421,7 +403,8 @@ test('locations named Salzburg', co(function* (t) { test('location', co(function* (t) { const loc = yield client.location(grazHbf) - assertValidStation(t, loc) + // todo: find a way to always get products from the API + assertValidStation(t, loc, false, true) t.equal(loc.id, grazHbf) t.end() @@ -471,12 +454,10 @@ test('radar Salzburg', co(function* (t) { t.ok(Array.isArray(v.frames)) for (let f of v.frames) { - assertValidStation(t, f.origin, true) - // can contain stations in germany which don't have a products property, would break - // assertValidStationProducts(t, f.origin.products) - assertValidStation(t, f.destination, true) - // can contain stations in germany which don't have a products property, would break - // assertValidStationProducts(t, f.destination.products) + // there are stations which the API desn't return products for + // todo: find a way to always get products from the API + assertValidStation(t, f.origin, true, true) + assertValidStation(t, f.destination, true, true) t.equal(typeof f.t, 'number') } } diff --git a/test/vbb.js b/test/vbb.js index c94dc9d5..60d2c691 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -10,6 +10,7 @@ const shorten = require('vbb-short-station-name') const co = require('./co') const createClient = require('..') const vbbProfile = require('../p/vbb') +const allProducts = require('../p/vbb/products') const { assertValidStation: _assertValidStation, assertValidPoi, @@ -24,20 +25,16 @@ const { const when = createWhen('Europe/Berlin', 'de-DE') +// todo: DRY with other tests, move into lib const assertValidStation = (t, s, coordsOptional = false) => { _assertValidStation(t, s, coordsOptional) t.equal(s.name, shorten(s.name)) -} - -const assertValidStationProducts = (t, p) => { - t.ok(p) - t.equal(typeof p.suburban, 'boolean') - t.equal(typeof p.subway, 'boolean') - t.equal(typeof p.tram, 'boolean') - t.equal(typeof p.bus, 'boolean') - t.equal(typeof p.ferry, 'boolean') - t.equal(typeof p.express, 'boolean') - t.equal(typeof p.regional, 'boolean') + t.ok(s.products) + for (let product of allProducts) { + product = product.id + const msg = `station.products[${product}] must be a boolean` + t.equal(typeof s.products[product], 'boolean', msg) + } } const assertValidLine = (t, l) => { @@ -76,13 +73,11 @@ test('journeys – station to station', co(function* (t) { t.equal(typeof leg.id, 'string') t.ok(leg.id) assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) t.ok(leg.origin.name.indexOf('(Berlin)') === -1) t.strictEqual(leg.origin.id, spichernstr) assertValidWhen(t, leg.departure, when) assertValidStation(t, leg.destination) - assertValidStationProducts(t, leg.destination.products) t.strictEqual(leg.destination.id, amrumerStr) assertValidWhen(t, leg.arrival, when) @@ -246,7 +241,6 @@ test('journeys – station to address', co(function* (t) { const leg = journey.legs[journey.legs.length - 1] assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) assertValidWhen(t, leg.departure, when) const dest = leg.destination @@ -275,7 +269,6 @@ test('journeys – station to POI', co(function* (t) { const leg = journey.legs[journey.legs.length - 1] assertValidStation(t, leg.origin) - assertValidStationProducts(t, leg.origin.products) assertValidWhen(t, leg.departure, when) const dest = leg.destination @@ -342,7 +335,6 @@ test('departures', co(function* (t) { t.equal(dep.station.name, 'U Spichernstr.') assertValidStation(t, dep.station) - assertValidStationProducts(t, dep.station.products) t.strictEqual(dep.station.id, spichernstr) assertValidWhen(t, dep.when, when) @@ -494,10 +486,8 @@ test('radar', co(function* (t) { t.ok(Array.isArray(v.frames)) for (let f of v.frames) { assertValidStation(t, f.origin, true) - assertValidStationProducts(t, f.origin.products) t.strictEqual(f.origin.name.indexOf('(Berlin)'), -1) assertValidStation(t, f.destination, true) - assertValidStationProducts(t, f.destination.products) t.strictEqual(f.destination.name.indexOf('(Berlin)'), -1) t.equal(typeof f.t, 'number') } From 486929acc7bf915222419e620af1c9dcf4dca002 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 18 Apr 2018 16:15:24 +0200 Subject: [PATCH 029/176] tests: move util, co, validate-line-without-mode into lib --- test/db.js | 4 ++-- test/insa.js | 4 ++-- test/{ => lib}/co.js | 2 ++ test/{ => lib}/util.js | 0 test/{ => lib}/validate-line-without-mode.js | 0 test/nahsh.js | 6 +++--- test/oebb.js | 6 +++--- test/vbb.js | 4 ++-- 8 files changed, 14 insertions(+), 12 deletions(-) rename test/{ => lib}/co.js (98%) rename test/{ => lib}/util.js (100%) rename test/{ => lib}/validate-line-without-mode.js (100%) diff --git a/test/db.js b/test/db.js index d4e1c97d..e20e11f0 100644 --- a/test/db.js +++ b/test/db.js @@ -5,7 +5,7 @@ const tapePromise = require('tape-promise').default const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') -const co = require('./co') +const co = require('./lib/co') const createClient = require('..') const dbProfile = require('../p/db') const allProducts = require('../p/db/products') @@ -17,7 +17,7 @@ const { assertValidLine, assertValidStopover, createWhen, assertValidWhen -} = require('./util.js') +} = require('./lib/util.js') const when = createWhen('Europe/Berlin', 'de-DE') diff --git a/test/insa.js b/test/insa.js index 6192120e..e838e31d 100644 --- a/test/insa.js +++ b/test/insa.js @@ -5,7 +5,7 @@ const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') const validateFptf = require('validate-fptf') -const co = require('./co') +const co = require('./lib/co') const createClient = require('..') const insaProfile = require('../p/insa') const allProducts = require('../p/insa/products') @@ -19,7 +19,7 @@ const { hour, createWhen, assertValidWhen -} = require('./util.js') +} = require('./lib/util.js') const when = createWhen('Europe/Berlin', 'de-DE') diff --git a/test/co.js b/test/lib/co.js similarity index 98% rename from test/co.js rename to test/lib/co.js index 595deb14..c01ad0b5 100644 --- a/test/co.js +++ b/test/lib/co.js @@ -1,3 +1,5 @@ +'use strict' + // https://github.com/babel/babel/blob/3c8d831fe41f502cbe2459a271d19c7329ffe369/packages/babel-helpers/src/helpers.js#L242-L270 const co = (fn) => { return function run () { diff --git a/test/util.js b/test/lib/util.js similarity index 100% rename from test/util.js rename to test/lib/util.js diff --git a/test/validate-line-without-mode.js b/test/lib/validate-line-without-mode.js similarity index 100% rename from test/validate-line-without-mode.js rename to test/lib/validate-line-without-mode.js diff --git a/test/nahsh.js b/test/nahsh.js index 6b23347a..0e30e0f3 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -7,9 +7,9 @@ const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') const validateFptf = require('validate-fptf') -const validateLineWithoutMode = require('./validate-line-without-mode') +const validateLineWithoutMode = require('./lib/validate-line-without-mode') -const co = require('./co') +const co = require('./lib/co') const createClient = require('..') const nahshProfile = require('../p/nahsh') const allProducts = require('../p/nahsh/products') @@ -20,7 +20,7 @@ const { assertValidLocation, assertValidStopover, hour, createWhen, assertValidWhen -} = require('./util.js') +} = require('./lib/util.js') const when = createWhen('Europe/Berlin', 'de-DE') diff --git a/test/oebb.js b/test/oebb.js index a2a6785e..a29c945c 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -7,9 +7,9 @@ const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') const validateFptf = require('validate-fptf') -const validateLineWithoutMode = require('./validate-line-without-mode') +const validateLineWithoutMode = require('./lib/validate-line-without-mode') -const co = require('./co') +const co = require('./lib/co') const createClient = require('..') const oebbProfile = require('../p/oebb') const allProducts = require('../p/oebb/products') @@ -20,7 +20,7 @@ const { assertValidLocation, assertValidStopover, hour, createWhen, assertValidWhen -} = require('./util.js') +} = require('./lib/util.js') const when = createWhen('Europe/Vienna', 'de-AT') diff --git a/test/vbb.js b/test/vbb.js index 60d2c691..af1ec977 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -7,7 +7,7 @@ const tapePromise = require('tape-promise').default const tape = require('tape') const shorten = require('vbb-short-station-name') -const co = require('./co') +const co = require('./lib/co') const createClient = require('..') const vbbProfile = require('../p/vbb') const allProducts = require('../p/vbb/products') @@ -21,7 +21,7 @@ const { hour, createWhen, assertValidWhen, assertValidTicket -} = require('./util') +} = require('./lib/util') const when = createWhen('Europe/Berlin', 'de-DE') From 08a33497a061f8b26fd79bae070815e715f7c836 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 18 Apr 2018 20:08:47 +0200 Subject: [PATCH 030/176] pull validators from test/lib/util, use validate-fptf more --- package.json | 2 +- test/lib/util.js | 140 +----------------------- test/lib/validate-fptf-with.js | 24 ++++ test/lib/validators.js | 193 +++++++++++++++++++++++++++++++++ 4 files changed, 222 insertions(+), 137 deletions(-) create mode 100644 test/lib/validate-fptf-with.js create mode 100644 test/lib/validators.js diff --git a/package.json b/package.json index 9c305030..55b42b0b 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "tap-spec": "^4.1.1", "tape": "^4.8.0", "tape-promise": "^3.0.0", - "validate-fptf": "^1.2.1", + "validate-fptf": "^1.3.0", "vbb-stations-autocomplete": "^3.1.0" }, "scripts": { diff --git a/test/lib/util.js b/test/lib/util.js index fbd0d8c5..d5afed72 100644 --- a/test/lib/util.js +++ b/test/lib/util.js @@ -1,98 +1,7 @@ 'use strict' -const validateFptf = require('validate-fptf') const isRoughlyEqual = require('is-roughly-equal') const {DateTime} = require('luxon') -const isValidWGS84 = require('is-coordinates') - -const validateFptfWith = (t, item, allowedTypes, name) => { - try { - validateFptf.recurse(allowedTypes, item, name) - } catch (err) { - t.ifError(err) - } -} - -const assertValidStation = (t, s, coordsOptional = false) => { - validateFptfWith(t, s, ['station'], 'station') - - if (!coordsOptional || (s.location !== null && s.location !== undefined)) { - t.ok(s.location) - assertValidLocation(t, s.location, coordsOptional) - } -} - -const assertValidPoi = (t, p) => { - assertValidLocation(t, p, true) - - t.equal(typeof p.id, 'string') - t.equal(typeof p.name, 'string') - if (p.address !== null && p.address !== undefined) { - t.equal(typeof p.address, 'string') - t.ok(p.address) - } -} - -const assertValidAddress = (t, a) => { - assertValidLocation(t, a, true) - - t.equal(typeof a.address, 'string') -} - -const assertValidLocation = (t, l, coordsOptional = false) => { - t.equal(l.type, 'location') - if (l.name !== null && l.name !== undefined) { - t.equal(typeof l.name, 'string') - t.ok(l.name) - } - - if (l.address !== null && l.address !== undefined) { - t.equal(typeof l.address, 'string') - t.ok(l.address) - } - - const hasLatitude = l.latitude !== null && l.latitude !== undefined - const hasLongitude = l.longitude !== null && l.longitude !== undefined - if (!coordsOptional && hasLatitude) t.equal(typeof l.latitude, 'number') - if (!coordsOptional && hasLongitude) t.equal(typeof l.longitude, 'number') - if ((hasLongitude && !hasLatitude) || (hasLatitude && !hasLongitude)) { - t.fail('should have both .latitude and .longitude') - } - if (hasLatitude && hasLongitude) isValidWGS84([l.longitude, l.latitude]) - - if (!coordsOptional && l.altitude !== null && l.altitude !== undefined) { - t.equal(typeof l.altitude, 'number') - } -} - -const validLineModes = [ - 'train', 'bus', 'watercraft', 'taxi', 'gondola', 'aircraft', - 'car', 'bicycle', 'walking' -] - -const assertValidLine = (t, l) => { - validateFptfWith(t, l, ['line'], 'line') -} - -const isValidDateTime = (w) => { - return !Number.isNaN(+new Date(w)) -} - -const assertValidStopover = (t, s, coordsOptional = false) => { - if ('arrival' in s) t.ok(isValidDateTime(s.arrival)) - if ('departure' in s) t.ok(isValidDateTime(s.departure)) - if (s.arrivalDelay !== null && s.arrivalDelay !== undefined) { - t.equal(typeof s.arrivalDelay, 'number') - } - if (s.departureDelay !== null && s.departureDelay !== undefined) { - t.equal(typeof s.departureDelay, 'number') - } - if (!('arrival' in s) && !('departure' in s)) { - t.fail('stopover doesn\'t contain arrival or departure') - } - t.ok(s.station) - assertValidStation(t, s.station, coordsOptional) -} const hour = 60 * 60 * 1000 const week = 7 * 24 * hour @@ -104,55 +13,14 @@ const createWhen = (timezone, locale) => { locale, }).startOf('week').plus({weeks: 1, hours: 10}).toJSDate() } + const isValidWhen = (actual, expected) => { const ts = +new Date(actual) if (Number.isNaN(ts)) return false - return isRoughlyEqual(12 * hour, +expected, ts) -} - -const assertValidWhen = (t, actual, expected) => { - t.ok(isValidWhen(actual, expected), 'invalid when') -} - -const assertValidTicket = (t, ti) => { - t.strictEqual(typeof ti.name, 'string') - t.ok(ti.name.length > 0) - if (ti.price !== null) { - t.strictEqual(typeof ti.price, 'number') - t.ok(ti.price > 0) - } - if (ti.amount !== null) { - t.strictEqual(typeof ti.amount, 'number') - t.ok(ti.amount > 0) - } - - if ('bike' in ti) t.strictEqual(typeof ti.bike, 'boolean') - if ('shortTrip' in ti) t.strictEqual(typeof ti.shortTrip, 'boolean') - if ('group' in ti) t.strictEqual(typeof ti.group, 'boolean') - if ('fullDay' in ti) t.strictEqual(typeof ti.fullDay, 'boolean') - - if (ti.tariff !== null) { - t.strictEqual(typeof ti.tariff, 'string') - t.ok(ti.tariff.length > 0) - } - if (ti.coverage !== null) { - t.strictEqual(typeof ti.coverage, 'string') - t.ok(ti.coverage.length > 0) - } - if (ti.variant !== null) { - t.strictEqual(typeof ti.variant, 'string') - t.ok(ti.variant.length > 0) - } + // the timestamps might be from long-distance trains + return isRoughlyEqual(14 * hour, +expected, ts) } module.exports = { - assertValidStation, - assertValidPoi, - assertValidAddress, - assertValidLocation, - assertValidLine, - isValidDateTime, - assertValidStopover, - hour, createWhen, isValidWhen, assertValidWhen, - assertValidTicket + hour, createWhen, isValidWhen } diff --git a/test/lib/validate-fptf-with.js b/test/lib/validate-fptf-with.js new file mode 100644 index 00000000..09624a3e --- /dev/null +++ b/test/lib/validate-fptf-with.js @@ -0,0 +1,24 @@ +'use strict' + +const {defaultValidators, createRecurse} = require('validate-fptf') +const validators = require('./validators') + +const create = (cfg, customValidators = {}) => { + const vals = Object.assign({}, defaultValidators) + for (let key of Object.keys(validators)) { + vals[key] = validators[key](cfg) + } + Object.assign(vals, customValidators) + const recurse = createRecurse(vals) + + const validateFptfWith = (t, item, allowedTypes, name) => { + try { + recurse(allowedTypes, item, name) + } catch (err) { + t.ifError(err) // todo: improve error logging + } + } + return validateFptfWith +} + +module.exports = create diff --git a/test/lib/validators.js b/test/lib/validators.js new file mode 100644 index 00000000..1ab565f5 --- /dev/null +++ b/test/lib/validators.js @@ -0,0 +1,193 @@ +'use strict' + +const a = require('assert') +const {defaultValidators} = require('validate-fptf') +const validateRef = require('validate-fptf/lib/reference') +const validateDate = require('validate-fptf/lib/date') + +const {isValidWhen} = require('./util') + +const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) +const is = val => val !== null && val !== undefined + +const createValidateStation = (cfg) => { + const validateStation = (validate, s, name = 'station') => { + defaultValidators.station(validate, s, name) + + if (!cfg.stationCoordsOptional) { + a.ok(is(s.location), `missing ${name}.location`) + } + a.ok(isObj(s.products), name + '.products must be an object') + for (let product of cfg.products) { + const msg = name + `.products[${product.id}] must be a boolean` + a.strictEqual(typeof s.products[product.id], 'boolean', msg) + } + + if ('lines' in s) { + a.ok(Array.isArray(s.lines), name + `.lines must be an array`) + for (let i = 0; i < s.lines.length; i++) { + validate(['line'], s.lines[i], name + `.lines[${i}]`) + } + } + } + return validateStation +} + +const validatePoi = (validate, poi, name = 'location') => { + defaultValidators.location(validate, poi, name) + validateRef(poi.id, name + '.id') + a.ok(poi.name, name + '.name must not be empty') +} + +const validateAddress = (validate, addr, name = 'location') => { + defaultValidators.location(validate, addr, name) + a.strictEqual(typeof addr.address, 'string', name + '.address must be a string') + a.ok(addr.address, name + '.address must not be empty') +} + +const validateLocation = (validate, loc, name = 'location') => { + a.ok(isObj(loc), name + ' must be an object') + if (loc.type === 'station') validate(['station'], loc, name) + else if ('id' in loc) validatePoi(validate, loc, name) + else validateAddress(validate, loc, name) +} + +const createValidateLine = (cfg) => { + const validLineModes = [] + for (let product of cfg.products) { + if (!validLineModes.includes(product.mode)) { + validLineModes.push(product.mode) + } + } + + const validateLine = (validate, line, name = 'line') => { + defaultValidators.line(validate, line, name) + a.ok(validLineModes.includes(line.mode)) + } + return validateLine +} + +const createValidateStopover = (cfg) => { + const validateStopover = (validate, s, name = 'stopover') => { + if (is(s.arrival)) { + validateDate(s.arrival, name + '.arrival') + a.ok(isValidWhen(s.arrival, cfg.when), name + '.arrival is invalid') + } + if (is(s.departure)) { + validateDate(s.departure, name + '.departure') + a.ok(isValidWhen(s.departure, cfg.when), name + '.departure is invalid') + } + if (!is(s.arrival) && !is(s.departure)) { + asser.fail(name + ' contains neither arrival nor departure') + } + + if (is(s.arrivalDelay)) { + const msg = name + '.arrivalDelay must be a number' + a.strictEqual(typeof s.arrivalDelay, 'number', msg) + } + if (is(s.departureDelay)) { + const msg = name + '.departureDelay must be a number' + a.strictEqual(typeof s.departureDelay, 'number', msg) + } + + validate(['station'], s.station, name + '.station') + } + return validateStopover +} + +const validateTicket = (validate, ti, name = 'ticket') => { + a.strictEqual(typeof ti.name, 'string', name + '.name must be a string') + a.ok(ti.name, name + '.name must not be empty') + + if (is(ti.price)) { + a.strictEqual(typeof ti.price, 'number', name + '.price must be a number') + a.ok(ti.price > 0, name + '.price must be >0') + } + if (is(ti.amount)) { + a.strictEqual(typeof ti.amount, 'number', name + '.amount must be a number') + a.ok(ti.amount > 0, name + '.amount must be >0') + } + + // todo: move to VBB tests + if ('bike' in ti) { + a.strictEqual(typeof ti.bike, 'boolean', name + '.bike must be a boolean') + } + if ('shortTrip' in ti) { + a.strictEqual(typeof ti.shortTrip, 'boolean', name + '.shortTrip must be a boolean') + } + if ('group' in ti) { + a.strictEqual(typeof ti.group, 'boolean', name + '.group must be a boolean') + } + if ('fullDay' in ti) { + a.strictEqual(typeof ti.fullDay, 'boolean', name + '.fullDay must be a boolean') + } + if ('tariff' in ti) { + a.strictEqual(typeof ti.tariff, 'string', name + '.tariff must be a string') + a.ok(ti.tariff, name + '.tariff must not be empty') + } + if ('coverage' in ti) { + a.strictEqual(typeof ti.coverage, 'string', name + '.coverage must be a string') + a.ok(ti.coverage, name + '.coverage must not be empty') + } + if ('variant' in ti) { + a.strictEqual(typeof ti.variant, 'string', name + '.variant must be a string') + a.ok(ti.variant, name + '.variant must not be empty') + } +} + +const createValidateJourneyLeg = (cfg) => { + const validateJourneyLeg = (validate, leg, name = 'journeyLeg') => { + const withFakeScheduleAndOperator = Object.assign({ + schedule: 'foo', // todo: let hafas-client parse a schedule ID + operator: 'bar' // todo: let hafas-client parse the operator + }, leg) + defaultValidators.journeyLeg(validate, withFakeScheduleAndOperator, name) + + if (leg.arrival !== null) { + const msg = name + '.arrival is invalid' + a.ok(isValidWhen(leg.arrival, cfg.when), msg) + } + if (leg.departure !== null) { + const msg = name + '.departure is invalid' + a.ok(isValidWhen(leg.departure, cfg.when), msg) + } + + if ('passed' in leg) { + a.ok(Array.isArray(leg.passed), name + '.passed must be an array') + a.ok(leg.passed.length > 0, name + '.passed must not be empty') + + for (let i = 0; i < leg.passed.length; i++) { + validate('stopover', leg.passed[i], name + `.passed[${i}]`) + } + } + } + return validateJourneyLeg +} + +const validateJourney = (validate, j, name = 'journey') => { + const withFakeId = Object.assign({ + id: 'foo' // todo: let hafas-client parse a journey ID + }, j) + defaultValidators.journey(validate, withFakeId, name) + + if ('tickets' in j) { + a.ok(Array.isArray(j.tickets), name + '.tickets must be an array') + a.ok(j.tickets.length > 0, name + '.tickets must not be empty') + + for (let i = 0; i < j.tickets.length; i++) { + validate(['ticket'], j.tickets[i], name + `.tickets[${i}]`) + } + } +} + +module.exports = { + station: createValidateStation, + location: () => validateLocation, + poi: () => validatePoi, + address: () => validateAddress, + line: createValidateLine, + stopover: createValidateStopover, + ticket: () => validateTicket, + journeyLeg: createValidateJourneyLeg, + journey: () => validateJourney +} From a2abf3b47d500e292c665a115a0c2219c75bf071 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 18 Apr 2018 23:54:09 +0200 Subject: [PATCH 031/176] tests: more validators --- test/lib/validators.js | 94 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/test/lib/validators.js b/test/lib/validators.js index 1ab565f5..afc76d1e 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -52,6 +52,14 @@ const validateLocation = (validate, loc, name = 'location') => { else validateAddress(validate, loc, name) } +const validateLocations = (validate, locs, name = 'locations') => { + a.ok(Array.isArray(locs), name + ' must be an array') + a.ok(locs.length > 0, name + ' must not be empty') + for (let i = 0; i < locs.length; i++) { + validate('location', locs[i], name + `[${i}]`) + } +} + const createValidateLine = (cfg) => { const validLineModes = [] for (let product of cfg.products) { @@ -180,14 +188,98 @@ const validateJourney = (validate, j, name = 'journey') => { } } +const validateJourneys = (validate, js, name = 'journeys') => { + a.ok(Array.isArray(js), name + ' must be an array') + a.ok(js.length > 0, name + ' must not be empty') + for (let i = 0; i < js.length; i++) { + validate(['journey'], js[i], name + `[${i}]`) + } +} + +const createValidateDeparture = (cfg) => { + const validateDeparture = (validate, dep, name = 'departure') => { + a.ok(isObj(dep), name + ' must be an object') + // todo: let hafas-client add a .type field + + a.strictEqual(typeof dep.journeyId, 'string', name + '.journeyId must be a string') + a.ok(dep.journeyId, name + '.journeyId must not be empty') + a.strictEqual(typeof dep.trip, 'number', name + '.trip must be a number') + + validate(['station'], dep.station, name + '.station') + + a.ok(isValidWhen(dep.when, cfg.when), name + '.when is invalid') + if (dep.delay !== null) { + const msg = name + '.delay must be a number' + a.strictEqual(typeof dep.delay, 'number', msg) + } + + validate(['line'], dep.line, name + '.line') + } + return validateDeparture +} + +const validateDepartures = (validate, deps, name = 'departures') => { + a.ok(Array.isArray(deps), name + ' must be an array') + a.ok(deps.length > 0, name + ' must not be empty') + for (let i = 0; i < deps.length; i++) { + validate('departure', deps[i], name + `[${i}]`) + } +} + +const validateMovement = (validate, m, name = 'movement') => { + a.ok(isObj(m), name + ' must be an object') + // todo: let hafas-client add a .type field + + validate(['line'], v.line, name + '.line') + + const lName = name + '.location' + validate(['location'], v.location, lName) + a.ok(v.location.latitude <= 55, lName + '.latitude is too small') + a.ok(v.location.latitude >= 45, lName + '.latitude is too large') + a.ok(v.location.longitude >= 9, lName + '.longitude is too small') + a.ok(v.location.longitude <= 15, lName + '.longitude is too small') + + a.ok(Array.isArray(v.nextStops), name + '.nextStops must be an array') + for (let i = 0; i < v.nextStops.length; i++) { + const st = v.nextStops[i] + assertValidStopover('stopover', v.nextStops[i], name + `.nextStops[${i}]`) + } + + a.ok(Array.isArray(v.frames), name + '.frames must be an array') + a.ok(v.frames.length > 0, name + '.frames must not be empty') + for (let i = 0; i < v.frames.length; i++) { + const f = v.frames[i] + const fName = name + `.frames[${i}]` + + a.ok(isObj(f), fName + ' must be an object') + assertValidStation(['station'], f.origin, fName + '.origin') + assertValidStation(['station'], f.destination, fName + '.destination') + a.strictEqual(typeof f.t, 'number', fName + '.frames must be a number') + } +} + +const validateMovements = (validate, ms, name = 'movements') => { + a.ok(Array.isArray(ms), name + ' must be an array') + a.ok(ms.length > 0, name + ' must not be empty') + for (let i = 0; i < ms.length; i++) { + validate('movement', ms[i], name + `[${i}]`) + } +} + module.exports = { station: createValidateStation, location: () => validateLocation, + locations: () => validateLocations, poi: () => validatePoi, address: () => validateAddress, line: createValidateLine, stopover: createValidateStopover, ticket: () => validateTicket, journeyLeg: createValidateJourneyLeg, - journey: () => validateJourney + journey: () => validateJourney, + journeys: () => validateJourneys, + departure: createValidateDeparture, + departures: () => validateDepartures, + movement: () => validateMovement, + movements: () => validateMovements } From 1fb6d64f8abebc9d51a7969a77a1f3b3a34bc5a8 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 18 Apr 2018 23:56:11 +0200 Subject: [PATCH 032/176] VBB tests: use new validators --- test/vbb.js | 395 +++++++++++++++++++++------------------------------- 1 file changed, 160 insertions(+), 235 deletions(-) diff --git a/test/vbb.js b/test/vbb.js index af1ec977..7995a066 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -1,52 +1,99 @@ 'use strict' -const a = require('assert') -const isRoughlyEqual = require('is-roughly-equal') const stations = require('vbb-stations-autocomplete') +const a = require('assert') +const shorten = require('vbb-short-station-name') const tapePromise = require('tape-promise').default const tape = require('tape') -const shorten = require('vbb-short-station-name') +const isRoughlyEqual = require('is-roughly-equal') +const {createWhen} = require('./lib/util') const co = require('./lib/co') const createClient = require('..') const vbbProfile = require('../p/vbb') -const allProducts = require('../p/vbb/products') +const products = require('../p/vbb/products') const { - assertValidStation: _assertValidStation, - assertValidPoi, - assertValidAddress, - assertValidLocation, - assertValidLine: _assertValidLine, - assertValidStopover, - hour, createWhen, - assertValidWhen, - assertValidTicket -} = require('./lib/util') + station: createValidateStation, + line: createValidateLine, + journeyLeg: createValidateJourneyLeg, + departure: createValidateDeparture, + movement: _validateMovement +} = require('./lib/validators') +const createValidate = require('./lib/validate-fptf-with') + +const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const when = createWhen('Europe/Berlin', 'de-DE') -// todo: DRY with other tests, move into lib -const assertValidStation = (t, s, coordsOptional = false) => { - _assertValidStation(t, s, coordsOptional) - t.equal(s.name, shorten(s.name)) - t.ok(s.products) - for (let product of allProducts) { - product = product.id - const msg = `station.products[${product}] must be a boolean` - t.equal(typeof s.products[product], 'boolean', msg) +const cfg = { + when, + stationCoordsOptional: false, + products +} + +const validateDirection = (dir, name) => { + a.strictEqual(typeof dir, 'string', name + ' must be a string') + a.ok(dir, name + ' must not be empty') + if (!stations(dir, true, false)[0]) { + console.error(name + `: station "${dir}" is unknown`) } } -const assertValidLine = (t, l) => { - _assertValidLine(t, l) - if (l.symbol !== null) t.equal(typeof l.symbol, 'string') - if (l.nr !== null) t.equal(typeof l.nr, 'number') - if (l.metro !== null) t.equal(typeof l.metro, 'boolean') - if (l.express !== null) t.equal(typeof l.express, 'boolean') - if (l.night !== null) t.equal(typeof l.night, 'boolean') +// todo: coordsOptional = false +const _validateStation = createValidateStation(cfg) +const validateStation = (validate, s, name) => { + _validateStation(validate, s, name) + a.equal(s.name, shorten(s.name), name + '.name must be shortened') } -const findStation = (query) => stations(query, true, false)[0] +const _validateLine = createValidateLine(cfg) +const validateLine = (validate, l, name) => { + _validateLine(validate, l, name) + if (l.symbol !== null) { + a.strictEqual(typeof l.symbol, 'string', name + '.symbol must be a string') + a.ok(l.symbol, name + '.symbol must not be empty') + } + if (l.nr !== null) { + a.strictEqual(typeof l.nr, 'number', name + '.nr must be a string') + a.ok(l.nr, name + '.nr must not be empty') + } + if (l.metro !== null) { + a.strictEqual(typeof l.metro, 'boolean', name + '.metro must be a boolean') + } + if (l.express !== null) { + a.strictEqual(typeof l.express, 'boolean', name + '.express must be a boolean') + } + if (l.night !== null) { + a.strictEqual(typeof l.night, 'boolean', name + '.night must be a boolean') + } +} + +const _validateJourneyLeg = createValidateJourneyLeg(cfg) +const validateJourneyLeg = (validate, l, name) => { + _validateJourneyLeg(validate, l, name) + if (l.mode !== 'walking') { + validateDirection(l.direction, name + '.direction') + } +} + +const _validateDeparture = createValidateDeparture(cfg) +const validateDeparture = (validate, dep, name) => { + _validateDeparture(validate, dep, name) + validateDirection(dep.direction, name + '.direction') +} + +const validateMovement = (validate, m, name) => { + _validateMovement(validate, m, name) + validateDirection(m.direction, name + '.direction') +} + +const validate = createValidate(cfg, { + station: validateStation, + line: validateLine, + journeyLeg: validateJourneyLeg, + departure: validateDeparture, + movement: validateMovement +}) const test = tapePromise(tape) const client = createClient(vbbProfile) @@ -54,49 +101,29 @@ const client = createClient(vbbProfile) const amrumerStr = '900000009101' const spichernstr = '900000042101' const bismarckstr = '900000024201' +const atze = '900980720' +const westhafen = '900000001201' +const wedding = '900000009104' +const württembergallee = '900000026153' +const berlinerStr = '900000044201' +const landhausstr = '900000043252' test('journeys – station to station', co(function* (t) { const journeys = yield client.journeys(spichernstr, amrumerStr, { results: 3, when, passedStations: true }) - t.ok(Array.isArray(journeys)) + validate(t, journeys, 'journeys', 'journeys') t.strictEqual(journeys.length, 3) + for (let i = 0; i < journeys.length; i++) { + const j = journeys[i] - for (let journey of journeys) { - t.equal(journey.type, 'journey') - - t.ok(Array.isArray(journey.legs)) - t.strictEqual(journey.legs.length, 1) - const leg = journey.legs[0] // todo: all legs - - t.equal(typeof leg.id, 'string') - t.ok(leg.id) - assertValidStation(t, leg.origin) - t.ok(leg.origin.name.indexOf('(Berlin)') === -1) - t.strictEqual(leg.origin.id, spichernstr) - assertValidWhen(t, leg.departure, when) - - assertValidStation(t, leg.destination) - t.strictEqual(leg.destination.id, amrumerStr) - assertValidWhen(t, leg.arrival, when) - - assertValidLine(t, leg.line) - if (!findStation(leg.direction)) { - const err = new Error('unknown direction: ' + leg.direction) - err.stack = err.stack.split('\n').slice(0, 2).join('\n') - console.error(err) - } - t.ok(leg.direction.indexOf('(Berlin)') === -1) - - t.ok(Array.isArray(leg.passed)) - for (let passed of leg.passed) assertValidStopover(t, passed) + const firstLeg = j.legs[0] + const lastLeg = j.legs[j.legs.length - 1] + t.strictEqual(firstLeg.origin.id, spichernstr) + t.strictEqual(lastLeg.destination.id, amrumerStr) // todo: find a journey where there ticket info is always available - if (journey.tickets) { - t.ok(Array.isArray(journey.tickets)) - for (let ticket of journey.tickets) assertValidTicket(t, ticket) - } } t.end() })) @@ -115,22 +142,28 @@ test('journeys – only subway', co(function* (t) { } }) - t.ok(Array.isArray(journeys)) + validate(t, journeys, 'journeys', 'journeys') t.ok(journeys.length > 1) + for (let i = 0; i < journeys.length; i++) { + const journey = journeys[i] + for (let j = 0; j < journey.legs.length; j++) { + const leg = journey.legs[j] - for (let journey of journeys) { - for (let leg of journey.legs) { + const name = `journeys[${i}].legs[${i}].line` if (leg.line) { - assertValidLine(t, leg.line) - t.equal(leg.line.mode, 'train') - t.equal(leg.line.product, 'subway') + t.equal(leg.line.mode, 'train', name + '.mode is invalid') + t.equal(leg.line.product, 'subway', name + '.product is invalid') } + t.ok(journey.legs.some(l => l.line), name + '.legs has no subway leg') } } + t.end() })) -test('journeys – fails with no product', co(function* (t) { +test('journeys – fails with no product', (t) => { + // todo: make this test work + // t.plan(1) try { client.journeys(spichernstr, bismarckstr, { when, @@ -148,15 +181,16 @@ test('journeys – fails with no product', co(function* (t) { .catch(() => {}) } catch (err) { t.ok(err, 'error thrown') - t.end() } -})) + t.end() +}) test('earlier/later journeys', co(function* (t) { const model = yield client.journeys(spichernstr, bismarckstr, { results: 3, when }) + // todo: move to journeys validator? t.equal(typeof model.earlierRef, 'string') t.ok(model.earlierRef) t.equal(typeof model.laterRef, 'string') @@ -167,11 +201,15 @@ test('earlier/later journeys', co(function* (t) { client.journeys(spichernstr, bismarckstr, { when, earlierThan: model.earlierRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) t.throws(() => { client.journeys(spichernstr, bismarckstr, { when, laterThan: model.laterRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) let earliestDep = Infinity, latestDep = -Infinity @@ -212,72 +250,52 @@ test('journey leg details', co(function* (t) { t.ok(p.line.name, 'precondition failed') const leg = yield client.journeyLeg(p.id, p.line.name, {when}) - t.equal(typeof leg.id, 'string') - t.ok(leg.id) - - assertValidLine(t, leg.line) - - t.equal(typeof leg.direction, 'string') - t.ok(leg.direction) - - t.ok(Array.isArray(leg.passed)) - for (let passed of leg.passed) assertValidStopover(t, passed) - + validate(t, leg, 'journeyLeg', 'leg') t.end() })) - - test('journeys – station to address', co(function* (t) { + const latitude = 52.541797 + const longitude = 13.350042 const journeys = yield client.journeys(spichernstr, { type: 'location', address: 'Torfstr. 17, Berlin', - latitude: 52.541797, longitude: 13.350042 + latitude, longitude }, {results: 1, when}) - t.ok(Array.isArray(journeys)) - t.strictEqual(journeys.length, 1) - const journey = journeys[0] - const leg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, leg.origin) - assertValidWhen(t, leg.departure, when) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const name = `journeys[0].legs[${i}].destination` - const dest = leg.destination - assertValidAddress(t, dest) - t.strictEqual(dest.address, '13353 Berlin-Wedding, Torfstr. 17') - t.ok(isRoughlyEqual(.0001, dest.latitude, 52.541797)) - t.ok(isRoughlyEqual(.0001, dest.longitude, 13.350042)) - assertValidWhen(t, leg.arrival, when) + t.strictEqual(d.address, '13353 Berlin-Wedding, Torfstr. 17', name + '.address is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') t.end() })) - - test('journeys – station to POI', co(function* (t) { + const latitude = 52.543333 + const longitude = 13.351686 const journeys = yield client.journeys(spichernstr, { type: 'location', - id: '900980720', + id: atze, name: 'Berlin, Atze Musiktheater für Kinder', - latitude: 52.543333, longitude: 13.351686 + latitude, longitude }, {results: 1, when}) - t.ok(Array.isArray(journeys)) - t.strictEqual(journeys.length, 1) - const journey = journeys[0] - const leg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, leg.origin) - assertValidWhen(t, leg.departure, when) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const name = `journeys[0].legs[${i}].destination` - const dest = leg.destination - assertValidPoi(t, dest) - t.strictEqual(dest.id, '900980720') - t.strictEqual(dest.name, 'Berlin, Atze Musiktheater für Kinder') - t.ok(isRoughlyEqual(.0001, dest.latitude, 52.543333)) - t.ok(isRoughlyEqual(.0001, dest.longitude, 13.351686)) - assertValidWhen(t, leg.arrival, when) + t.strictEqual(d.id, atze, name + '.id is invalid') + t.strictEqual(d.name, 'Berlin, Atze Musiktheater für Kinder', name + '.name is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') t.end() })) @@ -285,41 +303,21 @@ test('journeys – station to POI', co(function* (t) { test('journeys: via works – with detour', co(function* (t) { // Going from Westhafen to Wedding via Württembergalle without detour // is currently impossible. We check if the routing engine computes a detour. - const westhafen = '900000001201' - const wedding = '900000009104' - const württembergallee = '900000026153' - const [journey] = yield client.journeys(westhafen, wedding, { + const journeys = yield client.journeys(westhafen, wedding, { via: württembergallee, results: 1, when, passedStations: true }) - t.ok(journey) + validate(t, journeys, 'journeys', 'journeys') - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === württembergallee)) - t.ok(l, 'Württembergalle is not being passed') - - t.end() -})) - -test('journeys: via works – without detour', co(function* (t) { - // When going from Ruhleben to Zoo via Kastanienallee, there is *no need* - // to change trains / no need for a "detour". - const ruhleben = '900000025202' - const zoo = '900000023201' - const kastanienallee = '900000020152' - const [journey] = yield client.journeys(ruhleben, zoo, { - via: kastanienallee, - results: 1, - when, - passedStations: true + const leg = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return passed.station.id === württembergallee + }) }) - - t.ok(journey) - - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === kastanienallee)) - t.ok(l, 'Kastanienallee is not being passed') + t.ok(leg, 'Württembergalle is not being passed') t.end() })) @@ -327,29 +325,21 @@ test('journeys: via works – without detour', co(function* (t) { test('departures', co(function* (t) { const deps = yield client.departures(spichernstr, {duration: 5, when}) - t.ok(Array.isArray(deps)) - t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) - for (let dep of deps) { - t.equal(typeof dep.journeyId, 'string') - t.ok(dep.journeyId) + validate(t, deps, 'departures', 'departures') + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` - t.equal(dep.station.name, 'U Spichernstr.') - assertValidStation(t, dep.station) - t.strictEqual(dep.station.id, spichernstr) - - assertValidWhen(t, dep.when, when) - if (!findStation(dep.direction)) { - const err = new Error('unknown direction: ' + dep.direction) - err.stack = err.stack.split('\n').slice(0, 2).join('\n') - console.error(err) - } - assertValidLine(t, dep.line) + t.equal(dep.station.name, 'U Spichernstr.', name + '.station.name is invalid') + t.equal(dep.station.id, spichernstr, name + '.station.id is invalid') } + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) + t.end() })) test('departures with station object', co(function* (t) { - yield client.departures({ + const deps = yield client.departures({ type: 'station', id: spichernstr, name: 'U Spichernstr', @@ -360,7 +350,7 @@ test('departures with station object', co(function* (t) { } }, {when}) - t.ok('did not fail') + validate(t, deps, 'departures', 'departures') t.end() })) @@ -368,12 +358,9 @@ test('departures at 7-digit station', co(function* (t) { const eisenach = '8010097' // see derhuerst/vbb-hafas#22 yield client.departures(eisenach, {when}) t.pass('did not fail') - t.end() })) - - test('nearby', co(function* (t) { // Berliner Str./Bundesallee const nearby = yield client.nearby({ @@ -382,18 +369,14 @@ test('nearby', co(function* (t) { longitude: 13.3310411 }, {distance: 200}) - t.ok(Array.isArray(nearby)) - for (let n of nearby) { - if (n.type === 'station') assertValidStation(t, n) - else assertValidLocation(t, n, false) - } + validate(t, nearby, 'locations', 'nearby') - t.equal(nearby[0].id, '900000044201') + t.equal(nearby[0].id, berlinerStr) t.equal(nearby[0].name, 'U Berliner Str.') t.ok(nearby[0].distance > 0) t.ok(nearby[0].distance < 100) - t.equal(nearby[1].id, '900000043252') + t.equal(nearby[1].id, landhausstr) t.equal(nearby[1].name, 'Landhausstr.') t.ok(nearby[1].distance > 100) t.ok(nearby[1].distance < 200) @@ -401,18 +384,12 @@ test('nearby', co(function* (t) { t.end() })) - - test('locations', co(function* (t) { const locations = yield client.locations('Alexanderplatz', {results: 20}) - t.ok(Array.isArray(locations)) - t.ok(locations.length > 0) + validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 20) - for (let l of locations) { - if (l.type === 'station') assertValidStation(t, l) - else assertValidLocation(t, l) - } + t.ok(locations.find(s => s.type === 'station')) t.ok(locations.find(s => s.id && s.name)) // POIs t.ok(locations.find(s => !s.name && s.address)) // addresses @@ -421,21 +398,14 @@ test('locations', co(function* (t) { })) test('location', co(function* (t) { - const loc = yield client.location(spichernstr) + const s = yield client.location(spichernstr) - assertValidStation(t, loc) - t.equal(loc.id, spichernstr) - - t.ok(Array.isArray(loc.lines)) - if (Array.isArray(loc.lines)) { - for (let line of loc.lines) assertValidLine(t, line) - } + validate(t, s, 'station', 'station') + t.equal(s.id, spichernstr) t.end() })) - - test('radar', co(function* (t) { const vehicles = yield client.radar({ north: 52.52411, @@ -446,51 +416,6 @@ test('radar', co(function* (t) { duration: 5 * 60, when }) - t.ok(Array.isArray(vehicles)) - t.ok(vehicles.length > 0) - for (let v of vehicles) { - - if (!findStation(v.direction)) { - const err = new Error('unknown direction: ' + v.direction) - err.stack = err.stack.split('\n').slice(0, 2).join('\n') - console.error(err) - } - assertValidLine(t, v.line) - - t.equal(typeof v.location.latitude, 'number') - t.ok(v.location.latitude <= 55, 'vehicle is too far away') - t.ok(v.location.latitude >= 45, 'vehicle is too far away') - t.equal(typeof v.location.longitude, 'number') - t.ok(v.location.longitude >= 9, 'vehicle is too far away') - t.ok(v.location.longitude <= 15, 'vehicle is too far away') - - t.ok(Array.isArray(v.nextStops)) - for (let st of v.nextStops) { - assertValidStopover(t, st, true) - t.strictEqual(st.station.name.indexOf('(Berlin)'), -1) - - if (st.arrival) { - t.equal(typeof st.arrival, 'string') - const arr = +new Date(st.arrival) - // note that this can be an ICE train - t.ok(isRoughlyEqual(14 * hour, +when, arr)) - } - if (st.departure) { - t.equal(typeof st.departure, 'string') - const dep = +new Date(st.departure) - // note that this can be an ICE train - t.ok(isRoughlyEqual(14 * hour, +when, dep)) - } - } - - t.ok(Array.isArray(v.frames)) - for (let f of v.frames) { - assertValidStation(t, f.origin, true) - t.strictEqual(f.origin.name.indexOf('(Berlin)'), -1) - assertValidStation(t, f.destination, true) - t.strictEqual(f.destination.name.indexOf('(Berlin)'), -1) - t.equal(typeof f.t, 'number') - } - } + validate(t, vehicles, 'movements', 'vehicles') t.end() })) From 46ce4bbbe03a95044dac5b9aab49218857557d46 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Apr 2018 14:55:17 +0200 Subject: [PATCH 033/176] DB tests: use new validators --- test/db.js | 306 ++++++++++++++++------------------------- test/lib/validators.js | 20 +++ test/vbb.js | 3 +- 3 files changed, 142 insertions(+), 187 deletions(-) diff --git a/test/db.js b/test/db.js index e20e11f0..dee3e6b1 100644 --- a/test/db.js +++ b/test/db.js @@ -1,71 +1,47 @@ 'use strict' -const getStations = require('db-stations').full +const stations = require('db-stations/full.json') +const a = require('assert') const tapePromise = require('tape-promise').default const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') +const {createWhen} = require('./lib/util') const co = require('./lib/co') const createClient = require('..') const dbProfile = require('../p/db') -const allProducts = require('../p/db/products') +const products = require('../p/db/products') const { - assertValidStation: _assertValidStation, - assertValidPoi, - assertValidAddress, - assertValidLocation, - assertValidLine, - assertValidStopover, - createWhen, assertValidWhen -} = require('./lib/util.js') + station: createValidateStation +} = require('./lib/validators') +const createValidate = require('./lib/validate-fptf-with') + +const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const when = createWhen('Europe/Berlin', 'de-DE') -// todo: DRY with other tests, move into lib -const assertValidStation = (t, s) => { - _assertValidStation(t, s) - t.ok(s.products) - for (let product of allProducts) { - product = product.id - const msg = `station.products[${product}] must be a boolean` - t.equal(typeof s.products[product], 'boolean', msg) +const cfg = { + when, + stationCoordsOptional: false, + products +} + +const _validateStation = createValidateStation(cfg) +const validateStation = (validate, s, name) => { + _validateStation(validate, s, name) + const match = stations.some(station => ( + station.id === s.id || + (station.additionalIds && station.additionalIds.includes(s.id)) + )) + if (!match) { + console.error(name + `.id: unknown ID "${s.id}"`) } } -const findStation = (id) => new Promise((yay, nay) => { - const stations = getStations() - stations - .once('error', nay) - .on('data', (s) => { - if ( - s.id === id || - (s.additionalIds && s.additionalIds.includes(id)) - ) { - yay(s) - stations.destroy() - } - }) - .once('end', yay) +const validate = createValidate(cfg, { + station: validateStation }) -const isJungfernheide = (s) => { - return s.type === 'station' && - (s.id === '008011167' || s.id === jungfernh) && - s.name === 'Berlin Jungfernheide' && - s.location && - isRoughlyEqual(s.location.latitude, 52.530408, .0005) && - isRoughlyEqual(s.location.longitude, 13.299424, .0005) -} - -const assertIsJungfernheide = (t, s) => { - t.equal(s.type, 'station') - t.ok(s.id === '008011167' || s.id === jungfernh, 'id should be 8011167') - t.equal(s.name, 'Berlin Jungfernheide') - t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 52.530408, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 13.299424, .0005)) -} - const assertValidPrice = (t, p) => { t.ok(p) if (p.amount !== null) { @@ -81,101 +57,67 @@ const assertValidPrice = (t, p) => { const test = tapePromise(tape) const client = createClient(dbProfile) -const jungfernh = '8011167' const berlinHbf = '8011160' const münchenHbf = '8000261' -const hannoverHbf = '8000152' +const jungfernheide = '8011167' +const atze = '991598902' +const westhafen = '008089116' +const wedding = '008089131' +const württembergallee = '731084' const regensburgHbf = '8000309' test('Berlin Jungfernheide to München Hbf', co(function* (t) { - const journeys = yield client.journeys(jungfernh, münchenHbf, { + const journeys = yield client.journeys(jungfernheide, münchenHbf, { when, passedStations: true }) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length > 0, 'no journeys') + validate(t, journeys, 'journeys', 'journeys') for (let journey of journeys) { - t.equal(journey.type, 'journey') - - t.ok(Array.isArray(journey.legs)) - t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] // todo: all legs - - assertValidStation(t, leg.origin) - if (!(yield findStation(leg.origin.id))) { - console.error('unknown station', leg.origin.id, leg.origin.name) - } - assertValidWhen(t, leg.departure, when) - t.equal(typeof leg.departurePlatform, 'string') - - assertValidStation(t, leg.destination) - if (!(yield findStation(leg.destination.id))) { - console.error('unknown station', leg.destination.id, leg.destination.name) - } - assertValidWhen(t, leg.arrival, when) - t.equal(typeof leg.arrivalPlatform, 'string') - - assertValidLine(t, leg.line) - - t.ok(Array.isArray(leg.passed)) - for (let stopover of leg.passed) assertValidStopover(t, stopover) - if (journey.price) assertValidPrice(t, journey.price) } t.end() })) +// todo: journeys, only one product +// todo: journeys, fails with no product + test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) { - const journeys = yield client.journeys(jungfernh, { + const latitude = 52.5416823 + const longitude = 13.3491223 + const journeys = yield client.journeys(jungfernheide, { type: 'location', address: 'Torfstraße 17', - latitude: 52.5416823, longitude: 13.3491223 + latitude, longitude }, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const leg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, leg.origin) - if (!(yield findStation(leg.origin.id))) { - console.error('unknown station', leg.origin.id, leg.origin.name) - } - assertValidWhen(t, leg.departure, when) - assertValidWhen(t, leg.arrival, when) - - const d = leg.destination - assertValidAddress(t, d) - t.equal(d.address, 'Torfstraße 17') - t.ok(isRoughlyEqual(.0001, d.latitude, 52.5416823)) - t.ok(isRoughlyEqual(.0001, d.longitude, 13.3491223)) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const name = `journeys[0].legs[${i}].destination` + t.equal(d.address, 'Torfstraße 17', name + '.address is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') t.end() })) test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { - const journeys = yield client.journeys(jungfernh, { - type: 'location', id: '991598902', name: 'ATZE Musiktheater', - latitude: 52.542417, longitude: 13.350437 + const latitude = 52.542417 + const longitude = 13.350437 + const journeys = yield client.journeys(jungfernheide, { + type: 'location', id: atze, name: 'ATZE Musiktheater', + latitude, longitude }, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const leg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, leg.origin) - if (!(yield findStation(leg.origin.id))) { - console.error('unknown station', leg.origin.id, leg.origin.name) - } - assertValidWhen(t, leg.departure, when) - assertValidWhen(t, leg.arrival, when) - - const d = leg.destination - assertValidPoi(t, d) - t.equal(d.name, 'ATZE Musiktheater') - t.ok(isRoughlyEqual(.0001, d.latitude, 52.542399)) - t.ok(isRoughlyEqual(.0001, d.longitude, 13.350402)) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const name = `journeys[0].legs[${i}].destination` + t.equal(d.name, 'ATZE Musiktheater', name + '.name is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') t.end() })) @@ -183,50 +125,31 @@ test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { test('journeys: via works – with detour', co(function* (t) { // Going from Westhafen to Wedding via Württembergalle without detour // is currently impossible. We check if the routing engine computes a detour. - const westhafen = '008089116' - const wedding = '008089131' - const württembergallee = '731084' - const [journey] = yield client.journeys(westhafen, wedding, { + const journeys = yield client.journeys(westhafen, wedding, { via: württembergallee, results: 1, when, passedStations: true }) - t.ok(journey) + validate(t, journeys, 'journeys', 'journeys') - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === württembergallee)) - t.ok(l, 'Württembergalle is not being passed') - - t.end() -})) - -test('journeys: via works – without detour', co(function* (t) { - // When going from Ruhleben to Zoo via Kastanienallee, there is *no need* - // to change trains / no need for a "detour". - const ruhleben = '000731058' - const zoo = '008010406' - const kastanienallee = '730983' - const [journey] = yield client.journeys(ruhleben, zoo, { - via: kastanienallee, - results: 1, - when, - passedStations: true + const leg = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return passed.station.id === württembergallee + }) }) - - t.ok(journey) - - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === kastanienallee)) - t.ok(l, 'Kastanienallee is not being passed') + t.ok(leg, 'Württembergalle is not being passed') t.end() })) test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { - const model = yield client.journeys(jungfernh, münchenHbf, { + const model = yield client.journeys(jungfernheide, münchenHbf, { results: 3, when }) + // todo: move to journeys validator? t.equal(typeof model.earlierRef, 'string') t.ok(model.earlierRef) t.equal(typeof model.laterRef, 'string') @@ -234,14 +157,18 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { // when and earlierThan/laterThan should be mutually exclusive t.throws(() => { - client.journeys(jungfernh, münchenHbf, { + client.journeys(jungfernheide, münchenHbf, { when, earlierThan: model.earlierRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) t.throws(() => { - client.journeys(jungfernh, münchenHbf, { + client.journeys(jungfernheide, münchenHbf, { when, laterThan: model.laterRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) let earliestDep = Infinity, latestDep = -Infinity @@ -251,7 +178,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { else if (dep > latestDep) latestDep = dep } - const earlier = yield client.journeys(jungfernh, münchenHbf, { + const earlier = yield client.journeys(jungfernheide, münchenHbf, { results: 3, // todo: single journey ref? earlierThan: model.earlierRef @@ -260,7 +187,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { t.ok(new Date(j.legs[0].departure) < earliestDep) } - const later = yield client.journeys(jungfernh, münchenHbf, { + const later = yield client.journeys(jungfernheide, münchenHbf, { results: 3, // todo: single journey ref? laterThan: model.laterRef @@ -272,27 +199,42 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { t.end() })) +test.skip('journey leg details', co(function* (t) { + const journeys = yield client.journeys(berlinHbf, münchenHbf, { + results: 1, when + }) + + const p = journeys[0].legs[0] + t.ok(p.id, 'precondition failed') + t.ok(p.line.name, 'precondition failed') + const leg = yield client.journeyLeg(p.id, p.line.name, {when}) + + validate(t, leg, 'journeyLeg', 'leg') + t.end() +})) + test('departures at Berlin Jungfernheide', co(function* (t) { - const deps = yield client.departures(jungfernh, { + const deps = yield client.departures(jungfernheide, { duration: 5, when }) - t.ok(Array.isArray(deps)) - for (let dep of deps) { - assertValidStation(t, dep.station) - if (!(yield findStation(dep.station.id))) { - console.error('unknown station', dep.station.id, dep.station.name) - } - assertValidWhen(t, dep.when, when) + validate(t, deps, 'departures', 'departures') + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` + // todo: make this pass + // t.equal(dep.station.id, jungfernheide, name + '.station.id is invalid') } + // todo: move into deps validator + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) t.end() })) test('departures with station object', co(function* (t) { - yield client.departures({ + const deps = yield client.departures({ type: 'station', - id: jungfernh, + id: jungfernheide, name: 'Berlin Jungfernheide', location: { type: 'location', @@ -301,7 +243,7 @@ test('departures with station object', co(function* (t) { } }, {when}) - t.ok('did not fail') + validate(t, deps, 'departures', 'departures') t.end() })) @@ -314,18 +256,20 @@ test('nearby Berlin Jungfernheide', co(function* (t) { results: 2, distance: 400 }) - t.ok(Array.isArray(nearby)) + validate(t, nearby, 'locations', 'nearby') + t.equal(nearby.length, 2) - assertIsJungfernheide(t, nearby[0]) - t.ok(nearby[0].distance >= 0) - t.ok(nearby[0].distance <= 100) - - for (let n of nearby) { - if (n.type === 'station') assertValidStation(t, n) - else assertValidLocation(t, n) - } + const s0 = nearby[0] + // todo: trim IDs + t.ok(s0.id === '008011167' || s0.id === jungfernheide) + t.equal(s0.name, 'Berlin Jungfernheide') + t.ok(isRoughlyEqual(s0.location.latitude, 52.530408, .0005)) + t.ok(isRoughlyEqual(s0.location.longitude, 13.299424, .0005)) + t.ok(s0.distance >= 0) + t.ok(s0.distance <= 100) + // todo: nearby[0] t.end() })) @@ -334,29 +278,21 @@ test('locations named Jungfernheide', co(function* (t) { results: 10 }) - t.ok(Array.isArray(locations)) - t.ok(locations.length > 0) + validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 10) - - for (let l of locations) { - if (l.type === 'station') assertValidStation(t, l) - else assertValidLocation(t, l) - } - t.ok(locations.some(isJungfernheide)) + t.ok(locations.some((l) => { + // todo: trim IDs + return l.id === '008011167' || l.id === jungfernheide + }), 'Jungfernheide not found') t.end() })) test('location', co(function* (t) { - const loc = yield client.location(regensburgHbf) + const s = yield client.location(regensburgHbf) - assertValidStation(t, loc) - t.equal(loc.id, regensburgHbf) - - t.ok(Array.isArray(loc.lines)) - if (Array.isArray(loc.lines)) { - for (let line of loc.lines) assertValidLine(t, line) - } + validate(t, s, 'station', 'station') + t.equal(s.id, regensburgHbf) t.end() })) diff --git a/test/lib/validators.js b/test/lib/validators.js index afc76d1e..97e9f352 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -159,6 +159,16 @@ const createValidateJourneyLeg = (cfg) => { const msg = name + '.departure is invalid' a.ok(isValidWhen(leg.departure, cfg.when), msg) } + if (leg.arrivalPlatform !== null) { + const msg = name + '.arrivalPlatform must be a string' + a.strictEqual(typeof leg.arrivalPlatform, 'string', msg) + a.ok(leg.arrivalPlatform, name + '.arrivalPlatform must not be empty') + } + if (leg.departurePlatform !== null) { + const msg = name + '.departurePlatform must be a string' + a.strictEqual(typeof leg.departurePlatform, 'string', msg) + a.ok(leg.departurePlatform, name + '.departurePlatform must not be empty') + } if ('passed' in leg) { a.ok(Array.isArray(leg.passed), name + '.passed must be an array') @@ -168,6 +178,12 @@ const createValidateJourneyLeg = (cfg) => { validate('stopover', leg.passed[i], name + `.passed[${i}]`) } } + + if (leg.mode !== 'walking') { + const msg = name + '.direction must be a string' + a.strictEqual(typeof leg.direction, 'string', msg) + a.ok(leg.direction, name + '.direction must not be empty') + } } return validateJourneyLeg } @@ -214,6 +230,8 @@ const createValidateDeparture = (cfg) => { } validate(['line'], dep.line, name + '.line') + a.strictEqual(typeof dep.direction, 'string', name + '.direction must be a string') + a.ok(dep.direction, name + '.direction must not be empty') } return validateDeparture } @@ -231,6 +249,8 @@ const validateMovement = (validate, m, name = 'movement') => { // todo: let hafas-client add a .type field validate(['line'], v.line, name + '.line') + a.strictEqual(typeof v.direction, 'string', name + '.direction must be a string') + a.ok(v.direction, name + '.direction must not be empty') const lName = name + '.location' validate(['location'], v.location, lName) diff --git a/test/vbb.js b/test/vbb.js index 7995a066..ac194542 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -32,8 +32,6 @@ const cfg = { } const validateDirection = (dir, name) => { - a.strictEqual(typeof dir, 'string', name + ' must be a string') - a.ok(dir, name + ' must not be empty') if (!stations(dir, true, false)[0]) { console.error(name + `: station "${dir}" is unknown`) } @@ -333,6 +331,7 @@ test('departures', co(function* (t) { t.equal(dep.station.name, 'U Spichernstr.', name + '.station.name is invalid') t.equal(dep.station.id, spichernstr, name + '.station.id is invalid') } + // todo: move into deps validator t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) t.end() From e87ccf5a3b360495e4f83c0cccd33581f5fb6bf9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Apr 2018 14:55:51 +0200 Subject: [PATCH 034/176] DB: add currency to price :bug:, enable journeyLeg --- p/db/index.js | 10 ++++++++-- test/db.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/p/db/index.js b/p/db/index.js index 812cb94d..e43c00b1 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -62,7 +62,11 @@ const createParseJourney = (profile, stations, lines, remarks) => { ) { const tariff = j.trfRes.fareSetL[0].fareL[0] if (tariff.prc >= 0) { // wat - res.price = {amount: tariff.prc / 100, hint: null} + res.price = { + amount: tariff.prc / 100, + currency: 'EUR', + hint: null + } } } @@ -96,7 +100,9 @@ const dbProfile = { // todo: parseLocation parseJourney: createParseJourney, - formatStation + formatStation, + + journeyLeg: true } module.exports = dbProfile diff --git a/test/db.js b/test/db.js index dee3e6b1..21dc36e5 100644 --- a/test/db.js +++ b/test/db.js @@ -199,7 +199,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { t.end() })) -test.skip('journey leg details', co(function* (t) { +test('journey leg details', co(function* (t) { const journeys = yield client.journeys(berlinHbf, münchenHbf, { results: 1, when }) From a92afc3c28f34b30da27604ec5b1feeb4da0c7b5 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Apr 2018 15:59:07 +0200 Subject: [PATCH 035/176] test validators: bugfixes :bug: --- test/lib/validators.js | 48 +++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/test/lib/validators.js b/test/lib/validators.js index 97e9f352..17133ffc 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -49,7 +49,9 @@ const validateLocation = (validate, loc, name = 'location') => { a.ok(isObj(loc), name + ' must be an object') if (loc.type === 'station') validate(['station'], loc, name) else if ('id' in loc) validatePoi(validate, loc, name) - else validateAddress(validate, loc, name) + else if (!('name' in loc) && ('address' in loc)) { + validateAddress(validate, loc, name) + } else defaultValidators.location(validate, loc, name) } const validateLocations = (validate, locs, name = 'locations') => { @@ -86,7 +88,7 @@ const createValidateStopover = (cfg) => { a.ok(isValidWhen(s.departure, cfg.when), name + '.departure is invalid') } if (!is(s.arrival) && !is(s.departure)) { - asser.fail(name + ' contains neither arrival nor departure') + a.fail(name + ' contains neither arrival nor departure') } if (is(s.arrivalDelay)) { @@ -159,12 +161,14 @@ const createValidateJourneyLeg = (cfg) => { const msg = name + '.departure is invalid' a.ok(isValidWhen(leg.departure, cfg.when), msg) } - if (leg.arrivalPlatform !== null) { + // todo: leg.arrivalPlatform !== null + if (is(leg.arrivalPlatform)) { const msg = name + '.arrivalPlatform must be a string' a.strictEqual(typeof leg.arrivalPlatform, 'string', msg) a.ok(leg.arrivalPlatform, name + '.arrivalPlatform must not be empty') } - if (leg.departurePlatform !== null) { + // todo: leg.departurePlatform !== null + if (is(leg.departurePlatform)) { const msg = name + '.departurePlatform must be a string' a.strictEqual(typeof leg.departurePlatform, 'string', msg) a.ok(leg.departurePlatform, name + '.departurePlatform must not be empty') @@ -248,32 +252,32 @@ const validateMovement = (validate, m, name = 'movement') => { a.ok(isObj(m), name + ' must be an object') // todo: let hafas-client add a .type field - validate(['line'], v.line, name + '.line') - a.strictEqual(typeof v.direction, 'string', name + '.direction must be a string') - a.ok(v.direction, name + '.direction must not be empty') + validate(['line'], m.line, name + '.line') + a.strictEqual(typeof m.direction, 'string', name + '.direction must be a string') + a.ok(m.direction, name + '.direction must not be empty') const lName = name + '.location' - validate(['location'], v.location, lName) - a.ok(v.location.latitude <= 55, lName + '.latitude is too small') - a.ok(v.location.latitude >= 45, lName + '.latitude is too large') - a.ok(v.location.longitude >= 9, lName + '.longitude is too small') - a.ok(v.location.longitude <= 15, lName + '.longitude is too small') + validate(['location'], m.location, lName) + a.ok(m.location.latitude <= 55, lName + '.latitude is too small') + a.ok(m.location.latitude >= 45, lName + '.latitude is too large') + a.ok(m.location.longitude >= 9, lName + '.longitude is too small') + a.ok(m.location.longitude <= 15, lName + '.longitude is too small') - a.ok(Array.isArray(v.nextStops), name + '.nextStops must be an array') - for (let i = 0; i < v.nextStops.length; i++) { - const st = v.nextStops[i] - assertValidStopover('stopover', v.nextStops[i], name + `.nextStops[${i}]`) + a.ok(Array.isArray(m.nextStops), name + '.nextStops must be an array') + for (let i = 0; i < m.nextStops.length; i++) { + const st = m.nextStops[i] + validate('stopover', m.nextStops[i], name + `.nextStops[${i}]`) } - a.ok(Array.isArray(v.frames), name + '.frames must be an array') - a.ok(v.frames.length > 0, name + '.frames must not be empty') - for (let i = 0; i < v.frames.length; i++) { - const f = v.frames[i] + a.ok(Array.isArray(m.frames), name + '.frames must be an array') + a.ok(m.frames.length > 0, name + '.frames must not be empty') + for (let i = 0; i < m.frames.length; i++) { + const f = m.frames[i] const fName = name + `.frames[${i}]` a.ok(isObj(f), fName + ' must be an object') - assertValidStation(['station'], f.origin, fName + '.origin') - assertValidStation(['station'], f.destination, fName + '.destination') + validate(['location', 'station'], f.origin, fName + '.origin') + validate(['location', 'station'], f.destination, fName + '.destination') a.strictEqual(typeof f.t, 'number', fName + '.frames must be a number') } } From 0ce5e9114fcc072f3e583383bc1161a146b3f3ea Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Apr 2018 16:00:14 +0200 Subject: [PATCH 036/176] INSA tests: use new validators --- test/insa.js | 406 +++++++++++++++++++++------------------------------ 1 file changed, 168 insertions(+), 238 deletions(-) diff --git a/test/insa.js b/test/insa.js index e838e31d..33c6a439 100644 --- a/test/insa.js +++ b/test/insa.js @@ -3,94 +3,56 @@ const tapePromise = require('tape-promise').default const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') -const validateFptf = require('validate-fptf') +const {createWhen} = require('./lib/util') const co = require('./lib/co') const createClient = require('..') const insaProfile = require('../p/insa') -const allProducts = require('../p/insa/products') -const { - assertValidStation: _assertValidStation, - assertValidPoi, - assertValidAddress, - assertValidLocation, - assertValidLine, - assertValidStopover, - hour, - createWhen, - assertValidWhen -} = require('./lib/util.js') +const products = require('../p/insa/products') +const createValidate = require('./lib/validate-fptf-with') + +const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const when = createWhen('Europe/Berlin', 'de-DE') -// todo: DRY with other tests, move into lib -const assertValidStation = (t, s, coordsOptional = false) => { - _assertValidStation(t, s, coordsOptional) - t.ok(s.products) - for (let product of allProducts) { - product = product.id - const msg = `station.products[${product}] must be a boolean` - t.equal(typeof s.products[product], 'boolean', msg) - } +const cfg = { + when, + stationCoordsOptional: false, + products } -const isMagdeburgHbf = s => { - return ( - s.type === 'station' && - (s.id === '8010224' || s.id === '008010224') && - s.name === 'Magdeburg Hbf' && - s.location && - isRoughlyEqual(s.location.latitude, 52.130352, 0.001) && - isRoughlyEqual(s.location.longitude, 11.626891, 0.001) - ) -} - -const assertIsMagdeburgHbf = (t, s) => { - t.equal(s.type, 'station') - t.ok(s.id === '8010224' || s.id === '008010224', 'id should be 8010224') - t.equal(s.name, 'Magdeburg Hbf') - t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 52.130352, 0.001)) - t.ok(isRoughlyEqual(s.location.longitude, 11.626891, 0.001)) -} +const validate = createValidate(cfg, {}) const test = tapePromise(tape) const client = createClient(insaProfile) +const magdeburgHbf = '8010224' +const magdeburgBuckau = '8013456' +const hasselbachplatzSternstrasse = '000006545' +const stendal = '008010334' +const dessau = '008010077' + test('Magdeburg Hbf to Magdeburg-Buckau', co(function*(t) { - const magdeburgHbf = '8010224' - const magdeburgBuckau = '8013456' const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { when, passedStations: true }) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length > 0, 'no journeys') - for (let journey of journeys) { - t.ok(Array.isArray(journey.legs)) - t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] // todo: all legs + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, leg.origin) - assertValidWhen(t, leg.departure, when) - t.equal(typeof leg.departurePlatform, 'string') - - assertValidStation(t, leg.destination) - assertValidWhen(t, leg.arrival, when) - t.equal(typeof leg.arrivalPlatform, 'string') - - assertValidLine(t, leg.line) - - t.ok(Array.isArray(leg.passed)) - for (let stopover of leg.passed) assertValidStopover(t, stopover) + for (let j of journeys) { + const firstLeg = j.legs[0] + const lastLeg = j.legs[j.legs.length - 1] + t.strictEqual(firstLeg.origin.id, magdeburgHbf) + t.strictEqual(lastLeg.destination.id, magdeburgBuckau) } - t.end() })) +// todo: journeys, only one product +// todo: journeys, fails with no product + test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { - const magdeburgHbf = '8010224' const sternStr = { type: 'location', latitude: 52.118414, @@ -101,253 +63,221 @@ test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { const journeys = yield client.journeys(magdeburgHbf, sternStr, { when }) + validate(t, journeys, 'journeys', 'journeys') - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const firstLeg = journey.legs[0] - const lastLeg = journey.legs[journey.legs.length - 1] - - assertValidStation(t, firstLeg.origin) - assertValidWhen(t, firstLeg.departure, when) - assertValidWhen(t, firstLeg.arrival, when) - assertValidWhen(t, lastLeg.departure, when) - assertValidWhen(t, lastLeg.arrival, when) - - const d = lastLeg.destination - assertValidAddress(t, d) - t.equal(d.address, 'Magdeburg - Altenstadt, Sternstraße 10') - t.ok(isRoughlyEqual(0.0001, d.latitude, 52.118414)) - t.ok(isRoughlyEqual(0.0001, d.longitude, 11.422332)) - + for (let journey of journeys) { + const i = journey.legs.length - 1 + const d = journey.legs[i].destination + t.equal(d.address, sternStr.address) + t.ok(isRoughlyEqual(0.0001, d.latitude, sternStr.latitude)) + t.ok(isRoughlyEqual(0.0001, d.longitude, sternStr.longitude)) + } t.end() })) test('Kloster Unser Lieben Frauen to Magdeburg Hbf', co(function*(t) { const kloster = { type: 'location', - latitude: 52.127601, - longitude: 11.636437, + id: '970012223', name: 'Magdeburg, Kloster Unser Lieben Frauen (Denkmal)', - id: '970012223' + latitude: 52.127601, + longitude: 11.636437 } - const magdeburgHbf = '8010224' - const journeys = yield client.journeys(kloster, magdeburgHbf, { + + const journeys = yield client.journeys(magdeburgHbf, kloster, { when }) + validate(t, journeys, 'journeys', 'journeys') - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const firstLeg = journey.legs[0] - const lastLeg = journey.legs[journey.legs.length - 1] - - const o = firstLeg.origin - assertValidPoi(t, o) - t.equal(o.name, 'Magdeburg, Kloster Unser Lieben Frauen (Denkmal)') - t.ok(isRoughlyEqual(0.0001, o.latitude, 52.127601)) - t.ok(isRoughlyEqual(0.0001, o.longitude, 11.636437)) - - assertValidWhen(t, firstLeg.departure, when) - assertValidWhen(t, firstLeg.arrival, when) - assertValidWhen(t, lastLeg.departure, when) - assertValidWhen(t, lastLeg.arrival, when) - - assertValidStation(t, lastLeg.destination) - + for (let journey of journeys) { + const i = journey.legs.length - 1 + const d = journey.legs[i].destination + t.equal(d.id, kloster.id) + t.equal(d.name, kloster.name) + t.ok(isRoughlyEqual(0.0001, d.latitude, kloster.latitude)) + t.ok(isRoughlyEqual(0.0001, d.longitude, kloster.longitude)) + } t.end() })) test('journeys: via works – with detour', co(function* (t) { - // Going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Stendal via Dessau without detour - // is currently impossible. We check if the routing engine computes a detour. - const hasselbachplatzSternstrasse = '000006545' - const stendal = '008010334' - const dessau = '008010077' - const dessauPassed = '8010077' - const [journey] = yield client.journeys(hasselbachplatzSternstrasse, stendal, { + // Going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Stendal + // via Dessau without detour is currently impossible. We check if the routing + // engine computes a detour. + const journeys = yield client.journeys(hasselbachplatzSternstrasse, stendal, { via: dessau, results: 1, when, passedStations: true }) - t.ok(journey) + validate(t, journeys, 'journeys', 'journeys') - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === dessauPassed)) - t.ok(l, 'Dessau is not being passed') + const leg = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return ( + passed.station.id === '8010077' || // todo: trim IDs + passed.station.id === dessau + ) + }) + }) + t.ok(leg, 'Dessau is not being passed') t.end() })) -test('journeys: via works – without detour', co(function* (t) { - // When going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Magdeburg, Universität via Magdeburg, Breiter Weg, there is *no need* - // to change trains / no need for a "detour". - const hasselbachplatzSternstrasse = '000006545' - const universitaet = '000019686' - const breiterWeg = '000013519' - const breiterWegPassed = '13519' - - const [journey] = yield client.journeys(hasselbachplatzSternstrasse, universitaet, { - via: breiterWeg, - results: 1, - when, - passedStations: true +test('earlier/later journeys', co(function* (t) { + const model = yield client.journeys(magdeburgHbf, magdeburgBuckau, { + results: 3, when }) - t.ok(journey) + // todo: move to journeys validator? + t.equal(typeof model.earlierRef, 'string') + t.ok(model.earlierRef) + t.equal(typeof model.laterRef, 'string') + t.ok(model.laterRef) - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === breiterWegPassed)) - t.ok(l, 'Magdeburg, Breiter Weg is not being passed') - - t.end() -})) - -test('departures at Magdeburg Hbf', co(function*(t) { - const magdeburgHbf = '8010224' - const deps = yield client.departures(magdeburgHbf, { - duration: 5, - when + // when and earlierThan/laterThan should be mutually exclusive + t.throws(() => { + client.journeys(magdeburgHbf, magdeburgBuckau, { + when, earlierThan: model.earlierRef + }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) + }) + t.throws(() => { + client.journeys(magdeburgHbf, magdeburgBuckau, { + when, laterThan: model.laterRef + }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) - t.ok(Array.isArray(deps)) - for (let dep of deps) { - assertValidStation(t, dep.station) - assertValidWhen(t, dep.when, when) + let earliestDep = Infinity, latestDep = -Infinity + for (let j of model) { + const dep = +new Date(j.legs[0].departure) + if (dep < earliestDep) earliestDep = dep + else if (dep > latestDep) latestDep = dep } - t.end() -})) - -test('nearby Magdeburg Hbf', co(function*(t) { - const magdeburgHbfPosition = { - type: 'location', - latitude: 52.130352, - longitude: 11.626891 - } - const nearby = yield client.nearby(magdeburgHbfPosition, { - results: 2, - distance: 400 + const earlier = yield client.journeys(magdeburgHbf, magdeburgBuckau, { + results: 3, + // todo: single journey ref? + earlierThan: model.earlierRef }) + for (let j of earlier) { + t.ok(new Date(j.legs[0].departure) < earliestDep) + } - t.ok(Array.isArray(nearby)) - t.equal(nearby.length, 2) - - assertIsMagdeburgHbf(t, nearby[0]) - t.ok(nearby[0].distance >= 0) - t.ok(nearby[0].distance <= 100) - - for (let n of nearby) { - if (n.type === 'station') assertValidStation(t, n) - else assertValidLocation(t, n) + const later = yield client.journeys(magdeburgHbf, magdeburgBuckau, { + results: 3, + // todo: single journey ref? + laterThan: model.laterRef + }) + for (let j of later) { + t.ok(new Date(j.legs[0].departure) > latestDep) } t.end() })) test('journey leg details', co(function* (t) { - const magdeburgHbf = '8010224' - const magdeburgBuckau = '8013456' - const [journey] = yield client.journeys(magdeburgHbf, magdeburgBuckau, { + const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { results: 1, when }) - const p = journey.legs[0] - t.ok(p, 'missing legs[0]') - t.ok(p.id, 'missing legs[0].id') - t.ok(p.line, 'missing legs[0].line') - t.ok(p.line.name, 'missing legs[0].line.name') + const p = journeys[0].legs[0] + t.ok(p.id, 'precondition failed') + t.ok(p.line.name, 'precondition failed') const leg = yield client.journeyLeg(p.id, p.line.name, {when}) - t.equal(typeof leg.id, 'string') - t.ok(leg.id) + validate(t, leg, 'journeyLeg', 'leg') + t.end() +})) - assertValidLine(t, leg.line) +test('departures at Magdeburg Hbf', co(function*(t) { + const deps = yield client.departures(magdeburgHbf, { + duration: 5, + when + }) - t.equal(typeof leg.direction, 'string') - t.ok(leg.direction) + validate(t, deps, 'departures', 'departures') + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` - t.ok(Array.isArray(leg.passed)) - for (let passed of leg.passed) assertValidStopover(t, passed) + // todo: fix this + // t.equal(dep.station.name, 'Magdeburg Hbf', name + '.station.name is invalid') + // t.equal(dep.station.id, magdeburgHbf, name + '.station.id is invalid') + } + // todo: move into deps validator + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) t.end() })) +test('departures with station object', co(function* (t) { + const deps = yield client.departures({ + type: 'station', + id: magdeburgHbf, + name: 'Magdeburg Hbf', + location: { + type: 'location', + latitude: 1.23, + longitude: 2.34 + } + }, {when}) + + validate(t, deps, 'departures', 'departures') + t.end() +})) + +// todo: nearby + test('locations named Magdeburg', co(function*(t) { const locations = yield client.locations('Magdeburg', { - results: 10 + results: 20 }) - t.ok(Array.isArray(locations)) - t.ok(locations.length > 0) - t.ok(locations.length <= 10) + validate(t, locations, 'locations', 'locations') + t.ok(locations.length <= 20) - for (let l of locations) { - if (l.type === 'station') assertValidStation(t, l) - else assertValidLocation(t, l) - } - t.ok(locations.some(isMagdeburgHbf)) + t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.id && s.name)) // POIs + t.ok(locations.some((loc) => { + return ( + loc.id === '008010224' || // todo: trim IDs + loc.id === magdeburgHbf + ) + })) t.end() })) -test('location', co(function*(t) { - const magdeburgBuckau = '8013456' - const loc = yield client.location(magdeburgBuckau) +test('location Magdeburg-Buckau', co(function* (t) { + const s = yield client.location(magdeburgBuckau) - assertValidStation(t, loc) - t.equal(loc.id, magdeburgBuckau) + validate(t, s, 'station', 'station') + t.equal(s.id, magdeburgBuckau) t.end() })) test('radar', co(function* (t) { - const north = 52.148364 - const west = 11.600826 - const south = 52.108486 - const east = 11.651451 - const vehicles = yield client.radar({north, west, south, east}, { + const vehicles = yield client.radar({ + north: 52.148364, + west: 11.600826, + south: 52.108486, + east: 11.651451 + }, { duration: 5 * 60, when, results: 10 }) - t.ok(Array.isArray(vehicles)) - t.ok(vehicles.length > 0) - for (let v of vehicles) { - assertValidLine(t, v.line) + const customCfg = Object.assign({}, cfg, { + stationCoordsOptional: true, // see #28 + }) + const validate = createValidate(customCfg, {}) + validate(t, vehicles, 'movements', 'vehicles') - t.equal(typeof v.location.latitude, 'number') - t.ok(v.location.latitude <= 57, 'vehicle is too far away') - t.ok(v.location.latitude >= 47, 'vehicle is too far away') - t.equal(typeof v.location.longitude, 'number') - t.ok(v.location.longitude >= 8, 'vehicle is too far away') - t.ok(v.location.longitude <= 14, 'vehicle is too far away') - - t.ok(Array.isArray(v.nextStops)) - for (let st of v.nextStops) { - assertValidStopover(t, st, true) - - if (st.arrival) { - t.equal(typeof st.arrival, 'string') - const arr = +new Date(st.arrival) - // note that this can be an ICE train - t.ok(isRoughlyEqual(14 * hour, +when, arr)) - } - if (st.departure) { - t.equal(typeof st.departure, 'string') - const dep = +new Date(st.departure) - // note that this can be an ICE train - t.ok(isRoughlyEqual(14 * hour, +when, dep)) - } - } - - t.ok(Array.isArray(v.frames)) - for (let f of v.frames) { - // see #28 - // todo: check if this works by now - assertValidStation(t, f.origin, true) - assertValidStation(t, f.destination, true) - t.equal(typeof f.t, 'number') - } - } t.end() })) From e1f7e074be6936ef1f9874775f71325b4f3114e3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Apr 2018 18:14:10 +0200 Subject: [PATCH 037/176] NAH.SH tests: use new validators --- test/nahsh.js | 361 ++++++++++++++++++++------------------------------ 1 file changed, 141 insertions(+), 220 deletions(-) diff --git a/test/nahsh.js b/test/nahsh.js index 0e30e0f3..34a62ee3 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -1,57 +1,42 @@ 'use strict' -// todo -// const getStations = require('db-stations').full const tapePromise = require('tape-promise').default const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') -const validateFptf = require('validate-fptf') - -const validateLineWithoutMode = require('./lib/validate-line-without-mode') +const {createWhen} = require('./lib/util') const co = require('./lib/co') const createClient = require('..') const nahshProfile = require('../p/nahsh') -const allProducts = require('../p/nahsh/products') +const products = require('../p/nahsh/products') const { - assertValidStation: _assertValidStation, - assertValidPoi, - assertValidAddress, - assertValidLocation, - assertValidStopover, - hour, createWhen, assertValidWhen -} = require('./lib/util.js') + line: createValidateLine, + station: createValidateStation +} = require('./lib/validators') +const createValidate = require('./lib/validate-fptf-with') const when = createWhen('Europe/Berlin', 'de-DE') -// todo: DRY with other tests, move into lib -const assertValidStation = (t, s, coordsOptional = false) => { - _assertValidStation(t, s, coordsOptional) - t.ok(s.products) - for (let product of allProducts) { - product = product.id - const msg = `station.products[${product}] must be a boolean` - t.equal(typeof s.products[product], 'boolean', msg) +const cfg = { + when, + stationCoordsOptional: false, + products +} + +const _validateLine = createValidateLine(cfg) +const validateLine = (validate, l, name) => { + if (l && l.product === 'onCall') { + // skip line validation + // https://github.com/derhuerst/hafas-client/issues/8#issuecomment-355839965 + l = Object.assign({}, l) + l.mode = 'taxi' } + _validateLine(validate, l, name) } -const isKielHbf = (s) => { - return s.type === 'station' && - (s.id === '8000199') && - s.name === 'Kiel Hbf' && - s.location && - isRoughlyEqual(s.location.latitude, 54.314982, .0005) && - isRoughlyEqual(s.location.longitude, 10.131976, .0005) -} - -const assertIsKielHbf = (t, s) => { - t.equal(s.type, 'station') - t.ok(s.id === '8000199', 'id should be 8000199') - t.equal(s.name, 'Kiel Hbf') - t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 54.314982, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 10.131976, .0005)) -} +const validate = createValidate(cfg, { + line: validateLine +}) const assertValidPrice = (t, p) => { t.ok(p) @@ -65,152 +50,102 @@ const assertValidPrice = (t, p) => { } } -const assertValidLine = (t, l) => { // with optional mode - const validators = Object.assign({}, validateFptf.defaultValidators, { - line: validateLineWithoutMode - }) - const recurse = validateFptf.createRecurse(validators) - try { - recurse(['line'], l, 'line') - } catch (err) { - t.ifError(err) - } -} - const test = tapePromise(tape) const client = createClient(nahshProfile) const kielHbf = '8000199' const flensburg = '8000103' +const holstentor = '970003547' const luebeckHbf = '8000237' const husum = '8000181' const schleswig = '8005362' test('Kiel Hbf to Flensburg', co(function* (t) { const journeys = yield client.journeys(kielHbf, flensburg, { - when, passedStations: true + when, passedStations: true, results: 3 }) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length > 0, 'no journeys') - for (let journey of journeys) { - t.equal(journey.type, 'journey') + validate(t, journeys, 'journeys', 'journeys') + t.strictEqual(journeys.length, 3) + for (let i = 0; i < journeys.length; i++) { + const j = journeys[i] - t.ok(Array.isArray(journey.legs)) - t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] // todo: all legs + const firstLeg = j.legs[0] + const lastLeg = j.legs[j.legs.length - 1] + t.strictEqual(firstLeg.origin.id, kielHbf) + t.strictEqual(lastLeg.destination.id, flensburg) - assertValidStation(t, leg.origin) - // todo - // if (!(yield findStation(leg.origin.id))) { - // console.error('unknown station', leg.origin.id, leg.origin.name) - // } - assertValidWhen(t, leg.departure, when) - t.equal(typeof leg.departurePlatform, 'string') - - assertValidStation(t, leg.destination) - // todo - // if (!(yield findStation(leg.destination.id))) { - // console.error('unknown station', leg.destination.id, leg.destination.name) - // } - assertValidWhen(t, leg.arrival, when) - t.equal(typeof leg.arrivalPlatform, 'string') - - assertValidLine(t, leg.line) - - t.ok(Array.isArray(leg.passed)) - for (let stopover of leg.passed) assertValidStopover(t, stopover) - - if (journey.price) assertValidPrice(t, journey.price) + // todo: find a journey where there pricing info is always available + if (j.price) assertValidPrice(t, j.price) } - t.end() })) +// todo: journeys, only one product +// todo: journeys, fails with no product + test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { + const latitude = 54.475359 + const longitude = 9.050798 const zingel = { type: 'location', - latitude: 54.475359, - longitude: 9.050798, - address: 'Husum, Zingel 10' + address: 'Husum, Zingel 10', + latitude, longitude } - const journeys = yield client.journeys(kielHbf, zingel, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const firstLeg = journey.legs[0] - const lastLeg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, firstLeg.origin) - // todo - // if (!(yield findStation(leg.origin.id))) { - // console.error('unknown station', leg.origin.id, leg.origin.name) - // } - assertValidWhen(t, firstLeg.departure, when) - assertValidWhen(t, firstLeg.arrival, when) - assertValidWhen(t, lastLeg.departure, when) - assertValidWhen(t, lastLeg.arrival, when) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const name = `journeys[0].legs[${i}].destination` - const d = lastLeg.destination - assertValidAddress(t, d) - t.equal(d.address, 'Husum, Zingel 10') - t.ok(isRoughlyEqual(.0001, d.latitude, 54.475359)) - t.ok(isRoughlyEqual(.0001, d.longitude, 9.050798)) + t.strictEqual(d.address, 'Husum, Zingel 10', name + '.address is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') t.end() })) -test('Holstentor to Kiel Hbf', co(function* (t) { - const holstentor = { +test('Kiel Hbf to Holstentor', co(function* (t) { + const latitude = 53.866321 + const longitude = 10.679976 + const name = 'Hansestadt Lübeck, Holstentor (Denkmal)' + const journeys = yield client.journeys(kielHbf, { type: 'location', - latitude: 53.866321, - longitude: 10.679976, - name: 'Hansestadt Lübeck, Holstentor (Denkmal)', - id: '970003547' - } - const journeys = yield client.journeys(holstentor, kielHbf, {when}) + id: holstentor, + name, + latitude, longitude + }, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const firstLeg = journey.legs[0] - const lastLeg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - const o = firstLeg.origin - assertValidPoi(t, o) - t.equal(o.name, 'Hansestadt Lübeck, Holstentor (Denkmal)') - t.ok(isRoughlyEqual(.0001, o.latitude, 53.866321)) - t.ok(isRoughlyEqual(.0001, o.longitude, 10.679976)) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const k = `journeys[0].legs[${i}].destination` - assertValidWhen(t, firstLeg.departure, when) - assertValidWhen(t, firstLeg.arrival, when) - assertValidWhen(t, lastLeg.departure, when) - assertValidWhen(t, lastLeg.arrival, when) - - assertValidStation(t, lastLeg.destination) - // todo - // if (!(yield findStation(leg.destination.id))) { - // console.error('unknown station', leg.destination.id, leg.destination.name) - // } + t.strictEqual(d.id, holstentor, k + '.id is invalid') + t.strictEqual(d.name, name, k + '.name is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), k + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), k + '.longitude is invalid') t.end() })) -test('Husum to Lübeck Hbf with stopover at Husum', co(function* (t) { - const [journey] = yield client.journeys(husum, luebeckHbf, { +test('Husum to Lübeck Hbf with stopover at Kiel Hbf', co(function* (t) { + const journeys = yield client.journeys(husum, luebeckHbf, { via: kielHbf, - results: 1, - when + results: 1, when, passedStations: true }) - const i1 = journey.legs.findIndex(leg => leg.destination.id === kielHbf) - t.ok(i1 >= 0, 'no leg with Kiel Hbf as destination') + validate(t, journeys, 'journeys', 'journeys') - const i2 = journey.legs.findIndex(leg => leg.origin.id === kielHbf) - t.ok(i2 >= 0, 'no leg with Kiel Hbf as origin') - t.ok(i2 > i1, 'leg with Kiel Hbf as origin must be after leg to it') + const leg = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return passed.station.id === kielHbf + }) + }) + t.ok(leg, 'Kiel Hbf is not being passed') t.end() })) @@ -220,6 +155,7 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { results: 3, when }) + // todo: move to journeys validator? t.equal(typeof model.earlierRef, 'string') t.ok(model.earlierRef) t.equal(typeof model.laterRef, 'string') @@ -230,11 +166,15 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { client.journeys(kielHbf, flensburg, { when, earlierThan: model.earlierRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) t.throws(() => { client.journeys(kielHbf, flensburg, { when, laterThan: model.laterRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) let earliestDep = Infinity, latestDep = -Infinity @@ -265,7 +205,7 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { t.end() })) -test('leg details for Flensburg to Husum', co(function* (t) { +test('journey leg details for Flensburg to Husum', co(function* (t) { const journeys = yield client.journeys(flensburg, husum, { results: 1, when }) @@ -275,17 +215,7 @@ test('leg details for Flensburg to Husum', co(function* (t) { t.ok(p.line.name, 'precondition failed') const leg = yield client.journeyLeg(p.id, p.line.name, {when}) - t.equal(typeof leg.id, 'string') - t.ok(leg.id) - - assertValidLine(t, leg.line) - - t.equal(typeof leg.direction, 'string') - t.ok(leg.direction) - - t.ok(Array.isArray(leg.passed)) - for (let passed of leg.passed) assertValidStopover(t, passed) - + validate(t, leg, 'journeyLeg', 'leg') t.end() })) @@ -294,16 +224,34 @@ test('departures at Kiel Hbf', co(function* (t) { duration: 30, when }) - t.ok(Array.isArray(deps)) - for (let dep of deps) { - assertValidStation(t, dep.station) - // todo - // if (!(yield findStation(dep.station.id))) { - // console.error('unknown station', dep.station.id, dep.station.name) - // } - assertValidWhen(t, dep.when, when) - } + validate(t, deps, 'departures', 'departures') + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` + // todo: fix this + // t.equal(dep.station.name, 'Kiel Hauptbahnhof', name + '.station.name is invalid') + // t.equal(dep.station.id, kielHbf, name + '.station.id is invalid') + } + // todo: move into deps validator + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) + + t.end() +})) + +test('departures with station object', co(function* (t) { + const deps = yield client.departures({ + type: 'station', + id: kielHbf, + name: 'Kiel Hbf', + location: { + type: 'location', + latitude: 1.23, + longitude: 2.34 + } + }, {when}) + + validate(t, deps, 'departures', 'departures') t.end() })) @@ -317,92 +265,65 @@ test('nearby Kiel Hbf', co(function* (t) { results: 2, distance: 400 }) + validate(t, nearby, 'locations', 'nearby') + t.ok(Array.isArray(nearby)) t.equal(nearby.length, 2) - assertIsKielHbf(t, nearby[0]) + t.equal(nearby[0].id, kielHbf) + t.equal(nearby[0].name, 'Kiel Hbf') t.ok(nearby[0].distance >= 0) t.ok(nearby[0].distance <= 100) - for (let n of nearby) { - if (n.type === 'station') assertValidStation(t, n) - else assertValidLocation(t, n) - } - t.end() })) test('locations named Kiel', co(function* (t) { const locations = yield client.locations('Kiel', { - results: 10 + results: 20 }) - t.ok(Array.isArray(locations)) - t.ok(locations.length > 0) - t.ok(locations.length <= 10) + validate(t, locations, 'locations', 'locations') + t.ok(locations.length <= 20) - for (let l of locations) { - if (l.type === 'station') assertValidStation(t, l) - else assertValidLocation(t, l) - } - t.ok(locations.some(isKielHbf)) + t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.id && s.name)) // POIs + t.ok(locations.some(l => l.id === kielHbf)) t.end() })) test('location', co(function* (t) { - const loc = yield client.location(schleswig) + const s = yield client.location(kielHbf) - assertValidStation(t, loc) - t.equal(loc.id, schleswig) + validate(t, s, 'station', 'station') + t.equal(s.id, kielHbf) t.end() })) -// todo: see #34 -test.skip('radar Kiel', co(function* (t) { - const vehicles = yield client.radar(54.4, 10.0, 54.2, 10.2, { +test('radar', co(function* (t) { + const vehicles = yield client.radar({ + north: 54.4, + west: 10.0, + south: 54.2, + east: 10.2 + }, { duration: 5 * 60, when }) - t.ok(Array.isArray(vehicles)) - t.ok(vehicles.length > 0) - for (let v of vehicles) { - - // todo - // t.ok(findStation(v.direction)) - assertValidLine(t, v.line) - - t.equal(typeof v.location.latitude, 'number') - t.ok(v.location.latitude <= 57, 'vehicle is too far away') - t.ok(v.location.latitude >= 51, 'vehicle is too far away') - t.equal(typeof v.location.longitude, 'number') - t.ok(v.location.longitude >= 7, 'vehicle is too far away') - t.ok(v.location.longitude <= 13, 'vehicle is too far away') - - t.ok(Array.isArray(v.nextStops)) - for (let st of v.nextStops) { - assertValidStopover(t, st, true) - - if (st.arrival) { - t.equal(typeof st.arrival, 'string') - const arr = +new Date(st.arrival) - // note that this can be an ICE train - t.ok(isRoughlyEqual(14 * hour, +when, arr)) - } - if (st.departure) { - t.equal(typeof st.departure, 'string') - const dep = +new Date(st.departure) - t.ok(isRoughlyEqual(14 * hour, +when, dep)) - } + const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {}) + const validateStation = createValidateStation(cfg) + const validate = createValidate(cfg, { + station: (validate, s, name) => { + s = Object.assign({ + products: allProducts // todo: fix station.products + }, s) + if (!s.name) s.name = 'foo' // todo, see #34 + validateStation(validate, s, name) } + }) + validate(t, vehicles, 'movements', 'vehicles') - t.ok(Array.isArray(v.frames)) - for (let f of v.frames) { - assertValidStation(t, f.origin, true) - assertValidStation(t, f.destination, true) - t.equal(typeof f.t, 'number') - } - } t.end() })) From 279dfa4f8f2c8ffd0f75412cfbb0e1c579abddb4 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 20 Apr 2018 11:04:54 +0200 Subject: [PATCH 038/176] =?UTF-8?q?=C3=96BB=20tests:=20use=20new=20validat?= =?UTF-8?q?ors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/lib/validators.js | 2 +- test/nahsh.js | 1 + test/oebb.js | 404 ++++++++++++++++------------------------- 3 files changed, 157 insertions(+), 250 deletions(-) diff --git a/test/lib/validators.js b/test/lib/validators.js index 17133ffc..42fe3aca 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -72,7 +72,7 @@ const createValidateLine = (cfg) => { const validateLine = (validate, line, name = 'line') => { defaultValidators.line(validate, line, name) - a.ok(validLineModes.includes(line.mode)) + a.ok(validLineModes.includes(line.mode), name + '.mode is invalid') } return validateLine } diff --git a/test/nahsh.js b/test/nahsh.js index 34a62ee3..b0d3c7f0 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -312,6 +312,7 @@ test('radar', co(function* (t) { duration: 5 * 60, when }) + // todo: cfg.stationProductsOptional option const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {}) const validateStation = createValidateStation(cfg) const validate = createValidate(cfg, { diff --git a/test/oebb.js b/test/oebb.js index a29c945c..2913160f 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -1,76 +1,33 @@ 'use strict' -// todo -// const getStations = require('db-stations').full const tapePromise = require('tape-promise').default const tape = require('tape') const isRoughlyEqual = require('is-roughly-equal') -const validateFptf = require('validate-fptf') - -const validateLineWithoutMode = require('./lib/validate-line-without-mode') +const validateLine = require('validate-fptf/line') +const {createWhen} = require('./lib/util') const co = require('./lib/co') const createClient = require('..') const oebbProfile = require('../p/oebb') -const allProducts = require('../p/oebb/products') +const products = require('../p/oebb/products') const { - assertValidStation: _assertValidStation, - assertValidPoi, - assertValidAddress, - assertValidLocation, - assertValidStopover, - hour, createWhen, assertValidWhen -} = require('./lib/util.js') + station: createValidateStation +} = require('./lib/validators') +const createValidate = require('./lib/validate-fptf-with') const when = createWhen('Europe/Vienna', 'de-AT') -// todo: DRY with other tests, move into lib -const assertValidStation = (t, s, coordsOptional = false, productsOptional = false) => { - _assertValidStation(t, s, coordsOptional) - if (s.products || !productsOptional) { - t.ok(s.products) - for (let product of allProducts) { - product = product.id - const msg = `station.products[${product}] must be a boolean` - t.equal(typeof s.products[product], 'boolean', msg) - } - } +const cfg = { + when, + stationCoordsOptional: false, + products } -// todo -// const findStation = (id) => new Promise((yay, nay) => { -// const stations = getStations() -// stations -// .once('error', nay) -// .on('data', (s) => { -// if ( -// s.id === id || -// (s.additionalIds && s.additionalIds.includes(id)) -// ) { -// yay(s) -// stations.destroy() -// } -// }) -// .once('end', yay) -// }) +// todo validateDirection: search list of stations for direction -const isSalzburgHbf = (s) => { - return s.type === 'station' && - (s.id === '008100002' || s.id === '8100002') && - s.name === 'Salzburg Hbf' && - s.location && - isRoughlyEqual(s.location.latitude, 47.812851, .0005) && - isRoughlyEqual(s.location.longitude, 13.045604, .0005) -} - -const assertIsSalzburgHbf = (t, s) => { - t.equal(s.type, 'station') - t.ok(s.id === '008100002' || s.id === '8100002', 'id should be 8100002') - t.equal(s.name, 'Salzburg Hbf') - t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 47.812851, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 13.045604, .0005)) -} +const validate = createValidate(cfg, { + line: validateLine // bypass line validator in lib/validators +}) const assertValidPrice = (t, p) => { t.ok(p) @@ -84,20 +41,6 @@ const assertValidPrice = (t, p) => { } } -// todo: fix this upstream -// see https://github.com/derhuerst/hafas-client/blob/c6e558be217667f1bcdac4a605898eb75ea80374/p/oebb/products.js#L71 -const assertValidLine = (t, l) => { // with optional mode - const validators = Object.assign({}, validateFptf.defaultValidators, { - line: validateLineWithoutMode - }) - const recurse = validateFptf.createRecurse(validators) - try { - recurse(['line'], l, 'line') - } catch (err) { - t.ifError(err) - } -} - const test = tapePromise(tape) const client = createClient(oebbProfile) @@ -106,116 +49,65 @@ const wienWestbahnhof = '1291501' const wien = '1190100' const klagenfurtHbf = '8100085' const muenchenHbf = '8000261' -const grazHbf = '8100173' +const wienRenngasse = '1390186' test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) { const journeys = yield client.journeys(salzburgHbf, wienWestbahnhof, { when, passedStations: true }) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length > 0, 'no journeys') + validate(t, journeys, 'journeys', 'journeys') for (let journey of journeys) { - t.equal(journey.type, 'journey') - - t.ok(Array.isArray(journey.legs)) - t.ok(journey.legs.length > 0, 'no legs') - const leg = journey.legs[0] // todo: all legs - - assertValidStation(t, leg.origin) - // todo - // if (!(yield findStation(leg.origin.id))) { - // console.error('unknown station', leg.origin.id, leg.origin.name) - // } - assertValidWhen(t, leg.departure, when) - t.equal(typeof leg.departurePlatform, 'string') - - assertValidStation(t, leg.destination) - // todo - // if (!(yield findStation(leg.destination.id))) { - // console.error('unknown station', leg.destination.id, leg.destination.name) - // } - assertValidWhen(t, leg.arrival, when) - t.equal(typeof leg.arrivalPlatform, 'string') - - assertValidLine(t, leg.line) - - t.ok(Array.isArray(leg.passed)) - for (let stopover of leg.passed) assertValidStopover(t, stopover) - if (journey.price) assertValidPrice(t, journey.price) } t.end() })) +// todo: journeys, only one product +// todo: journeys, fails with no product + test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) { + const latitude = 48.236216 + const longitude = 16.425863 const wagramerStr = { type: 'location', - latitude: 48.236216, - longitude: 16.425863, - address: '1220 Wien, Wagramer Straße 5' + address: '1220 Wien, Wagramer Straße 5', + latitude, longitude } - const journeys = yield client.journeys(salzburgHbf, wagramerStr, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const firstLeg = journey.legs[0] - const lastLeg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - assertValidStation(t, firstLeg.origin) - // todo - // if (!(yield findStation(leg.origin.id))) { - // console.error('unknown station', leg.origin.id, leg.origin.name) - // } - assertValidWhen(t, firstLeg.departure, when) - assertValidWhen(t, firstLeg.arrival, when) - assertValidWhen(t, lastLeg.departure, when) - assertValidWhen(t, lastLeg.arrival, when) - - const d = lastLeg.destination - assertValidAddress(t, d) - t.equal(d.address, '1220 Wien, Wagramer Straße 5') - t.ok(isRoughlyEqual(.0001, d.latitude, 48.236216)) - t.ok(isRoughlyEqual(.0001, d.longitude, 16.425863)) + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const k = `journeys[0].legs[${i}].destination` + t.equal(d.address, '1220 Wien, Wagramer Straße 5', k + '.address is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), k + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), k + '.longitude is invalid') t.end() })) -test('Albertina to Salzburg Hbf', co(function* (t) { +test('Salzburg Hbf to Albertina', co(function* (t) { + const latitude = 48.204699 + const longitude = 16.368404 const albertina = { type: 'location', - latitude: 48.204699, - longitude: 16.368404, + id: '975900003', name: 'Albertina', - id: '975900003' + latitude, longitude } - const journeys = yield client.journeys(albertina, salzburgHbf, {when}) + const journeys = yield client.journeys(salzburgHbf, albertina, {when}) - t.ok(Array.isArray(journeys)) - t.ok(journeys.length >= 1, 'no journeys') - const journey = journeys[0] - const firstLeg = journey.legs[0] - const lastLeg = journey.legs[journey.legs.length - 1] + validate(t, journeys, 'journeys', 'journeys') - const o = firstLeg.origin - assertValidPoi(t, o) - t.equal(o.name, 'Albertina') - t.ok(isRoughlyEqual(.0001, o.latitude, 48.204699)) - t.ok(isRoughlyEqual(.0001, o.longitude, 16.368404)) - - assertValidWhen(t, firstLeg.departure, when) - assertValidWhen(t, firstLeg.arrival, when) - assertValidWhen(t, lastLeg.departure, when) - assertValidWhen(t, lastLeg.arrival, when) - - assertValidStation(t, lastLeg.destination) - // todo - // if (!(yield findStation(leg.destination.id))) { - // console.error('unknown station', leg.destination.id, leg.destination.name) - // } + const i = journeys[0].legs.length - 1 + const d = journeys[0].legs[i].destination + const k = `journeys[0].legs[${i}].destination` + t.equal(d.name, 'Albertina', k + '.name is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), k + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), k + '.longitude is invalid') t.end() })) @@ -227,40 +119,59 @@ test('journeys: via works – with detour', co(function* (t) { const schottenring = '001390163' const donauinsel = '001392277' const donauinselPassed = '922001' - const [journey] = yield client.journeys(stephansplatz, schottenring, { + const journeys = yield client.journeys(stephansplatz, schottenring, { via: donauinsel, results: 1, when, passedStations: true }) - t.ok(journey) + validate(t, journeys, 'journeys', 'journeys') - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === donauinselPassed)) - t.ok(l, 'Donauinsel is not being passed') + const leg = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return ( + passed.station.id === donauinsel || + passed.station.id === donauinselPassed + ) + }) + }) + t.ok(leg, 'Donauinsel is not being passed') t.end() })) test('journeys: via works – without detour', co(function* (t) { - // When going from Karlsplatz to Praterstern via Museumsquartier, there is *no need* - // to change trains / no need for a "detour". + // When going from Karlsplatz to Praterstern via Museumsquartier, there is + // *no need* to change trains / no need for a "detour". const karlsplatz = '001390461' const praterstern = '001290201' const museumsquartier = '001390171' const museumsquartierPassed = '901014' - const [journey] = yield client.journeys(karlsplatz, praterstern, { + const journeys = yield client.journeys(karlsplatz, praterstern, { via: museumsquartier, results: 1, when, passedStations: true }) - t.ok(journey) + validate(t, journeys, 'journeys', 'journeys') - const l = journey.legs.some(l => l.passed && l.passed.some(p => p.station.id === museumsquartierPassed)) - t.ok(l, 'Weihburggasse is not being passed') + const l1 = journeys[0].legs.some((leg) => { + return ( + leg.destination.id === museumsquartier || + leg.destination.id === museumsquartierPassed + ) + }) + t.notOk(l1, 'transfer at Museumsquartier') + + const l2 = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return passed.station.id === museumsquartierPassed + }) + }) + t.ok(l2, 'Museumsquartier is not being passed') t.end() })) @@ -270,6 +181,7 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t results: 3, when }) + // todo: move to journeys validator? t.equal(typeof model.earlierRef, 'string') t.ok(model.earlierRef) t.equal(typeof model.laterRef, 'string') @@ -280,11 +192,15 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t client.journeys(salzburgHbf, wienWestbahnhof, { when, earlierThan: model.earlierRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) t.throws(() => { client.journeys(salzburgHbf, wienWestbahnhof, { when, laterThan: model.laterRef }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) }) let earliestDep = Infinity, latestDep = -Infinity @@ -325,141 +241,131 @@ test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) { t.ok(p.line.name, 'precondition failed') const leg = yield client.journeyLeg(p.id, p.line.name, {when}) - t.equal(typeof leg.id, 'string') - t.ok(leg.id) + validate(t, leg, 'journeyLeg', 'leg') + t.end() +})) - assertValidLine(t, leg.line) +test('departures at Wien Renngasse', co(function* (t) { + const deps = yield client.departures(wienRenngasse, { + duration: 5, when + }) - t.equal(typeof leg.direction, 'string') - t.ok(leg.direction) + validate(t, deps, 'departures', 'departures') + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` - t.ok(Array.isArray(leg.passed)) - for (let passed of leg.passed) assertValidStopover(t, passed) + // todo: fis this + // ÖBB HAFAS data is just too detailed :P + // t.equal(dep.station.name, 'Wien Renngasse', name + '.station.name is invalid') + // t.equal(dep.station.id, wienRenngasse, name + '.station.id is invalid') + } + // todo: move into deps validator + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) t.end() })) -test('departures at Salzburg Hbf', co(function* (t) { - const deps = yield client.departures(salzburgHbf, { - duration: 5, when - }) - - t.ok(Array.isArray(deps)) - for (let dep of deps) { - assertValidStation(t, dep.station) - // todo - // if (!(yield findStation(dep.station.id))) { - // console.error('unknown station', dep.station.id, dep.station.name) - // } - assertValidWhen(t, dep.when, when) - } +test('departures with station object', co(function* (t) { + const deps = yield client.departures({ + type: 'station', + id: salzburgHbf, + name: 'Salzburg Hbf', + location: { + type: 'location', + latitude: 1.23, + longitude: 2.34 + } + }, {when}) + validate(t, deps, 'departures', 'departures') t.end() })) test('nearby Salzburg Hbf', co(function* (t) { - const salzburgHbfPosition = { + const nearby = yield client.nearby({ type: 'location', longitude: 13.045604, latitude: 47.812851 - } - const nearby = yield client.nearby(salzburgHbfPosition, { - results: 2, distance: 400 + }, { + results: 5, distance: 400 }) - t.ok(Array.isArray(nearby)) - t.equal(nearby.length, 2) + validate(t, nearby, 'locations', 'nearby') + t.equal(nearby.length, 5) - assertIsSalzburgHbf(t, nearby[0]) - t.ok(nearby[0].distance >= 0) - t.ok(nearby[0].distance <= 100) - - for (let n of nearby) { - if (n.type === 'station') assertValidStation(t, n) - else assertValidLocation(t, n) - } + const s = nearby[0] + t.ok(s.id === '008100002' || s.id === '8100002', 'id should be 8100002') + t.equal(s.name, 'Salzburg Hbf') + t.ok(isRoughlyEqual(s.location.latitude, 47.812851, .0005)) + t.ok(isRoughlyEqual(s.location.longitude, 13.045604, .0005)) + t.ok(s.distance >= 0) + t.ok(s.distance <= 100) t.end() })) test('locations named Salzburg', co(function* (t) { const locations = yield client.locations('Salzburg', { - results: 10 + results: 20 }) - t.ok(Array.isArray(locations)) - t.ok(locations.length > 0) - t.ok(locations.length <= 10) + validate(t, locations, 'locations', 'locations') + t.ok(locations.length <= 20) - for (let l of locations) { - if (l.type === 'station') assertValidStation(t, l) - else assertValidLocation(t, l) - } - t.ok(locations.some(isSalzburgHbf)) + t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.id && s.name)) // POIs + t.ok(locations.some(s => s.id === '008100002' || s.id === '8100002')) t.end() })) test('location', co(function* (t) { - const loc = yield client.location(grazHbf) + const loc = yield client.location(wienRenngasse) // todo: find a way to always get products from the API - assertValidStation(t, loc, false, true) - t.equal(loc.id, grazHbf) + // todo: cfg.stationProductsOptional option + const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {}) + const validateStation = createValidateStation(cfg) + const validate = createValidate(cfg, { + station: (validate, s, name) => { + const withFakeProducts = Object.assign({products: allProducts}, s) + validateStation(validate, withFakeProducts, name) + } + }) + validate(t, loc, 'station', 'station') + + t.equal(loc.id, wienRenngasse) t.end() })) test('radar Salzburg', co(function* (t) { - const vehicles = yield client.radar({ + let vehicles = yield client.radar({ north: 47.827203, west: 13.001261, south: 47.773278, east: 13.07562 }, { - duration: 5 * 60, when + duration: 5 * 60, + // when }) - t.ok(Array.isArray(vehicles)) - t.ok(vehicles.length > 0) - for (let v of vehicles) { + // todo: find a way to always get frames from the API + vehicles = vehicles.filter(m => m.frames && m.frames.length > 0) - // todo - // t.ok(findStation(v.direction)) - assertValidLine(t, v.line) + // todo: find a way to always get products from the API + // todo: cfg.stationProductsOptional option + const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {}) + const validateStation = createValidateStation(cfg) + const validate = createValidate(cfg, { + station: (validate, s, name) => { + const withFakeProducts = Object.assign({products: allProducts}, s) + validateStation(validate, withFakeProducts, name) + }, + line: validateLine + }) + validate(t, vehicles, 'movements', 'vehicles') - t.equal(typeof v.location.latitude, 'number') - t.ok(v.location.latitude <= 52, 'vehicle is too far away') - t.ok(v.location.latitude >= 42, 'vehicle is too far away') - t.equal(typeof v.location.longitude, 'number') - t.ok(v.location.longitude >= 10, 'vehicle is too far away') - t.ok(v.location.longitude <= 16, 'vehicle is too far away') - - t.ok(Array.isArray(v.nextStops)) - for (let st of v.nextStops) { - assertValidStopover(t, st, true) - - if (st.arrival) { - t.equal(typeof st.arrival, 'string') - const arr = +new Date(st.arrival) - // note that this can be an ICE train - t.ok(isRoughlyEqual(14 * hour, +when, arr)) - } - if (st.departure) { - t.equal(typeof st.departure, 'string') - const dep = +new Date(st.departure) - t.ok(isRoughlyEqual(14 * hour, +when, dep)) - } - } - - t.ok(Array.isArray(v.frames)) - for (let f of v.frames) { - // there are stations which the API desn't return products for - // todo: find a way to always get products from the API - assertValidStation(t, f.origin, true, true) - assertValidStation(t, f.destination, true, true) - t.equal(typeof f.t, 'number') - } - } t.end() })) From 63e303b6f836d6b11329ac1a8451802db137d196 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 23 Apr 2018 13:56:00 +0200 Subject: [PATCH 039/176] journeys from station to station test: use helper fn --- test/db.js | 17 ++++++++++++---- test/insa.js | 21 ++++++++++---------- test/lib/journeys-station-to-station.js | 20 +++++++++++++++++++ test/nahsh.js | 24 ++++++++++++----------- test/oebb.js | 24 ++++++++++++++++------- test/vbb.js | 26 +++++++++++-------------- 6 files changed, 84 insertions(+), 48 deletions(-) create mode 100644 test/lib/journeys-station-to-station.js diff --git a/test/db.js b/test/db.js index 21dc36e5..89cf28c1 100644 --- a/test/db.js +++ b/test/db.js @@ -15,6 +15,7 @@ const { station: createValidateStation } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') +const testJourneysStationToStation = require('./lib/journeys-station-to-station') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -60,18 +61,26 @@ const client = createClient(dbProfile) const berlinHbf = '8011160' const münchenHbf = '8000261' const jungfernheide = '8011167' +const blnSchwedterStr = '732652' const atze = '991598902' const westhafen = '008089116' const wedding = '008089131' const württembergallee = '731084' const regensburgHbf = '8000309' -test('Berlin Jungfernheide to München Hbf', co(function* (t) { - const journeys = yield client.journeys(jungfernheide, münchenHbf, { - when, passedStations: true +test('journeys – Berlin Schwedter Str. to München Hbf', co(function* (t) { + const journeys = yield client.journeys(blnSchwedterStr, münchenHbf, { + results: 3, when, passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') + yield testJourneysStationToStation({ + test: t, + journeys, + validate, + fromId: blnSchwedterStr, + toId: münchenHbf + }) + // todo: find a journey where there pricing info is always available for (let journey of journeys) { if (journey.price) assertValidPrice(t, journey.price) } diff --git a/test/insa.js b/test/insa.js index 33c6a439..99b48497 100644 --- a/test/insa.js +++ b/test/insa.js @@ -10,6 +10,7 @@ const createClient = require('..') const insaProfile = require('../p/insa') const products = require('../p/insa/products') const createValidate = require('./lib/validate-fptf-with') +const testJourneysStationToStation = require('./lib/journeys-station-to-station') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -32,20 +33,18 @@ const hasselbachplatzSternstrasse = '000006545' const stendal = '008010334' const dessau = '008010077' -test('Magdeburg Hbf to Magdeburg-Buckau', co(function*(t) { +test('journeys – Magdeburg Hbf to Magdeburg-Buckau', co(function* (t) { const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { - when, - passedStations: true + results: 3, when, passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') - - for (let j of journeys) { - const firstLeg = j.legs[0] - const lastLeg = j.legs[j.legs.length - 1] - t.strictEqual(firstLeg.origin.id, magdeburgHbf) - t.strictEqual(lastLeg.destination.id, magdeburgBuckau) - } + yield testJourneysStationToStation({ + test: t, + journeys, + validate, + fromId: magdeburgHbf, + toId: magdeburgBuckau + }) t.end() })) diff --git a/test/lib/journeys-station-to-station.js b/test/lib/journeys-station-to-station.js new file mode 100644 index 00000000..8d44fce4 --- /dev/null +++ b/test/lib/journeys-station-to-station.js @@ -0,0 +1,20 @@ +'use strict' + +const co = require('./co') + +const testJourneysStationToStation = co(function* (cfg) { + const {test: t, journeys, validate, fromId, toId} = cfg + + validate(t, journeys, 'journeys', 'journeys') + t.strictEqual(journeys.length, 3) + for (let i = 0; i < journeys.length; i++) { + const j = journeys[i] + + const firstLeg = j.legs[0] + const lastLeg = j.legs[j.legs.length - 1] + t.strictEqual(firstLeg.origin.id, fromId) + t.strictEqual(lastLeg.destination.id, toId) + } +}) + +module.exports = testJourneysStationToStation diff --git a/test/nahsh.js b/test/nahsh.js index b0d3c7f0..6b41e9b3 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -14,6 +14,7 @@ const { station: createValidateStation } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') +const testJourneysStationToStation = require('./lib/journeys-station-to-station') const when = createWhen('Europe/Berlin', 'de-DE') @@ -60,24 +61,25 @@ const luebeckHbf = '8000237' const husum = '8000181' const schleswig = '8005362' -test('Kiel Hbf to Flensburg', co(function* (t) { +test('journeys – Kiel Hbf to Flensburg', co(function* (t) { const journeys = yield client.journeys(kielHbf, flensburg, { - when, passedStations: true, results: 3 + results: 3, when, passedStations: true + }) + + yield testJourneysStationToStation({ + test: t, + journeys, + validate, + fromId: kielHbf, + toId: flensburg }) - validate(t, journeys, 'journeys', 'journeys') - t.strictEqual(journeys.length, 3) for (let i = 0; i < journeys.length; i++) { const j = journeys[i] - - const firstLeg = j.legs[0] - const lastLeg = j.legs[j.legs.length - 1] - t.strictEqual(firstLeg.origin.id, kielHbf) - t.strictEqual(lastLeg.destination.id, flensburg) - // todo: find a journey where there pricing info is always available - if (j.price) assertValidPrice(t, j.price) + if (j.price) assertValidPrice(t, j.price, `journeys[${i}].price`) } + t.end() })) diff --git a/test/oebb.js b/test/oebb.js index 2913160f..529b0a40 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -14,6 +14,7 @@ const { station: createValidateStation } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') +const testJourneysStationToStation = require('./lib/journeys-station-to-station') const when = createWhen('Europe/Vienna', 'de-AT') @@ -45,20 +46,29 @@ const test = tapePromise(tape) const client = createClient(oebbProfile) const salzburgHbf = '8100002' -const wienWestbahnhof = '1291501' +const wienFickeystr = '911014' const wien = '1190100' +const wienWestbahnhof = '1291501' const klagenfurtHbf = '8100085' const muenchenHbf = '8000261' const wienRenngasse = '1390186' -test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) { - const journeys = yield client.journeys(salzburgHbf, wienWestbahnhof, { - when, passedStations: true +test('journeys – Salzburg Hbf to Wien Westbahnhof', co(function* (t) { + const journeys = yield client.journeys(salzburgHbf, wienFickeystr, { + results: 3, when, passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') - for (let journey of journeys) { - if (journey.price) assertValidPrice(t, journey.price) + yield testJourneysStationToStation({ + test: t, + journeys, + validate, + fromId: salzburgHbf, + toId: wienFickeystr + }) + + for (let i = 0; i < journeys.length; i++) { + const j = journeys[i] + if (j.price) assertValidPrice(t, j.price, `journeys[${i}].price`) } t.end() diff --git a/test/vbb.js b/test/vbb.js index ac194542..e49d5ef1 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -20,8 +20,7 @@ const { movement: _validateMovement } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') - -const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) +const testJourneysStationToStation = require('./lib/journeys-station-to-station') const when = createWhen('Europe/Berlin', 'de-DE') @@ -106,23 +105,20 @@ const württembergallee = '900000026153' const berlinerStr = '900000044201' const landhausstr = '900000043252' -test('journeys – station to station', co(function* (t) { - const journeys = yield client.journeys(spichernstr, amrumerStr, { +test('journeys – Spichernstr. to Bismarckstr.', co(function* (t) { + const journeys = yield client.journeys(spichernstr, bismarckstr, { results: 3, when, passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') - t.strictEqual(journeys.length, 3) - for (let i = 0; i < journeys.length; i++) { - const j = journeys[i] + yield testJourneysStationToStation({ + test: t, + journeys, + validate, + fromId: spichernstr, + toId: bismarckstr + }) + // todo: find a journey where there ticket info is always available - const firstLeg = j.legs[0] - const lastLeg = j.legs[j.legs.length - 1] - t.strictEqual(firstLeg.origin.id, spichernstr) - t.strictEqual(lastLeg.destination.id, amrumerStr) - - // todo: find a journey where there ticket info is always available - } t.end() })) From b36426904196da8a0154409b3d842f7011a2d06e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 23 Apr 2018 15:58:27 +0200 Subject: [PATCH 040/176] Nah.sh: enable radar despite failing test see #34 for more details --- p/nahsh/index.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/p/nahsh/index.js b/p/nahsh/index.js index 563f1ad2..28d1a08d 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -2,6 +2,7 @@ const _parseLocation = require('../../parse/location') const _createParseJourney = require('../../parse/journey') +const _createParseMovement = require('../../parse/movement') const products = require('./products') @@ -77,6 +78,19 @@ const createParseJourney = (profile, stations, lines, remarks) => { return parseJourneyWithTickets } +const createParseMovement = (profile, locations, lines, remarks) => { + const _parseMovement = _createParseMovement(profile, locations, lines, remarks) + const parseMovement = (m) => { + const res = _parseMovement(m) + // filter out empty nextStops entries + res.nextStops = res.nextStops.filter((f) => { + return f.station !== null || f.arrival !== null || f.departure !== null + }) + return res + } + return parseMovement +} + const nahshProfile = { locale: 'de-DE', timezone: 'Europe/Berlin', @@ -87,9 +101,10 @@ const nahshProfile = { parseLocation, parseJourney: createParseJourney, + parseMovement: createParseMovement, journeyLeg: true, - radar: false // todo: see #34 + radar: true // todo: see #34 } module.exports = nahshProfile From 6818635ee4e35bc092a32a617dc55dfb9911085e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 15:24:59 +0200 Subject: [PATCH 041/176] earlier/later journeys test: use helper fn --- test/db.js | 56 ++++--------------------- test/insa.js | 56 ++++--------------------- test/lib/earlier-later-journeys.js | 67 ++++++++++++++++++++++++++++++ test/nahsh.js | 56 ++++--------------------- test/oebb.js | 56 ++++--------------------- test/vbb.js | 56 ++++--------------------- 6 files changed, 102 insertions(+), 245 deletions(-) create mode 100644 test/lib/earlier-later-journeys.js diff --git a/test/db.js b/test/db.js index 89cf28c1..09371669 100644 --- a/test/db.js +++ b/test/db.js @@ -16,6 +16,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -154,57 +155,14 @@ test('journeys: via works – with detour', co(function* (t) { })) test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { - const model = yield client.journeys(jungfernheide, münchenHbf, { - results: 3, when + yield testEarlierLaterJourneys({ + test: t, + fetchJourneys: client.journeys, + validate, + fromId: jungfernheide, + toId: münchenHbf }) - // todo: move to journeys validator? - t.equal(typeof model.earlierRef, 'string') - t.ok(model.earlierRef) - t.equal(typeof model.laterRef, 'string') - t.ok(model.laterRef) - - // when and earlierThan/laterThan should be mutually exclusive - t.throws(() => { - client.journeys(jungfernheide, münchenHbf, { - when, earlierThan: model.earlierRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - t.throws(() => { - client.journeys(jungfernheide, münchenHbf, { - when, laterThan: model.laterRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - - let earliestDep = Infinity, latestDep = -Infinity - for (let j of model) { - const dep = +new Date(j.legs[0].departure) - if (dep < earliestDep) earliestDep = dep - else if (dep > latestDep) latestDep = dep - } - - const earlier = yield client.journeys(jungfernheide, münchenHbf, { - results: 3, - // todo: single journey ref? - earlierThan: model.earlierRef - }) - for (let j of earlier) { - t.ok(new Date(j.legs[0].departure) < earliestDep) - } - - const later = yield client.journeys(jungfernheide, münchenHbf, { - results: 3, - // todo: single journey ref? - laterThan: model.laterRef - }) - for (let j of later) { - t.ok(new Date(j.legs[0].departure) > latestDep) - } - t.end() })) diff --git a/test/insa.js b/test/insa.js index 99b48497..2ce5a0c8 100644 --- a/test/insa.js +++ b/test/insa.js @@ -11,6 +11,7 @@ const insaProfile = require('../p/insa') const products = require('../p/insa/products') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -126,57 +127,14 @@ test('journeys: via works – with detour', co(function* (t) { })) test('earlier/later journeys', co(function* (t) { - const model = yield client.journeys(magdeburgHbf, magdeburgBuckau, { - results: 3, when + yield testEarlierLaterJourneys({ + test: t, + fetchJourneys: client.journeys, + validate, + fromId: magdeburgHbf, + toId: magdeburgBuckau }) - // todo: move to journeys validator? - t.equal(typeof model.earlierRef, 'string') - t.ok(model.earlierRef) - t.equal(typeof model.laterRef, 'string') - t.ok(model.laterRef) - - // when and earlierThan/laterThan should be mutually exclusive - t.throws(() => { - client.journeys(magdeburgHbf, magdeburgBuckau, { - when, earlierThan: model.earlierRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - t.throws(() => { - client.journeys(magdeburgHbf, magdeburgBuckau, { - when, laterThan: model.laterRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - - let earliestDep = Infinity, latestDep = -Infinity - for (let j of model) { - const dep = +new Date(j.legs[0].departure) - if (dep < earliestDep) earliestDep = dep - else if (dep > latestDep) latestDep = dep - } - - const earlier = yield client.journeys(magdeburgHbf, magdeburgBuckau, { - results: 3, - // todo: single journey ref? - earlierThan: model.earlierRef - }) - for (let j of earlier) { - t.ok(new Date(j.legs[0].departure) < earliestDep) - } - - const later = yield client.journeys(magdeburgHbf, magdeburgBuckau, { - results: 3, - // todo: single journey ref? - laterThan: model.laterRef - }) - for (let j of later) { - t.ok(new Date(j.legs[0].departure) > latestDep) - } - t.end() })) diff --git a/test/lib/earlier-later-journeys.js b/test/lib/earlier-later-journeys.js new file mode 100644 index 00000000..a9323815 --- /dev/null +++ b/test/lib/earlier-later-journeys.js @@ -0,0 +1,67 @@ +'use strict' + +const co = require('./co') + +const testEarlierLaterJourneys = co(function* (cfg) { + const { + test: t, + fetchJourneys, + fromId, + toId, + when, + // todo: validate + } = cfg + + const model = yield fetchJourneys(fromId, toId, { + results: 3, when + }) + + // todo: move to journeys validator? + t.equal(typeof model.earlierRef, 'string') + t.ok(model.earlierRef) + t.equal(typeof model.laterRef, 'string') + t.ok(model.laterRef) + + // when and earlierThan/laterThan should be mutually exclusive + t.throws(() => { + fetchJourneys(fromId, toId, { + when, earlierThan: model.earlierRef + }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) + }) + t.throws(() => { + fetchJourneys(fromId, toId, { + when, laterThan: model.laterRef + }) + // silence rejections, we're only interested in exceptions + .catch(() => {}) + }) + + let earliestDep = Infinity, latestDep = -Infinity + for (let j of model) { + const dep = +new Date(j.legs[0].departure) + if (dep < earliestDep) earliestDep = dep + else if (dep > latestDep) latestDep = dep + } + + const earlier = yield fetchJourneys(fromId, toId, { + results: 3, + // todo: single journey ref? + earlierThan: model.earlierRef + }) + for (let j of earlier) { + t.ok(new Date(j.legs[0].departure) < earliestDep) + } + + const later = yield fetchJourneys(fromId, toId, { + results: 3, + // todo: single journey ref? + laterThan: model.laterRef + }) + for (let j of later) { + t.ok(new Date(j.legs[0].departure) > latestDep) + } +}) + +module.exports = testEarlierLaterJourneys diff --git a/test/nahsh.js b/test/nahsh.js index 6b41e9b3..dfe1f0af 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -15,6 +15,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Berlin', 'de-DE') @@ -153,57 +154,14 @@ test('Husum to Lübeck Hbf with stopover at Kiel Hbf', co(function* (t) { })) test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { - const model = yield client.journeys(kielHbf, flensburg, { - results: 3, when + yield testEarlierLaterJourneys({ + test: t, + fetchJourneys: client.journeys, + validate, + fromId: kielHbf, + toId: flensburg }) - // todo: move to journeys validator? - t.equal(typeof model.earlierRef, 'string') - t.ok(model.earlierRef) - t.equal(typeof model.laterRef, 'string') - t.ok(model.laterRef) - - // when and earlierThan/laterThan should be mutually exclusive - t.throws(() => { - client.journeys(kielHbf, flensburg, { - when, earlierThan: model.earlierRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - t.throws(() => { - client.journeys(kielHbf, flensburg, { - when, laterThan: model.laterRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - - let earliestDep = Infinity, latestDep = -Infinity - for (let j of model) { - const dep = +new Date(j.legs[0].departure) - if (dep < earliestDep) earliestDep = dep - else if (dep > latestDep) latestDep = dep - } - - const earlier = yield client.journeys(kielHbf, flensburg, { - results: 3, - // todo: single journey ref? - earlierThan: model.earlierRef - }) - for (let j of earlier) { - t.ok(new Date(j.legs[0].departure) < earliestDep) - } - - const later = yield client.journeys(kielHbf, flensburg, { - results: 3, - // todo: single journey ref? - laterThan: model.laterRef - }) - for (let j of later) { - t.ok(new Date(j.legs[0].departure) > latestDep) - } - t.end() })) diff --git a/test/oebb.js b/test/oebb.js index 529b0a40..90083c06 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -15,6 +15,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Vienna', 'de-AT') @@ -187,57 +188,14 @@ test('journeys: via works – without detour', co(function* (t) { })) test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t) { - const model = yield client.journeys(salzburgHbf, wienWestbahnhof, { - results: 3, when + yield testEarlierLaterJourneys({ + test: t, + fetchJourneys: client.journeys, + validate, + fromId: salzburgHbf, + toId: wienWestbahnhof }) - // todo: move to journeys validator? - t.equal(typeof model.earlierRef, 'string') - t.ok(model.earlierRef) - t.equal(typeof model.laterRef, 'string') - t.ok(model.laterRef) - - // when and earlierThan/laterThan should be mutually exclusive - t.throws(() => { - client.journeys(salzburgHbf, wienWestbahnhof, { - when, earlierThan: model.earlierRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - t.throws(() => { - client.journeys(salzburgHbf, wienWestbahnhof, { - when, laterThan: model.laterRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - - let earliestDep = Infinity, latestDep = -Infinity - for (let j of model) { - const dep = +new Date(j.legs[0].departure) - if (dep < earliestDep) earliestDep = dep - else if (dep > latestDep) latestDep = dep - } - - const earlier = yield client.journeys(salzburgHbf, wienWestbahnhof, { - results: 3, - // todo: single journey ref? - earlierThan: model.earlierRef - }) - for (let j of earlier) { - t.ok(new Date(j.legs[0].departure) < earliestDep) - } - - const later = yield client.journeys(salzburgHbf, wienWestbahnhof, { - results: 3, - // todo: single journey ref? - laterThan: model.laterRef - }) - for (let j of later) { - t.ok(new Date(j.legs[0].departure) > latestDep) - } - t.end() })) diff --git a/test/vbb.js b/test/vbb.js index e49d5ef1..4b31a1dd 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -21,6 +21,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Berlin', 'de-DE') @@ -180,57 +181,14 @@ test('journeys – fails with no product', (t) => { }) test('earlier/later journeys', co(function* (t) { - const model = yield client.journeys(spichernstr, bismarckstr, { - results: 3, when + yield testEarlierLaterJourneys({ + test: t, + fetchJourneys: client.journeys, + validate, + fromId: spichernstr, + toId: bismarckstr }) - // todo: move to journeys validator? - t.equal(typeof model.earlierRef, 'string') - t.ok(model.earlierRef) - t.equal(typeof model.laterRef, 'string') - t.ok(model.laterRef) - - // when and earlierThan/laterThan should be mutually exclusive - t.throws(() => { - client.journeys(spichernstr, bismarckstr, { - when, earlierThan: model.earlierRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - t.throws(() => { - client.journeys(spichernstr, bismarckstr, { - when, laterThan: model.laterRef - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - }) - - let earliestDep = Infinity, latestDep = -Infinity - for (let j of model) { - const dep = +new Date(j.legs[0].departure) - if (dep < earliestDep) earliestDep = dep - else if (dep > latestDep) latestDep = dep - } - - const earlier = yield client.journeys(spichernstr, bismarckstr, { - results: 3, - // todo: single journey ref? - earlierThan: model.earlierRef - }) - for (let j of earlier) { - t.ok(new Date(j.legs[0].departure) < earliestDep) - } - - const later = yield client.journeys(spichernstr, bismarckstr, { - results: 3, - // todo: single journey ref? - laterThan: model.laterRef - }) - for (let j of later) { - t.ok(new Date(j.legs[0].departure) > latestDep) - } - t.end() })) From 10183695764671853244d3ce402518b92115dac3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 15:42:53 +0200 Subject: [PATCH 042/176] journeys from station to address tests: use helper fn --- test/db.js | 34 +++++++++++++------------ test/insa.js | 22 ++++++++-------- test/lib/journeys-station-to-address.js | 29 +++++++++++++++++++++ test/nahsh.js | 27 ++++++++++---------- test/oebb.js | 26 +++++++++---------- test/vbb.js | 32 +++++++++++------------ 6 files changed, 100 insertions(+), 70 deletions(-) create mode 100644 test/lib/journeys-station-to-address.js diff --git a/test/db.js b/test/db.js index 09371669..10461cb9 100644 --- a/test/db.js +++ b/test/db.js @@ -16,6 +16,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -92,23 +93,24 @@ test('journeys – Berlin Schwedter Str. to München Hbf', co(function* (t) { // todo: journeys, only one product // todo: journeys, fails with no product -test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) { - const latitude = 52.5416823 - const longitude = 13.3491223 - const journeys = yield client.journeys(jungfernheide, { - type: 'location', address: 'Torfstraße 17', - latitude, longitude - }, {when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const name = `journeys[0].legs[${i}].destination` - t.equal(d.address, 'Torfstraße 17', name + '.address is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') +test('Berlin Schwedter Str. to Torfstraße 17', co(function* (t) { + const torfstr = { + type: 'location', + address: 'Torfstraße 17', + latitude: 52.5416823, + longitude: 13.3491223 + } + const journeys = yield client.journeys(blnSchwedterStr, torfstr, { + results: 3, when + }) + yield testJourneysStationToAddress({ + test: t, + journeys, + validate, + fromId: blnSchwedterStr, + to: torfstr + }) t.end() })) diff --git a/test/insa.js b/test/insa.js index 2ce5a0c8..271a1389 100644 --- a/test/insa.js +++ b/test/insa.js @@ -11,6 +11,7 @@ const insaProfile = require('../p/insa') const products = require('../p/insa/products') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -55,23 +56,22 @@ test('journeys – Magdeburg Hbf to Magdeburg-Buckau', co(function* (t) { test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { const sternStr = { type: 'location', + address: 'Magdeburg - Altenstadt, Sternstraße 10', latitude: 52.118414, - longitude: 11.422332, - address: 'Magdeburg - Altenstadt, Sternstraße 10' + longitude: 11.422332 } const journeys = yield client.journeys(magdeburgHbf, sternStr, { - when + results: 3, when }) - validate(t, journeys, 'journeys', 'journeys') - for (let journey of journeys) { - const i = journey.legs.length - 1 - const d = journey.legs[i].destination - t.equal(d.address, sternStr.address) - t.ok(isRoughlyEqual(0.0001, d.latitude, sternStr.latitude)) - t.ok(isRoughlyEqual(0.0001, d.longitude, sternStr.longitude)) - } + yield testJourneysStationToAddress({ + test: t, + journeys, + validate, + fromId: magdeburgHbf, + to: sternStr + }) t.end() })) diff --git a/test/lib/journeys-station-to-address.js b/test/lib/journeys-station-to-address.js new file mode 100644 index 00000000..f7e9f9fb --- /dev/null +++ b/test/lib/journeys-station-to-address.js @@ -0,0 +1,29 @@ +'use strict' + +const isRoughlyEqual = require('is-roughly-equal') + +const co = require('./co') + +const testJourneysStationToAddress = co(function* (cfg) { + const {test: t, journeys, validate, fromId} = cfg + const {address, latitude, longitude} = cfg.to + + validate(t, journeys, 'journeys', 'journeys') + t.strictEqual(journeys.length, 3) + for (let i = 0; i < journeys.length; i++) { + const j = journeys[i] + + const firstLeg = j.legs[0] + t.strictEqual(firstLeg.origin.id, fromId) + + const d = j.legs[j.legs.length - 1].destination + const n = `journeys[0].legs[${i}].destination` + + t.strictEqual(d.type, 'location', n + '.type is invalid') + t.strictEqual(d.address, address, n + '.address is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), n + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), n + '.longitude is invalid') + } +}) + +module.exports = testJourneysStationToAddress diff --git a/test/nahsh.js b/test/nahsh.js index dfe1f0af..6cf62138 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -15,6 +15,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Berlin', 'de-DE') @@ -88,25 +89,23 @@ test('journeys – Kiel Hbf to Flensburg', co(function* (t) { // todo: journeys, fails with no product test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { - const latitude = 54.475359 - const longitude = 9.050798 const zingel = { type: 'location', address: 'Husum, Zingel 10', - latitude, longitude + latitude: 54.475359, + longitude: 9.050798 } - const journeys = yield client.journeys(kielHbf, zingel, {when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const name = `journeys[0].legs[${i}].destination` - - t.strictEqual(d.address, 'Husum, Zingel 10', name + '.address is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') + const journeys = yield client.journeys(kielHbf, zingel, { + results: 3, when + }) + yield testJourneysStationToAddress({ + test: t, + journeys, + validate, + fromId: kielHbf, + to: zingel + }) t.end() })) diff --git a/test/oebb.js b/test/oebb.js index 90083c06..e4ceb148 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -15,6 +15,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Vienna', 'de-AT') @@ -79,24 +80,23 @@ test('journeys – Salzburg Hbf to Wien Westbahnhof', co(function* (t) { // todo: journeys, fails with no product test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) { - const latitude = 48.236216 - const longitude = 16.425863 const wagramerStr = { type: 'location', address: '1220 Wien, Wagramer Straße 5', - latitude, longitude + latitude: 48.236216, + longitude: 16.425863 } - const journeys = yield client.journeys(salzburgHbf, wagramerStr, {when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const k = `journeys[0].legs[${i}].destination` - t.equal(d.address, '1220 Wien, Wagramer Straße 5', k + '.address is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), k + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), k + '.longitude is invalid') + const journeys = yield client.journeys(salzburgHbf, wagramerStr, { + results: 3, when + }) + yield testJourneysStationToAddress({ + test: t, + journeys, + validate, + fromId: salzburgHbf, + to: wagramerStr + }) t.end() })) diff --git a/test/vbb.js b/test/vbb.js index 4b31a1dd..95195d38 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -21,6 +21,7 @@ const { } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Berlin', 'de-DE') @@ -207,24 +208,23 @@ test('journey leg details', co(function* (t) { })) test('journeys – station to address', co(function* (t) { - const latitude = 52.541797 - const longitude = 13.350042 - const journeys = yield client.journeys(spichernstr, { + const torfstr = { type: 'location', - address: 'Torfstr. 17, Berlin', - latitude, longitude - }, {results: 1, when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const name = `journeys[0].legs[${i}].destination` - - t.strictEqual(d.address, '13353 Berlin-Wedding, Torfstr. 17', name + '.address is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') + address: '13353 Berlin-Wedding, Torfstr. 17', + latitude: 52.541797, + longitude: 13.350042 + } + const journeys = yield client.journeys(spichernstr, torfstr, { + results: 3, when + }) + yield testJourneysStationToAddress({ + test: t, + journeys, + validate, + fromId: spichernstr, + to: torfstr + }) t.end() })) From 3d69ae225d9fb4807c964abce7284a6cc8bf5d06 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 16:59:35 +0200 Subject: [PATCH 043/176] DB: tram mode should be `train` :bug: --- p/db/products.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/db/products.js b/p/db/products.js index fa683e2b..3381f401 100644 --- a/p/db/products.js +++ b/p/db/products.js @@ -68,7 +68,7 @@ module.exports = [ }, { id: 'tram', - mode: 'tram', + mode: 'train', bitmasks: [256], name: 'Tram', short: 'T', From 9501dab9f4a8dadf49543c4ee2ef54b199ef20fb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 16:10:56 +0200 Subject: [PATCH 044/176] journeys from station to POI tests: use helper fn --- test/db.js | 36 +++++++++++++++++++----------------- test/insa.js | 22 ++++++++++------------ test/nahsh.js | 37 +++++++++++++++++-------------------- test/oebb.js | 26 +++++++++++++------------- test/vbb.js | 34 ++++++++++++++++------------------ 5 files changed, 75 insertions(+), 80 deletions(-) diff --git a/test/db.js b/test/db.js index 10461cb9..5041ea99 100644 --- a/test/db.js +++ b/test/db.js @@ -17,6 +17,7 @@ const { const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') const testJourneysStationToAddress = require('./lib/journeys-station-to-address') +const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -64,7 +65,6 @@ const berlinHbf = '8011160' const münchenHbf = '8000261' const jungfernheide = '8011167' const blnSchwedterStr = '732652' -const atze = '991598902' const westhafen = '008089116' const wedding = '008089131' const württembergallee = '731084' @@ -114,23 +114,25 @@ test('Berlin Schwedter Str. to Torfstraße 17', co(function* (t) { t.end() })) -test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { - const latitude = 52.542417 - const longitude = 13.350437 - const journeys = yield client.journeys(jungfernheide, { - type: 'location', id: atze, name: 'ATZE Musiktheater', - latitude, longitude - }, {when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const name = `journeys[0].legs[${i}].destination` - t.equal(d.name, 'ATZE Musiktheater', name + '.name is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') +test('Berlin Schwedter Str. to ATZE Musiktheater', co(function* (t) { + const atze = { + type: 'location', + id: '991598902', + name: 'ATZE Musiktheater', + latitude: 52.542417, + longitude: 13.350437 + } + const journeys = yield client.journeys(blnSchwedterStr, atze, { + results: 3, when + }) + yield testJourneysStationToPoi({ + test: t, + journeys, + validate, + fromId: blnSchwedterStr, + to: atze + }) t.end() })) diff --git a/test/insa.js b/test/insa.js index 271a1389..0f2072c0 100644 --- a/test/insa.js +++ b/test/insa.js @@ -12,6 +12,7 @@ const products = require('../p/insa/products') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') const testJourneysStationToAddress = require('./lib/journeys-station-to-address') +const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -75,7 +76,7 @@ test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { t.end() })) -test('Kloster Unser Lieben Frauen to Magdeburg Hbf', co(function*(t) { +test('Magdeburg Hbf to Kloster Unser Lieben Frauen', co(function*(t) { const kloster = { type: 'location', id: '970012223', @@ -83,20 +84,17 @@ test('Kloster Unser Lieben Frauen to Magdeburg Hbf', co(function*(t) { latitude: 52.127601, longitude: 11.636437 } - const journeys = yield client.journeys(magdeburgHbf, kloster, { - when + results: 3, when }) - validate(t, journeys, 'journeys', 'journeys') - for (let journey of journeys) { - const i = journey.legs.length - 1 - const d = journey.legs[i].destination - t.equal(d.id, kloster.id) - t.equal(d.name, kloster.name) - t.ok(isRoughlyEqual(0.0001, d.latitude, kloster.latitude)) - t.ok(isRoughlyEqual(0.0001, d.longitude, kloster.longitude)) - } + yield testJourneysStationToPoi({ + test: t, + journeys, + validate, + fromId: magdeburgHbf, + to: kloster + }) t.end() })) diff --git a/test/nahsh.js b/test/nahsh.js index 6cf62138..12756e03 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -16,6 +16,7 @@ const { const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') const testJourneysStationToAddress = require('./lib/journeys-station-to-address') +const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Berlin', 'de-DE') @@ -58,7 +59,6 @@ const client = createClient(nahshProfile) const kielHbf = '8000199' const flensburg = '8000103' -const holstentor = '970003547' const luebeckHbf = '8000237' const husum = '8000181' const schleswig = '8005362' @@ -110,27 +110,24 @@ test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { })) test('Kiel Hbf to Holstentor', co(function* (t) { - const latitude = 53.866321 - const longitude = 10.679976 - const name = 'Hansestadt Lübeck, Holstentor (Denkmal)' - const journeys = yield client.journeys(kielHbf, { + const holstentor = { type: 'location', - id: holstentor, - name, - latitude, longitude - }, {when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const k = `journeys[0].legs[${i}].destination` - - t.strictEqual(d.id, holstentor, k + '.id is invalid') - t.strictEqual(d.name, name, k + '.name is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), k + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), k + '.longitude is invalid') + id: '970003547', + name: 'Hansestadt Lübeck, Holstentor (Denkmal)', + latitude: 53.866321, + longitude: 10.679976 + } + const journeys = yield client.journeys(kielHbf, holstentor, { + results: 3, when + }) + yield testJourneysStationToPoi({ + test: t, + journeys, + validate, + fromId: kielHbf, + to: holstentor + }) t.end() })) diff --git a/test/oebb.js b/test/oebb.js index e4ceb148..9a0480f2 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -16,6 +16,7 @@ const { const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') const testJourneysStationToAddress = require('./lib/journeys-station-to-address') +const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Vienna', 'de-AT') @@ -101,25 +102,24 @@ test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) { })) test('Salzburg Hbf to Albertina', co(function* (t) { - const latitude = 48.204699 - const longitude = 16.368404 const albertina = { type: 'location', id: '975900003', name: 'Albertina', - latitude, longitude + latitude: 48.204699, + longitude: 16.368404 } - const journeys = yield client.journeys(salzburgHbf, albertina, {when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const k = `journeys[0].legs[${i}].destination` - t.equal(d.name, 'Albertina', k + '.name is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), k + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), k + '.longitude is invalid') + const journeys = yield client.journeys(salzburgHbf, albertina, { + results: 3, when + }) + yield testJourneysStationToPoi({ + test: t, + journeys, + validate, + fromId: salzburgHbf, + to: albertina + }) t.end() })) diff --git a/test/vbb.js b/test/vbb.js index 95195d38..a34c05bc 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -22,6 +22,7 @@ const { const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') const testJourneysStationToAddress = require('./lib/journeys-station-to-address') +const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const when = createWhen('Europe/Berlin', 'de-DE') @@ -100,7 +101,6 @@ const client = createClient(vbbProfile) const amrumerStr = '900000009101' const spichernstr = '900000042101' const bismarckstr = '900000024201' -const atze = '900980720' const westhafen = '900000001201' const wedding = '900000009104' const württembergallee = '900000026153' @@ -229,26 +229,24 @@ test('journeys – station to address', co(function* (t) { })) test('journeys – station to POI', co(function* (t) { - const latitude = 52.543333 - const longitude = 13.351686 - const journeys = yield client.journeys(spichernstr, { + const atze = { type: 'location', - id: atze, + id: '900980720', name: 'Berlin, Atze Musiktheater für Kinder', - latitude, longitude - }, {results: 1, when}) - - validate(t, journeys, 'journeys', 'journeys') - - const i = journeys[0].legs.length - 1 - const d = journeys[0].legs[i].destination - const name = `journeys[0].legs[${i}].destination` - - t.strictEqual(d.id, atze, name + '.id is invalid') - t.strictEqual(d.name, 'Berlin, Atze Musiktheater für Kinder', name + '.name is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), name + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), name + '.longitude is invalid') + latitude: 52.543333, + longitude: 13.351686 + } + const journeys = yield client.journeys(spichernstr, atze, { + results: 3, when + }) + yield testJourneysStationToPoi({ + test: t, + journeys, + validate, + fromId: spichernstr, + to: atze + }) t.end() })) From cb5e01ed5bd69e89500f605c41622c9cc5d395b2 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Apr 2018 16:58:17 +0200 Subject: [PATCH 045/176] departures tests: use helper fn --- test/db.js | 21 +++++++++------------ test/insa.js | 27 +++++++++++---------------- test/nahsh.js | 21 ++++++++------------- test/oebb.js | 31 ++++++++++++++----------------- test/vbb.js | 22 ++++++++++------------ 5 files changed, 52 insertions(+), 70 deletions(-) diff --git a/test/db.js b/test/db.js index 5041ea99..79895f2d 100644 --- a/test/db.js +++ b/test/db.js @@ -19,6 +19,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testDepartures = require('./lib/departures') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -184,21 +185,17 @@ test('journey leg details', co(function* (t) { t.end() })) -test('departures at Berlin Jungfernheide', co(function* (t) { - const deps = yield client.departures(jungfernheide, { +test('departures at Berlin Schwedter Str.', co(function* (t) { + const departures = yield client.departures(blnSchwedterStr, { duration: 5, when }) - validate(t, deps, 'departures', 'departures') - for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - const name = `deps[${i}]` - // todo: make this pass - // t.equal(dep.station.id, jungfernheide, name + '.station.id is invalid') - } - // todo: move into deps validator - t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) - + yield testDepartures({ + test: t, + departures, + validate, + id: blnSchwedterStr + }) t.end() })) diff --git a/test/insa.js b/test/insa.js index 0f2072c0..59e0f331 100644 --- a/test/insa.js +++ b/test/insa.js @@ -14,6 +14,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testDepartures = require('./lib/departures') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -32,6 +33,7 @@ const client = createClient(insaProfile) const magdeburgHbf = '8010224' const magdeburgBuckau = '8013456' +const leiterstr = '7464' const hasselbachplatzSternstrasse = '000006545' const stendal = '008010334' const dessau = '008010077' @@ -150,24 +152,17 @@ test('journey leg details', co(function* (t) { t.end() })) -test('departures at Magdeburg Hbf', co(function*(t) { - const deps = yield client.departures(magdeburgHbf, { - duration: 5, - when +test('departures at Magdeburg Leiterstr.', co(function*(t) { + const departures = yield client.departures(leiterstr, { + duration: 5, when }) - validate(t, deps, 'departures', 'departures') - for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - const name = `deps[${i}]` - - // todo: fix this - // t.equal(dep.station.name, 'Magdeburg Hbf', name + '.station.name is invalid') - // t.equal(dep.station.id, magdeburgHbf, name + '.station.id is invalid') - } - // todo: move into deps validator - t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) - + yield testDepartures({ + test: t, + departures, + validate, + id: leiterstr + }) t.end() })) diff --git a/test/nahsh.js b/test/nahsh.js index 12756e03..3c2db30d 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -18,6 +18,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testDepartures = require('./lib/departures') const when = createWhen('Europe/Berlin', 'de-DE') @@ -176,22 +177,16 @@ test('journey leg details for Flensburg to Husum', co(function* (t) { })) test('departures at Kiel Hbf', co(function* (t) { - const deps = yield client.departures(kielHbf, { + const departures = yield client.departures(kielHbf, { duration: 30, when }) - validate(t, deps, 'departures', 'departures') - for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - const name = `deps[${i}]` - - // todo: fix this - // t.equal(dep.station.name, 'Kiel Hauptbahnhof', name + '.station.name is invalid') - // t.equal(dep.station.id, kielHbf, name + '.station.id is invalid') - } - // todo: move into deps validator - t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) - + yield testDepartures({ + test: t, + departures, + validate, + id: kielHbf + }) t.end() })) diff --git a/test/oebb.js b/test/oebb.js index 9a0480f2..d32efdd5 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -18,6 +18,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testDepartures = require('./lib/departures') const when = createWhen('Europe/Vienna', 'de-AT') @@ -56,7 +57,7 @@ const klagenfurtHbf = '8100085' const muenchenHbf = '8000261' const wienRenngasse = '1390186' -test('journeys – Salzburg Hbf to Wien Westbahnhof', co(function* (t) { +test.skip('journeys – Salzburg Hbf to Wien Westbahnhof', co(function* (t) { const journeys = yield client.journeys(salzburgHbf, wienFickeystr, { results: 3, when, passedStations: true }) @@ -213,24 +214,20 @@ test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) { t.end() })) -test('departures at Wien Renngasse', co(function* (t) { - const deps = yield client.departures(wienRenngasse, { - duration: 5, when +test.skip('departures at Wien Renngasse', co(function* (t) { + const wienLeibenfrostgasse = '1390469' + const departures = yield client.departures(wienLeibenfrostgasse, { + duration: 15, when }) - validate(t, deps, 'departures', 'departures') - for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - const name = `deps[${i}]` - - // todo: fis this - // ÖBB HAFAS data is just too detailed :P - // t.equal(dep.station.name, 'Wien Renngasse', name + '.station.name is invalid') - // t.equal(dep.station.id, wienRenngasse, name + '.station.id is invalid') - } - // todo: move into deps validator - t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) - + // todo: fix this + // ÖBB HAFAS data is just too detailed :P + yield testDepartures({ + test: t, + departures, + validate, + id: wienLeibenfrostgasse + }) t.end() })) diff --git a/test/vbb.js b/test/vbb.js index a34c05bc..7d84092f 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -24,6 +24,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testDepartures = require('./lib/departures') const when = createWhen('Europe/Berlin', 'de-DE') @@ -273,19 +274,16 @@ test('journeys: via works – with detour', co(function* (t) { })) test('departures', co(function* (t) { - const deps = yield client.departures(spichernstr, {duration: 5, when}) - - validate(t, deps, 'departures', 'departures') - for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - const name = `deps[${i}]` - - t.equal(dep.station.name, 'U Spichernstr.', name + '.station.name is invalid') - t.equal(dep.station.id, spichernstr, name + '.station.id is invalid') - } - // todo: move into deps validator - t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) + const departures = yield client.departures(spichernstr, { + duration: 5, when + }) + yield testDepartures({ + test: t, + departures, + validate, + id: spichernstr + }) t.end() })) From 2ffcf02946221d830c9035e1aa1cd87727d4135f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 25 Apr 2018 12:48:05 +0200 Subject: [PATCH 046/176] validate-fptf@2 --- package.json | 2 +- test/lib/validate-fptf-with.js | 18 ++++--- test/lib/validators.js | 89 +++++++++++++++++----------------- 3 files changed, 57 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 55b42b0b..4549994f 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "tap-spec": "^4.1.1", "tape": "^4.8.0", "tape-promise": "^3.0.0", - "validate-fptf": "^1.3.0", + "validate-fptf": "^2.0.1", "vbb-stations-autocomplete": "^3.1.0" }, "scripts": { diff --git a/test/lib/validate-fptf-with.js b/test/lib/validate-fptf-with.js index 09624a3e..9db61db5 100644 --- a/test/lib/validate-fptf-with.js +++ b/test/lib/validate-fptf-with.js @@ -1,19 +1,25 @@ 'use strict' -const {defaultValidators, createRecurse} = require('validate-fptf') +const {defaultValidators} = require('validate-fptf') +const anyOf = require('validate-fptf/lib/any-of') + const validators = require('./validators') const create = (cfg, customValidators = {}) => { - const vals = Object.assign({}, defaultValidators) + const val = Object.assign({}, defaultValidators) for (let key of Object.keys(validators)) { - vals[key] = validators[key](cfg) + val[key] = validators[key](cfg) } - Object.assign(vals, customValidators) - const recurse = createRecurse(vals) + Object.assign(val, customValidators) const validateFptfWith = (t, item, allowedTypes, name) => { try { - recurse(allowedTypes, item, name) + if ('string' === typeof allowedTypes) { + val[allowedTypes](val, item, name) + } else { + anyOf(allowedTypes, val, item, name) + } + t.pass(name + ' is valid') } catch (err) { t.ifError(err) // todo: improve error logging } diff --git a/test/lib/validators.js b/test/lib/validators.js index 42fe3aca..14a4d353 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -2,8 +2,7 @@ const a = require('assert') const {defaultValidators} = require('validate-fptf') -const validateRef = require('validate-fptf/lib/reference') -const validateDate = require('validate-fptf/lib/date') +const anyOf = require('validate-fptf/lib/any-of') const {isValidWhen} = require('./util') @@ -11,8 +10,8 @@ const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const is = val => val !== null && val !== undefined const createValidateStation = (cfg) => { - const validateStation = (validate, s, name = 'station') => { - defaultValidators.station(validate, s, name) + const validateStation = (val, s, name = 'station') => { + defaultValidators.station(val, s, name) if (!cfg.stationCoordsOptional) { a.ok(is(s.location), `missing ${name}.location`) @@ -26,39 +25,39 @@ const createValidateStation = (cfg) => { if ('lines' in s) { a.ok(Array.isArray(s.lines), name + `.lines must be an array`) for (let i = 0; i < s.lines.length; i++) { - validate(['line'], s.lines[i], name + `.lines[${i}]`) + val.line(val, s.lines[i], name + `.lines[${i}]`) } } } return validateStation } -const validatePoi = (validate, poi, name = 'location') => { - defaultValidators.location(validate, poi, name) - validateRef(poi.id, name + '.id') +const validatePoi = (val, poi, name = 'location') => { + defaultValidators.location(val, poi, name) + val.ref(val, poi.id, name + '.id') a.ok(poi.name, name + '.name must not be empty') } -const validateAddress = (validate, addr, name = 'location') => { - defaultValidators.location(validate, addr, name) +const validateAddress = (val, addr, name = 'location') => { + defaultValidators.location(val, addr, name) a.strictEqual(typeof addr.address, 'string', name + '.address must be a string') a.ok(addr.address, name + '.address must not be empty') } -const validateLocation = (validate, loc, name = 'location') => { +const validateLocation = (val, loc, name = 'location') => { a.ok(isObj(loc), name + ' must be an object') - if (loc.type === 'station') validate(['station'], loc, name) - else if ('id' in loc) validatePoi(validate, loc, name) + if (loc.type === 'station') val.station(val, loc, name) + else if ('id' in loc) validatePoi(val, loc, name) else if (!('name' in loc) && ('address' in loc)) { - validateAddress(validate, loc, name) - } else defaultValidators.location(validate, loc, name) + validateAddress(val, loc, name) + } else defaultValidators.location(val, loc, name) } -const validateLocations = (validate, locs, name = 'locations') => { +const validateLocations = (val, locs, name = 'locations') => { a.ok(Array.isArray(locs), name + ' must be an array') a.ok(locs.length > 0, name + ' must not be empty') for (let i = 0; i < locs.length; i++) { - validate('location', locs[i], name + `[${i}]`) + val.location(locs[i], name + `[${i}]`) } } @@ -70,21 +69,21 @@ const createValidateLine = (cfg) => { } } - const validateLine = (validate, line, name = 'line') => { - defaultValidators.line(validate, line, name) + const validateLine = (val, line, name = 'line') => { + defaultValidators.line(val, line, name) a.ok(validLineModes.includes(line.mode), name + '.mode is invalid') } return validateLine } const createValidateStopover = (cfg) => { - const validateStopover = (validate, s, name = 'stopover') => { + const validateStopover = (val, s, name = 'stopover') => { if (is(s.arrival)) { - validateDate(s.arrival, name + '.arrival') + val.date(val, s.arrival, name + '.arrival') a.ok(isValidWhen(s.arrival, cfg.when), name + '.arrival is invalid') } if (is(s.departure)) { - validateDate(s.departure, name + '.departure') + val.date(val, s.departure, name + '.departure') a.ok(isValidWhen(s.departure, cfg.when), name + '.departure is invalid') } if (!is(s.arrival) && !is(s.departure)) { @@ -100,12 +99,12 @@ const createValidateStopover = (cfg) => { a.strictEqual(typeof s.departureDelay, 'number', msg) } - validate(['station'], s.station, name + '.station') + val.station(val, s.station, name + '.station') } return validateStopover } -const validateTicket = (validate, ti, name = 'ticket') => { +const validateTicket = (val, ti, name = 'ticket') => { a.strictEqual(typeof ti.name, 'string', name + '.name must be a string') a.ok(ti.name, name + '.name must not be empty') @@ -146,12 +145,12 @@ const validateTicket = (validate, ti, name = 'ticket') => { } const createValidateJourneyLeg = (cfg) => { - const validateJourneyLeg = (validate, leg, name = 'journeyLeg') => { + const validateJourneyLeg = (val, leg, name = 'journeyLeg') => { const withFakeScheduleAndOperator = Object.assign({ schedule: 'foo', // todo: let hafas-client parse a schedule ID operator: 'bar' // todo: let hafas-client parse the operator }, leg) - defaultValidators.journeyLeg(validate, withFakeScheduleAndOperator, name) + defaultValidators.journeyLeg(val, withFakeScheduleAndOperator, name) if (leg.arrival !== null) { const msg = name + '.arrival is invalid' @@ -179,7 +178,7 @@ const createValidateJourneyLeg = (cfg) => { a.ok(leg.passed.length > 0, name + '.passed must not be empty') for (let i = 0; i < leg.passed.length; i++) { - validate('stopover', leg.passed[i], name + `.passed[${i}]`) + val.stopover(val, leg.passed[i], name + `.passed[${i}]`) } } @@ -192,32 +191,32 @@ const createValidateJourneyLeg = (cfg) => { return validateJourneyLeg } -const validateJourney = (validate, j, name = 'journey') => { +const validateJourney = (val, j, name = 'journey') => { const withFakeId = Object.assign({ id: 'foo' // todo: let hafas-client parse a journey ID }, j) - defaultValidators.journey(validate, withFakeId, name) + defaultValidators.journey(val, withFakeId, name) if ('tickets' in j) { a.ok(Array.isArray(j.tickets), name + '.tickets must be an array') a.ok(j.tickets.length > 0, name + '.tickets must not be empty') for (let i = 0; i < j.tickets.length; i++) { - validate(['ticket'], j.tickets[i], name + `.tickets[${i}]`) + val.ticket(val, j.tickets[i], name + `.tickets[${i}]`) } } } -const validateJourneys = (validate, js, name = 'journeys') => { +const validateJourneys = (val, js, name = 'journeys') => { a.ok(Array.isArray(js), name + ' must be an array') a.ok(js.length > 0, name + ' must not be empty') for (let i = 0; i < js.length; i++) { - validate(['journey'], js[i], name + `[${i}]`) + val.journey(val, js[i], name + `[${i}]`) } } const createValidateDeparture = (cfg) => { - const validateDeparture = (validate, dep, name = 'departure') => { + const validateDeparture = (val, dep, name = 'departure') => { a.ok(isObj(dep), name + ' must be an object') // todo: let hafas-client add a .type field @@ -225,7 +224,7 @@ const createValidateDeparture = (cfg) => { a.ok(dep.journeyId, name + '.journeyId must not be empty') a.strictEqual(typeof dep.trip, 'number', name + '.trip must be a number') - validate(['station'], dep.station, name + '.station') + val.station(val, dep.station, name + '.station') a.ok(isValidWhen(dep.when, cfg.when), name + '.when is invalid') if (dep.delay !== null) { @@ -233,31 +232,31 @@ const createValidateDeparture = (cfg) => { a.strictEqual(typeof dep.delay, 'number', msg) } - validate(['line'], dep.line, name + '.line') + val.line(val, dep.line, name + '.line') a.strictEqual(typeof dep.direction, 'string', name + '.direction must be a string') a.ok(dep.direction, name + '.direction must not be empty') } return validateDeparture } -const validateDepartures = (validate, deps, name = 'departures') => { +const validateDepartures = (val, deps, name = 'departures') => { a.ok(Array.isArray(deps), name + ' must be an array') a.ok(deps.length > 0, name + ' must not be empty') for (let i = 0; i < deps.length; i++) { - validate('departure', deps[i], name + `[${i}]`) + val.departure(val, deps[i], name + `[${i}]`) } } -const validateMovement = (validate, m, name = 'movement') => { +const validateMovement = (val, m, name = 'movement') => { a.ok(isObj(m), name + ' must be an object') // todo: let hafas-client add a .type field - validate(['line'], m.line, name + '.line') + val.line(val, m.line, name + '.line') a.strictEqual(typeof m.direction, 'string', name + '.direction must be a string') a.ok(m.direction, name + '.direction must not be empty') const lName = name + '.location' - validate(['location'], m.location, lName) + val.location(val, m.location, lName) a.ok(m.location.latitude <= 55, lName + '.latitude is too small') a.ok(m.location.latitude >= 45, lName + '.latitude is too large') a.ok(m.location.longitude >= 9, lName + '.longitude is too small') @@ -266,7 +265,7 @@ const validateMovement = (validate, m, name = 'movement') => { a.ok(Array.isArray(m.nextStops), name + '.nextStops must be an array') for (let i = 0; i < m.nextStops.length; i++) { const st = m.nextStops[i] - validate('stopover', m.nextStops[i], name + `.nextStops[${i}]`) + val.stopover(val, m.nextStops[i], name + `.nextStops[${i}]`) } a.ok(Array.isArray(m.frames), name + '.frames must be an array') @@ -276,17 +275,17 @@ const validateMovement = (validate, m, name = 'movement') => { const fName = name + `.frames[${i}]` a.ok(isObj(f), fName + ' must be an object') - validate(['location', 'station'], f.origin, fName + '.origin') - validate(['location', 'station'], f.destination, fName + '.destination') + anyOf(['location', 'station'], val, f.origin, fName + '.origin') + anyOf(['location', 'station'], val, f.destination, fName + '.destination') a.strictEqual(typeof f.t, 'number', fName + '.frames must be a number') } } -const validateMovements = (validate, ms, name = 'movements') => { +const validateMovements = (val, ms, name = 'movements') => { a.ok(Array.isArray(ms), name + ' must be an array') a.ok(ms.length > 0, name + ' must not be empty') for (let i = 0; i < ms.length; i++) { - validate('movement', ms[i], name + `[${i}]`) + val.movement(val, ms[i], name + `[${i}]`) } } From fecd2aefc0e8312802f141ae49fbe40b6184fd43 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 25 Apr 2018 13:07:31 +0200 Subject: [PATCH 047/176] journeys fails with no product tests: use helper fn --- test/db.js | 14 ++++++++++- test/insa.js | 14 ++++++++++- test/lib/journeys-fails-with-no-product.js | 23 +++++++++++++++++ test/nahsh.js | 14 ++++++++++- test/oebb.js | 14 ++++++++++- test/vbb.js | 29 +++++++--------------- 6 files changed, 84 insertions(+), 24 deletions(-) create mode 100644 test/lib/journeys-fails-with-no-product.js diff --git a/test/db.js b/test/db.js index 79895f2d..e6db740a 100644 --- a/test/db.js +++ b/test/db.js @@ -19,6 +19,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -92,7 +93,18 @@ test('journeys – Berlin Schwedter Str. to München Hbf', co(function* (t) { })) // todo: journeys, only one product -// todo: journeys, fails with no product + +test('journeys – fails with no product', (t) => { + journeysFailsWithNoProduct({ + test: t, + fetchJourneys: client.journeys, + fromId: blnSchwedterStr, + toId: münchenHbf, + when, + products + }) + t.end() +}) test('Berlin Schwedter Str. to Torfstraße 17', co(function* (t) { const torfstr = { diff --git a/test/insa.js b/test/insa.js index 59e0f331..70e68e37 100644 --- a/test/insa.js +++ b/test/insa.js @@ -14,6 +14,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -54,7 +55,18 @@ test('journeys – Magdeburg Hbf to Magdeburg-Buckau', co(function* (t) { })) // todo: journeys, only one product -// todo: journeys, fails with no product + +test('journeys – fails with no product', (t) => { + journeysFailsWithNoProduct({ + test: t, + fetchJourneys: client.journeys, + fromId: magdeburgHbf, + toId: magdeburgBuckau, + when, + products + }) + t.end() +}) test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { const sternStr = { diff --git a/test/lib/journeys-fails-with-no-product.js b/test/lib/journeys-fails-with-no-product.js new file mode 100644 index 00000000..b4ae96f4 --- /dev/null +++ b/test/lib/journeys-fails-with-no-product.js @@ -0,0 +1,23 @@ +'use strict' + +const journeysFailsWithNoProduct = (cfg) => { + const { + test: t, + fetchJourneys, + fromId, + toId, + when, + products + } = cfg + + const productsObj = Object.create(null) + for (let p of products) productsObj[p.id] = false + + t.throws(() => { + client.journeys(fromId, toId, {when, products}) + // silence rejections, we're only interested in exceptions + .catch(() => {}) + }) +} + +module.exports = journeysFailsWithNoProduct diff --git a/test/nahsh.js b/test/nahsh.js index 3c2db30d..34f816be 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -18,6 +18,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const when = createWhen('Europe/Berlin', 'de-DE') @@ -87,7 +88,18 @@ test('journeys – Kiel Hbf to Flensburg', co(function* (t) { })) // todo: journeys, only one product -// todo: journeys, fails with no product + +test('journeys – fails with no product', (t) => { + journeysFailsWithNoProduct({ + test: t, + fetchJourneys: client.journeys, + fromId: kielHbf, + toId: flensburg, + when, + products + }) + t.end() +}) test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { const zingel = { diff --git a/test/oebb.js b/test/oebb.js index d32efdd5..fc92c581 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -18,6 +18,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const when = createWhen('Europe/Vienna', 'de-AT') @@ -79,7 +80,18 @@ test.skip('journeys – Salzburg Hbf to Wien Westbahnhof', co(function* (t) { })) // todo: journeys, only one product -// todo: journeys, fails with no product + +test('journeys – fails with no product', (t) => { + journeysFailsWithNoProduct({ + test: t, + fetchJourneys: client.journeys, + fromId: salzburgHbf, + toId: wienFickeystr, + when, + products + }) + t.end() +}) test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) { const wagramerStr = { diff --git a/test/vbb.js b/test/vbb.js index 7d84092f..23574746 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -24,6 +24,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const when = createWhen('Europe/Berlin', 'de-DE') @@ -159,26 +160,14 @@ test('journeys – only subway', co(function* (t) { })) test('journeys – fails with no product', (t) => { - // todo: make this test work - // t.plan(1) - try { - client.journeys(spichernstr, bismarckstr, { - when, - products: { - suburban: false, - subway: false, - tram: false, - bus: false, - ferry: false, - express: false, - regional: false - } - }) - // silence rejections, we're only interested in exceptions - .catch(() => {}) - } catch (err) { - t.ok(err, 'error thrown') - } + journeysFailsWithNoProduct({ + test: t, + fetchJourneys: client.journeys, + fromId: spichernstr, + toId: bismarckstr, + when, + products + }) t.end() }) From c7696380057021492c8b977125116a2b2b7789eb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 25 Apr 2018 13:24:27 +0200 Subject: [PATCH 048/176] add missing test helper fn, add todos, clean up --- test/db.js | 2 ++ test/insa.js | 2 ++ test/lib/journeys-station-to-poi.js | 30 ++++++++++++++++++++ test/lib/validate-line-without-mode.js | 38 -------------------------- test/nahsh.js | 3 ++ test/vbb.js | 3 ++ 6 files changed, 40 insertions(+), 38 deletions(-) create mode 100644 test/lib/journeys-station-to-poi.js delete mode 100644 test/lib/validate-line-without-mode.js diff --git a/test/db.js b/test/db.js index e6db740a..2c963efd 100644 --- a/test/db.js +++ b/test/db.js @@ -171,6 +171,8 @@ test('journeys: via works – with detour', co(function* (t) { t.end() })) +// todo: without detour + test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { yield testEarlierLaterJourneys({ test: t, diff --git a/test/insa.js b/test/insa.js index 70e68e37..6387bd0b 100644 --- a/test/insa.js +++ b/test/insa.js @@ -138,6 +138,8 @@ test('journeys: via works – with detour', co(function* (t) { t.end() })) +// todo: without detour + test('earlier/later journeys', co(function* (t) { yield testEarlierLaterJourneys({ test: t, diff --git a/test/lib/journeys-station-to-poi.js b/test/lib/journeys-station-to-poi.js new file mode 100644 index 00000000..e996dc37 --- /dev/null +++ b/test/lib/journeys-station-to-poi.js @@ -0,0 +1,30 @@ +'use strict' + +const isRoughlyEqual = require('is-roughly-equal') + +const co = require('./co') + +const testJourneysStationToPoi = co(function* (cfg) { + const {test: t, journeys, validate, fromId} = cfg + const {id, name, latitude, longitude} = cfg.to + + validate(t, journeys, 'journeys', 'journeys') + t.strictEqual(journeys.length, 3) + for (let i = 0; i < journeys.length; i++) { + const j = journeys[i] + + const firstLeg = j.legs[0] + t.strictEqual(firstLeg.origin.id, fromId) + + const d = j.legs[j.legs.length - 1].destination + const n = `journeys[0].legs[${i}].destination` + + t.strictEqual(d.type, 'location', n + '.type is invalid') + t.strictEqual(d.id, id, n + '.id is invalid') + t.strictEqual(d.name, name, n + '.name is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), n + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), n + '.longitude is invalid') + } +}) + +module.exports = testJourneysStationToPoi diff --git a/test/lib/validate-line-without-mode.js b/test/lib/validate-line-without-mode.js deleted file mode 100644 index 71068e32..00000000 --- a/test/lib/validate-line-without-mode.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -const a = require('assert') -const is = require('@sindresorhus/is') - -const validateItem = require('validate-fptf/lib/item') -const validateReference = require('validate-fptf/lib/reference') - -// todo: this is copied code, DRY this up! -// see https://github.com/public-transport/validate-fptf/blob/373b4847ec9668c4a9ec9b0dbd50f8a70ffbe127/line.js -const validateLineWithoutMode = (validate, line, name) => { - validateItem(line, name) - - a.strictEqual(line.type, 'line', name + '.type must be `line`') - - validateReference(line.id, name + '.id') - - a.strictEqual(typeof line.name, 'string', name + '.name must be a string') - a.ok(line.name.length > 0, name + '.name can\'t be empty') - - // skipping line validation here - // see https://github.com/derhuerst/hafas-client/issues/8#issuecomment-355839965 - if (is.undefined(line.mode) || is.null(line.mode)) { - console.error(`ÖBB: Missing \`mode\` for line ${line.name} (at ${name}).`) - } - - if (!is.undefined(line.subMode)) { - a.fail(name + '.subMode is reserved an should not be used for now') - } - - // todo: routes - - if (!is.null(line.operator) && !is.undefined(line.operator)) { - validate(['operator'], line.operator, name + '.operator') - } -} - -module.exports = validateLineWithoutMode diff --git a/test/nahsh.js b/test/nahsh.js index 34f816be..cb3cf021 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -174,6 +174,9 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { t.end() })) +// todo: with detour test +// todo: without detour test + test('journey leg details for Flensburg to Husum', co(function* (t) { const journeys = yield client.journeys(flensburg, husum, { results: 1, when diff --git a/test/vbb.js b/test/vbb.js index 23574746..b9751308 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -45,6 +45,7 @@ const validateDirection = (dir, name) => { const _validateStation = createValidateStation(cfg) const validateStation = (validate, s, name) => { _validateStation(validate, s, name) + // todo: find station by ID a.equal(s.name, shorten(s.name), name + '.name must be shortened') } @@ -262,6 +263,8 @@ test('journeys: via works – with detour', co(function* (t) { t.end() })) +// todo: without detour test + test('departures', co(function* (t) { const departures = yield client.departures(spichernstr, { duration: 5, when From 7f02bfe4d572ee1bd7d7ad0e3bb3b8648b543958 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 26 Apr 2018 16:17:53 +0200 Subject: [PATCH 049/176] add missing test helper fn --- test/lib/departures.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/lib/departures.js diff --git a/test/lib/departures.js b/test/lib/departures.js new file mode 100644 index 00000000..1da624a6 --- /dev/null +++ b/test/lib/departures.js @@ -0,0 +1,21 @@ +'use strict' + +const co = require('./co') + +const testDepartures = co(function* (cfg) { + const {test: t, departures: deps, validate, id} = cfg + + validate(t, deps, 'departures', 'departures') + t.ok(deps.length > 0, 'must be >0 departures') + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` + + t.equal(dep.station.id, id, name + '.station.id is invalid') + } + + // todo: move into deps validator + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) +}) + +module.exports = testDepartures From 47aea604df3d2949840b7f8fcb7750dd2bfb20a4 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 3 May 2018 10:29:43 +0200 Subject: [PATCH 050/176] fix error message :bug: --- format/location.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/format/location.js b/format/location.js index aa7d19d1..39f0a990 100644 --- a/format/location.js +++ b/format/location.js @@ -7,7 +7,7 @@ const formatLocation = (profile, l, name = 'location') => { if ('string' === typeof l.id) return profile.formatPoi(l) if ('string' === typeof l.address) return profile.formatAddress(l) if (!l.type) throw new Error(`missing ${name}.type`) - throw new Error(`invalid ${name}type: ${l.type}`) + throw new Error(`invalid ${name}.type: ${l.type}`) } throw new Error(name + ': valid station, address or poi required.') } From df293e31cc612e16450651252d840d8d74af235d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 7 May 2018 12:33:53 +0200 Subject: [PATCH 051/176] fix test helpers :bug: --- test/lib/earlier-later-journeys.js | 1 + test/lib/validators.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/lib/earlier-later-journeys.js b/test/lib/earlier-later-journeys.js index a9323815..bdf4eb1d 100644 --- a/test/lib/earlier-later-journeys.js +++ b/test/lib/earlier-later-journeys.js @@ -40,6 +40,7 @@ const testEarlierLaterJourneys = co(function* (cfg) { let earliestDep = Infinity, latestDep = -Infinity for (let j of model) { + if (j.legs[0].departure === null) continue const dep = +new Date(j.legs[0].departure) if (dep < earliestDep) earliestDep = dep else if (dep > latestDep) latestDep = dep diff --git a/test/lib/validators.js b/test/lib/validators.js index 14a4d353..8a19db86 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -57,7 +57,7 @@ const validateLocations = (val, locs, name = 'locations') => { a.ok(Array.isArray(locs), name + ' must be an array') a.ok(locs.length > 0, name + ' must not be empty') for (let i = 0; i < locs.length; i++) { - val.location(locs[i], name + `[${i}]`) + val.location(val, locs[i], name + `[${i}]`) } } From bdc23c64e26578d1a4aaf6e97fa210c69d3b5c09 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 7 May 2018 12:34:56 +0200 Subject: [PATCH 052/176] cherry-pick c60213a --- p/db/products.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/db/products.js b/p/db/products.js index fa683e2b..3381f401 100644 --- a/p/db/products.js +++ b/p/db/products.js @@ -68,7 +68,7 @@ module.exports = [ }, { id: 'tram', - mode: 'tram', + mode: 'train', bitmasks: [256], name: 'Tram', short: 'T', From c435e253907af5fc244b5847ade5e192b5606423 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 13 May 2018 22:52:33 +0200 Subject: [PATCH 053/176] parseJourneyLeg: null as fallback direction :bug: --- parse/journey-leg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 65032de2..fd634a9f 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -48,7 +48,7 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => // 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) + res.direction = profile.parseStationName(pt.jny.dirTxt) || null if (pt.dep.dPlatfS) res.departurePlatform = pt.dep.dPlatfS if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS From 2ca7e64fd5008c6bc88b1c7c58d17983bf06b54c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 13 May 2018 00:34:26 +0200 Subject: [PATCH 054/176] journeys detour tests: use helper fn --- test/db.js | 14 ++++++-------- test/insa.js | 17 ++++++----------- test/lib/journeys-with-detour.js | 21 +++++++++++++++++++++ test/oebb.js | 17 ++++++----------- test/vbb.js | 14 ++++++-------- 5 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 test/lib/journeys-with-detour.js diff --git a/test/db.js b/test/db.js index 2c963efd..214ce9d0 100644 --- a/test/db.js +++ b/test/db.js @@ -21,6 +21,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testJourneysWithDetour = require('./lib/journeys-with-detour') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -159,15 +160,12 @@ test('journeys: via works – with detour', co(function* (t) { passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') - - const leg = journeys[0].legs.some((leg) => { - return leg.passed && leg.passed.some((passed) => { - return passed.station.id === württembergallee - }) + yield testJourneysWithDetour({ + test: t, + journeys, + validate, + detourIds: [württembergallee] }) - t.ok(leg, 'Württembergalle is not being passed') - t.end() })) diff --git a/test/insa.js b/test/insa.js index 6387bd0b..af3ca98e 100644 --- a/test/insa.js +++ b/test/insa.js @@ -16,6 +16,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testJourneysWithDetour = require('./lib/journeys-with-detour') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -123,18 +124,12 @@ test('journeys: via works – with detour', co(function* (t) { passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') - - const leg = journeys[0].legs.some((leg) => { - return leg.passed && leg.passed.some((passed) => { - return ( - passed.station.id === '8010077' || // todo: trim IDs - passed.station.id === dessau - ) - }) + yield testJourneysWithDetour({ + test: t, + journeys, + validate, + detourIds: ['8010077', dessau] // todo: trim IDs }) - t.ok(leg, 'Dessau is not being passed') - t.end() })) diff --git a/test/lib/journeys-with-detour.js b/test/lib/journeys-with-detour.js new file mode 100644 index 00000000..2358a898 --- /dev/null +++ b/test/lib/journeys-with-detour.js @@ -0,0 +1,21 @@ +'use strict' + +const co = require('./co') + +const testJourneysWithDetour = co(function* (cfg) { + const {test: t, journeys, validate, detourIds} = cfg + + // We assume that going from A to B via C *without* detour is currently + // impossible. We check if the routing engine computes a detour. + + validate(t, journeys, 'journeys', 'journeys') + + const leg = journeys[0].legs.some((leg) => { + return leg.passed && leg.passed.some((passed) => { + return detourIds.includes(passed.station.id) + }) + }) + t.ok(leg, detourIds.join('/') + ' is not being passed') +}) + +module.exports = testJourneysWithDetour diff --git a/test/oebb.js b/test/oebb.js index fc92c581..10e9ec2e 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -20,6 +20,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testJourneysWithDetour = require('./lib/journeys-with-detour') const when = createWhen('Europe/Vienna', 'de-AT') @@ -150,18 +151,12 @@ test('journeys: via works – with detour', co(function* (t) { passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') - - const leg = journeys[0].legs.some((leg) => { - return leg.passed && leg.passed.some((passed) => { - return ( - passed.station.id === donauinsel || - passed.station.id === donauinselPassed - ) - }) + yield testJourneysWithDetour({ + test: t, + journeys, + validate, + detourIds: [donauinsel, donauinselPassed] }) - t.ok(leg, 'Donauinsel is not being passed') - t.end() })) diff --git a/test/vbb.js b/test/vbb.js index b9751308..e1f6d79c 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -26,6 +26,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testJourneysWithDetour = require('./lib/journeys-with-detour') const when = createWhen('Europe/Berlin', 'de-DE') @@ -251,15 +252,12 @@ test('journeys: via works – with detour', co(function* (t) { passedStations: true }) - validate(t, journeys, 'journeys', 'journeys') - - const leg = journeys[0].legs.some((leg) => { - return leg.passed && leg.passed.some((passed) => { - return passed.station.id === württembergallee - }) + yield testJourneysWithDetour({ + test: t, + journeys, + validate, + detourIds: [württembergallee] }) - t.ok(leg, 'Württembergalle is not being passed') - t.end() })) From 4d908e1f72454634f733eae6e51f3eb5ceb62380 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 13 May 2018 01:03:10 +0200 Subject: [PATCH 055/176] tests: fix isRoughEqual call :bug:, minor change --- test/db.js | 5 ++--- test/oebb.js | 4 ++-- test/vbb.js | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/db.js b/test/db.js index 214ce9d0..ea492d2a 100644 --- a/test/db.js +++ b/test/db.js @@ -244,12 +244,11 @@ test('nearby Berlin Jungfernheide', co(function* (t) { // todo: trim IDs t.ok(s0.id === '008011167' || s0.id === jungfernheide) t.equal(s0.name, 'Berlin Jungfernheide') - t.ok(isRoughlyEqual(s0.location.latitude, 52.530408, .0005)) - t.ok(isRoughlyEqual(s0.location.longitude, 13.299424, .0005)) + t.ok(isRoughlyEqual(.0005, s0.location.latitude, 52.530408)) + t.ok(isRoughlyEqual(.0005, s0.location.longitude, 13.299424)) t.ok(s0.distance >= 0) t.ok(s0.distance <= 100) - // todo: nearby[0] t.end() })) diff --git a/test/oebb.js b/test/oebb.js index 10e9ec2e..73929b01 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -269,8 +269,8 @@ test('nearby Salzburg Hbf', co(function* (t) { const s = nearby[0] t.ok(s.id === '008100002' || s.id === '8100002', 'id should be 8100002') t.equal(s.name, 'Salzburg Hbf') - t.ok(isRoughlyEqual(s.location.latitude, 47.812851, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 13.045604, .0005)) + t.ok(isRoughlyEqual(.0005, s.location.latitude, 47.812851)) + t.ok(isRoughlyEqual(.0005, s.location.longitude, 13.045604)) t.ok(s.distance >= 0) t.ok(s.distance <= 100) diff --git a/test/vbb.js b/test/vbb.js index e1f6d79c..db7530ac 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -108,8 +108,6 @@ const bismarckstr = '900000024201' const westhafen = '900000001201' const wedding = '900000009104' const württembergallee = '900000026153' -const berlinerStr = '900000044201' -const landhausstr = '900000043252' test('journeys – Spichernstr. to Bismarckstr.', co(function* (t) { const journeys = yield client.journeys(spichernstr, bismarckstr, { @@ -301,6 +299,9 @@ test('departures at 7-digit station', co(function* (t) { })) test('nearby', co(function* (t) { + const berlinerStr = '900000044201' + const landhausstr = '900000043252' + // Berliner Str./Bundesallee const nearby = yield client.nearby({ type: 'location', From 196681513c46777336427c96b50f7b5100020b7e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sun, 13 May 2018 22:52:33 +0200 Subject: [PATCH 056/176] parseJourneyLeg: null as fallback direction :bug: --- parse/journey-leg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 65032de2..fd634a9f 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -48,7 +48,7 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => // 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) + res.direction = profile.parseStationName(pt.jny.dirTxt) || null if (pt.dep.dPlatfS) res.departurePlatform = pt.dep.dPlatfS if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS From 06e759966cf1e77964b097f851a86219bd8891b6 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 14 May 2018 00:35:21 +0200 Subject: [PATCH 057/176] tests: isValidWhen -> assertValidWhen --- test/lib/util.js | 12 +++++++----- test/lib/validators.js | 14 ++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/lib/util.js b/test/lib/util.js index d5afed72..89381beb 100644 --- a/test/lib/util.js +++ b/test/lib/util.js @@ -2,9 +2,11 @@ const isRoughlyEqual = require('is-roughly-equal') const {DateTime} = require('luxon') +const a = require('assert') const hour = 60 * 60 * 1000 -const week = 7 * 24 * hour +const day = 24 * hour +const week = 7 * day // next Monday 10 am const createWhen = (timezone, locale) => { @@ -14,13 +16,13 @@ const createWhen = (timezone, locale) => { }).startOf('week').plus({weeks: 1, hours: 10}).toJSDate() } -const isValidWhen = (actual, expected) => { +const assertValidWhen = (actual, expected, name) => { const ts = +new Date(actual) - if (Number.isNaN(ts)) return false + a.ok(!Number.isNaN(ts), name + ' is not parsable by Date') // the timestamps might be from long-distance trains - return isRoughlyEqual(14 * hour, +expected, ts) + a.ok(isRoughlyEqual(day, +expected, ts), name + ' is out of range') } module.exports = { - hour, createWhen, isValidWhen + hour, createWhen, assertValidWhen } diff --git a/test/lib/validators.js b/test/lib/validators.js index 8a19db86..ec864a04 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -4,7 +4,7 @@ const a = require('assert') const {defaultValidators} = require('validate-fptf') const anyOf = require('validate-fptf/lib/any-of') -const {isValidWhen} = require('./util') +const {assertValidWhen} = require('./util') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) const is = val => val !== null && val !== undefined @@ -80,11 +80,11 @@ const createValidateStopover = (cfg) => { const validateStopover = (val, s, name = 'stopover') => { if (is(s.arrival)) { val.date(val, s.arrival, name + '.arrival') - a.ok(isValidWhen(s.arrival, cfg.when), name + '.arrival is invalid') + assertValidWhen(s.arrival, cfg.when, name) } if (is(s.departure)) { val.date(val, s.departure, name + '.departure') - a.ok(isValidWhen(s.departure, cfg.when), name + '.departure is invalid') + assertValidWhen(s.departure, cfg.when, name) } if (!is(s.arrival) && !is(s.departure)) { a.fail(name + ' contains neither arrival nor departure') @@ -153,12 +153,10 @@ const createValidateJourneyLeg = (cfg) => { defaultValidators.journeyLeg(val, withFakeScheduleAndOperator, name) if (leg.arrival !== null) { - const msg = name + '.arrival is invalid' - a.ok(isValidWhen(leg.arrival, cfg.when), msg) + assertValidWhen(leg.arrival, cfg.when, name + '.arrival') } if (leg.departure !== null) { - const msg = name + '.departure is invalid' - a.ok(isValidWhen(leg.departure, cfg.when), msg) + assertValidWhen(leg.departure, cfg.when, name + '.departure') } // todo: leg.arrivalPlatform !== null if (is(leg.arrivalPlatform)) { @@ -226,7 +224,7 @@ const createValidateDeparture = (cfg) => { val.station(val, dep.station, name + '.station') - a.ok(isValidWhen(dep.when, cfg.when), name + '.when is invalid') + assertValidWhen(dep.when, cfg.when, name) if (dep.delay !== null) { const msg = name + '.delay must be a number' a.strictEqual(typeof dep.delay, 'number', msg) From 0840d69ee9cc0fb9b4bf35237fa93f677991bd7d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:53:33 +0200 Subject: [PATCH 058/176] radar: assert north > south & east > west --- index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.js b/index.js index 6d5c082e..ab7f03e6 100644 --- a/index.js +++ b/index.js @@ -316,6 +316,8 @@ const createClient = (profile, request = _request) => { if ('number' !== typeof west) throw new Error('west must be a number.') if ('number' !== typeof south) throw new Error('south must be a number.') if ('number' !== typeof east) throw new Error('east must be a number.') + if (north <= south) throw new Error('north must be larger than south.') + if (east <= west) throw new Error('east must be larger than west.') opt = Object.assign({ results: 256, // maximum number of vehicles From 0ef03015e06daabf9d1b843ef75d1bd52109713c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 21 May 2018 17:10:42 +0200 Subject: [PATCH 059/176] validate opt.when :boom: --- index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index ab7f03e6..c533168f 100644 --- a/index.js +++ b/index.js @@ -31,7 +31,8 @@ const createClient = (profile, request = _request) => { direction: null, // only show departures heading to this station duration: 10 // show departures for the next n minutes }, opt) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const products = profile.formatProductsFilter(opt.products || {}) const dir = opt.direction ? profile.formatStation(opt.direction) : null @@ -96,7 +97,8 @@ const createClient = (profile, request = _request) => { polylines: false // return leg shapes? }, opt) if (opt.via) opt.via = profile.formatLocation(profile, opt.via) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const filters = [ profile.formatProductsFilter(opt.products || {}) @@ -281,7 +283,8 @@ const createClient = (profile, request = _request) => { passedStations: true, // return stations on the way? polyline: false }, opt) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') return request(profile, { cfg: {polyEnc: 'GPA'}, @@ -326,7 +329,8 @@ const createClient = (profile, request = _request) => { products: null, // optionally an object of booleans polylines: false // return a track shape for each vehicle? }, opt || {}) - opt.when = opt.when || new Date() + opt.when = new Date(opt.when || Date.now()) + if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const durationPerStep = opt.duration / Math.max(opt.frames, 1) * 1000 return request(profile, { From deefeb1f647524595a7747a48558564bc72c2ce3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:07:05 +0200 Subject: [PATCH 060/176] add parsePolyline to default profile --- lib/default-profile.js | 2 ++ package.json | 2 ++ parse/polyline.js | 53 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 parse/polyline.js diff --git a/lib/default-profile.js b/lib/default-profile.js index 340de25f..57d7eb36 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -6,6 +6,7 @@ const parseJourneyLeg = require('../parse/journey-leg') const parseJourney = require('../parse/journey') const parseLine = require('../parse/line') const parseLocation = require('../parse/location') +const parsePolyline = require('../parse/polyline') const parseMovement = require('../parse/movement') const parseNearby = require('../parse/nearby') const parseOperator = require('../parse/operator') @@ -42,6 +43,7 @@ const defaultProfile = { parseLine, parseStationName: id, parseLocation, + parsePolyline, parseMovement, parseNearby, parseOperator, diff --git a/package.json b/package.json index de06f070..d654d719 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,10 @@ "node": ">=6" }, "dependencies": { + "@mapbox/polyline": "^1.0.0", "capture-stack-trace": "^1.0.0", "fetch-ponyfill": "^6.0.0", + "gps-distance": "0.0.4", "lodash": "^4.17.5", "luxon": "^0.5.8", "p-throttle": "^1.1.0", diff --git a/parse/polyline.js b/parse/polyline.js new file mode 100644 index 00000000..5cdcff0c --- /dev/null +++ b/parse/polyline.js @@ -0,0 +1,53 @@ +'use strict' + +const {toGeoJSON} = require('@mapbox/polyline') +const distance = require('gps-distance') + +const createParsePolyline = (locations) => { + // todo: what is p.delta? + // todo: what is p.type? + // todo: what is p.crdEncS? + // todo: what is p.crdEncF? + const parsePolyline = (p) => { + const shape = toGeoJSON(p.crdEncYX) + if (shape.coordinates.length === 0) return null + + const res = shape.coordinates.map(crd => ({ + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: crd + } + })) + + if (Array.isArray(p.ppLocRefL)) { + for (let ref of p.ppLocRefL) { + const p = res[ref.ppIdx] + const loc = locations[ref.locX] + if (p && loc) p.properties = loc + } + + // Often there is one more point right next to each point at a station. + // We filter them here if they are < 5m from each other. + for (let i = 1; i < res.length; i++) { + const p1 = res[i - 1].geometry.coordinates + const p2 = res[i].geometry.coordinates + const d = distance(p1[1], p1[0], p2[1], p2[0]) + if (d >= .005) continue + const l1 = Object.keys(res[i - 1].properties).length + const l2 = Object.keys(res[i].properties).length + if (l1 === 0 && l2 > 0) res.splice(i - 1, 1) + else if (l2 === 0 && l1 > 0) res.splice(i, 1) + } + } + + return { + type: 'FeatureCollection', + features: res + } + } + return parsePolyline +} + +module.exports = createParsePolyline From 431574b7569754985e17dcaf66ccda67cb9408e3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:07:43 +0200 Subject: [PATCH 061/176] parseJourneyLeg, parseMovement: use parsePolyline :boom:, refactor --- index.js | 15 +++------------ parse/journey-leg.js | 5 +++-- parse/movement.js | 13 ++++++++----- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index c533168f..d2410c70 100644 --- a/index.js +++ b/index.js @@ -149,10 +149,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.outConL)) return [] - let polylines = [] - if (opt.polylines && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL - } + const polylines = opt.polyline && d.common.polyL || [] const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks, polylines) if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB @@ -298,10 +295,7 @@ const createClient = (profile, request = _request) => { } }) .then((d) => { - let polylines = [] - if (opt.polyline && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL - } + const polylines = opt.polyline && d.common.polyL || [] const parse = profile.parseJourneyLeg(profile, d.locations, d.lines, d.remarks, polylines) const leg = { // pretend the leg is contained in a journey @@ -354,10 +348,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.jnyL)) return [] - let polylines = [] - if (opt.polylines && d.common && Array.isArray(d.common.polyL)) { - polylines = d.common.polyL - } + const polylines = opt.polyline && d.common.polyL || [] const parse = profile.parseMovement(profile, d.locations, d.lines, d.remarks, polylines) return d.jnyL.map(parse) }) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index fd634a9f..bbc3f9da 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -36,9 +36,10 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => if (pt.jny && pt.jny.polyG) { let p = pt.jny.polyG.polyXL - p = p && polylines[p[0]] + p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - res.polyline = p && p.crdEncYX || null + const parse = profile.parsePolyline(stations) + res.polyline = p && parse(p) || null } if (pt.type === 'WALK') { diff --git a/parse/movement.js b/parse/movement.js index 7999eb52..8a3ab401 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -35,12 +35,15 @@ const createParseMovement = (profile, locations, lines, remarks, polylines = []) } } - if (m.ani.poly && m.ani.poly.crdEncYX) { - res.polyline = m.ani.poly.crdEncYX - } else if (m.ani.polyG && Array.isArray(m.ani.polyG.polyXL)) { - let p = m.ani.polyG.polyXL[0] + if (m.ani.poly) { + const parse = profile.parsePolyline(locations) + res.polyline = parse(m.ani.poly) + } else if (m.ani.polyG) { + let p = m.ani.polyG.polyXL + p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - res.polyline = polylines[p] && polylines[p].crdEncYX || null + const parse = profile.parsePolyline(locations) + res.polyline = p && parse(p) || null } } From 8c896fe8c1b88905e565852afed4c2c9d5cfc1f9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 21:27:12 +0200 Subject: [PATCH 062/176] docs for new polyline format :memo: --- docs/journey-leg.md | 64 ++++++++++++++++++++++++++++++++++++++++++++- docs/journeys.md | 2 +- docs/radar.md | 2 +- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/docs/journey-leg.md b/docs/journey-leg.md index f54b0e6e..e21293c7 100644 --- a/docs/journey-leg.md +++ b/docs/journey-leg.md @@ -118,4 +118,66 @@ The response looked like this: } ``` -If you pass `polyline: true`, the leg will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. +### `polyline` option + +If you pass `polyline: true`, the leg will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1). Every `Point` next to a station will have `properties` containing the station's metadata. + +We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken from the [VBB profile](../p/vbb): + +```js +{ + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + type: 'station', + id: '900000070301', + name: 'U Alt-Mariendorf', + /* … */ + }, + geometry: { + type: 'Point', + coordinates: [13.3875, 52.43993] // longitude, latitude + } + }, + /* … */ + { + type: 'Feature', + properties: { + type: 'station', + id: '900000017101', + name: 'U Mehringdamm', + /* … */ + }, + geometry: { + type: 'Point', + coordinates: [13.38892, 52.49448] // longitude, latitude + } + }, + /* … */ + { + // intermediate point, without associated station + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [13.28599, 52.58742] // longitude, latitude + } + }, + { + type: 'Feature', + properties: { + type: 'station', + id: '900000089301', + name: 'U Alt-Tegel', + /* … */ + }, + geometry: { + type: 'Point', + coordinates: [13.28406, 52.58915] // longitude, latitude + } + } + ] +} +``` diff --git a/docs/journeys.md b/docs/journeys.md index 1b6d0051..016f052c 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -262,4 +262,4 @@ departure of last journey 2017-12-17T19:07:00.000+01:00 departure of first (later) journey 2017-12-17T19:19:00.000+01:00 ``` -If you pass `polylines: true`, each journey leg will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. +If you pass `polylines: true`, each journey leg will have a `polyline` field. Refer to [the section in the `journeyLeg()` docs](journey-leg.md#polyline-option) for details. diff --git a/docs/radar.md b/docs/radar.md index ab00b767..e42155c0 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -163,4 +163,4 @@ The response may look like this: }, /* … */ ] ``` -If you pass `polylines: true`, each result will have a `polyline` field, containing an encoded shape. You can use e.g. [`@mapbox/polyline`](https://www.npmjs.com/package/@mapbox/polyline) to decode it. +If you pass `polylines: true`, each journey leg will have a `polyline` field, as documented in [the corresponding section in the `journeyLeg()` docs](journey-leg.md#polyline-option), with the exception that station info is missing. From 03b9ab9a839fa15332b8dd2bd2b4ba47eebddf9c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 16 May 2018 22:28:59 +0200 Subject: [PATCH 063/176] expect parsePolyline in lib/validate-profile --- lib/validate-profile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 21c86829..88e3d0c1 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -16,6 +16,7 @@ const types = { parseLine: 'function', parseStationName: 'function', parseLocation: 'function', + parsePolyline: 'function', parseMovement: 'function', parseNearby: 'function', parseOperator: 'function', From 00b1eb387c7c6b8e65053f04597e750a0d578f17 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 21 May 2018 12:15:40 +0200 Subject: [PATCH 064/176] crypto.createHash -> create-hash, 2.7.3 closes #52 --- lib/request.js | 4 ++-- package.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/request.js b/lib/request.js index 084fe26b..3cde8fbb 100644 --- a/lib/request.js +++ b/lib/request.js @@ -1,6 +1,6 @@ 'use strict' -const crypto = require('crypto') +const createHash = require('create-hash') let captureStackTrace = () => {} if (process.env.NODE_DEBUG === 'hafas-client') { captureStackTrace = require('capture-stack-trace') @@ -9,7 +9,7 @@ const {stringify} = require('query-string') const Promise = require('pinkie-promise') const {fetch} = require('fetch-ponyfill')({Promise}) -const md5 = input => crypto.createHash('md5').update(input).digest() +const md5 = input => createHash('md5').update(input).digest() const request = (profile, data) => { const body = profile.transformReqBody({lang: 'en', svcReqL: [data]}) diff --git a/package.json b/package.json index d654d719..7ab50742 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hafas-client", "description": "JavaScript client for HAFAS public transport APIs.", - "version": "2.7.2", + "version": "2.7.3", "main": "index.js", "files": [ "index.js", @@ -34,6 +34,7 @@ "dependencies": { "@mapbox/polyline": "^1.0.0", "capture-stack-trace": "^1.0.0", + "create-hash": "^1.2.0", "fetch-ponyfill": "^6.0.0", "gps-distance": "0.0.4", "lodash": "^4.17.5", From e47961938707d0f8f7b523da49070a56a50fadcf Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 24 May 2018 14:06:35 +0200 Subject: [PATCH 065/176] cherry-pick 16c3f01: DB: 1.16 protocol, journeyLeg, fix polylines :bug: --- p/db/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/p/db/index.js b/p/db/index.js index 812cb94d..773f8ab3 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -10,7 +10,7 @@ const formatLoyaltyCard = require('./loyalty-cards').format const transformReqBody = (body) => { body.client = {id: 'DB', v: '16040000', type: 'IPH', name: 'DB Navigator'} body.ext = 'DB.R15.12.a' - body.ver = '1.15' + body.ver = '1.16' body.auth = {type: 'AID', aid: 'n91dB8Z77MLdoR0K'} return body @@ -34,8 +34,8 @@ const transformJourneysQuery = (query, opt) => { return query } -const createParseJourney = (profile, stations, lines, remarks) => { - const parseJourney = _createParseJourney(profile, stations, lines, remarks) +const createParseJourney = (profile, stations, lines, remarks, polylines) => { + const parseJourney = _createParseJourney(profile, stations, lines, remarks, polylines) // todo: j.sotRating, j.conSubscr, j.isSotCon, j.showARSLink, k.sotCtxt // todo: j.conSubscr, j.showARSLink, j.useableTime @@ -96,7 +96,9 @@ const dbProfile = { // todo: parseLocation parseJourney: createParseJourney, - formatStation + formatStation, + + journeyLeg: true // todo: #49 } module.exports = dbProfile From 6063fa6651aa47c661f4267cc2514d8d68b13835 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 24 May 2018 21:54:25 +0200 Subject: [PATCH 066/176] journeys: fix polylines option :bug: --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index eeecdd0e..69f831ea 100644 --- a/index.js +++ b/index.js @@ -149,7 +149,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.outConL)) return [] - const polylines = opt.polyline && d.common.polyL || [] + const polylines = opt.polylines && d.common.polyL || [] const parse = profile.parseJourney(profile, d.locations, d.lines, d.remarks, polylines) if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB From 187d2ac04e0ad72d48519325c47e92502d68901c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 23 Apr 2018 15:58:27 +0200 Subject: [PATCH 067/176] Nah.sh: enable radar despite failing test see #34 for more details --- p/nahsh/index.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/p/nahsh/index.js b/p/nahsh/index.js index 563f1ad2..28d1a08d 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -2,6 +2,7 @@ const _parseLocation = require('../../parse/location') const _createParseJourney = require('../../parse/journey') +const _createParseMovement = require('../../parse/movement') const products = require('./products') @@ -77,6 +78,19 @@ const createParseJourney = (profile, stations, lines, remarks) => { return parseJourneyWithTickets } +const createParseMovement = (profile, locations, lines, remarks) => { + const _parseMovement = _createParseMovement(profile, locations, lines, remarks) + const parseMovement = (m) => { + const res = _parseMovement(m) + // filter out empty nextStops entries + res.nextStops = res.nextStops.filter((f) => { + return f.station !== null || f.arrival !== null || f.departure !== null + }) + return res + } + return parseMovement +} + const nahshProfile = { locale: 'de-DE', timezone: 'Europe/Berlin', @@ -87,9 +101,10 @@ const nahshProfile = { parseLocation, parseJourney: createParseJourney, + parseMovement: createParseMovement, journeyLeg: true, - radar: false // todo: see #34 + radar: true // todo: see #34 } module.exports = nahshProfile From 7541bcae667c90c2ff0b7f126340b74fe8c4c4a3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 7 May 2018 11:47:49 +0200 Subject: [PATCH 068/176] adapt nah.sh radar test to #34 --- test/nahsh.js | 53 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/test/nahsh.js b/test/nahsh.js index c125274b..3d0b491d 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -379,9 +379,41 @@ test('location', co(function* (t) { t.end() })) -// todo: see #34 -test.skip('radar Kiel', co(function* (t) { - const vehicles = yield client.radar(54.4, 10.0, 54.2, 10.2, { +test('radar Kiel', co(function* (t) { + const fakeStation = (s) => { + const fake = Object.assign({ + products: { + nationalExp: true, + national: false, + interregional: true, + regional: false, + suburban: true, + bus: false, + ferry: true, + subway: false, + tram: true, + onCall: false + } + }, s) + if (s.name === null) fake.name = 'foo' + return fake + } + const _assertValidStation = (t, s, coordsOptional = false) => { + assertValidStation(t, fakeStation(s), coordsOptional) + } + const _assertValidStopover = (t, s, coordsOptional = false) => { + const fake = Object.assign({}, s, { + station: fakeStation(s.station) + }) + assertValidStopover(t, fake, coordsOptional) + } + + const vehicles = yield client.radar({ + north: 54.4, + west: 10.0, + south: 54.2, + east: 10.2 + }, { duration: 5 * 60, when }) @@ -402,7 +434,7 @@ test.skip('radar Kiel', co(function* (t) { t.ok(Array.isArray(v.nextStops)) for (let st of v.nextStops) { - assertValidStopover(t, st, true) + _assertValidStopover(t, st, true) if (st.arrival) { t.equal(typeof st.arrival, 'string') @@ -419,10 +451,15 @@ test.skip('radar Kiel', co(function* (t) { t.ok(Array.isArray(v.frames)) for (let f of v.frames) { - assertValidStation(t, f.origin, true) - assertValidStationProducts(t, f.origin.products) - assertValidStation(t, f.destination, true) - assertValidStationProducts(t, f.destination.products) + // todo: see #34 + _assertValidStation(t, f.origin, true) + if (f.origin.products) { + assertValidStationProducts(t, f.origin.products) + } + _assertValidStation(t, f.destination, true) + if (f.destination.products) { + assertValidStationProducts(t, f.destination.products) + } t.equal(typeof f.t, 'number') } } From 57f3cdb7e014e692f2b47b4e8a8ca6b1b13cbc83 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 7 May 2018 11:48:08 +0200 Subject: [PATCH 069/176] parseLocation: null as fallback station name --- parse/location.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/location.js b/parse/location.js index 1659ea92..44f34f5c 100644 --- a/parse/location.js +++ b/parse/location.js @@ -19,7 +19,7 @@ const parseLocation = (profile, l, lines) => { const station = { type: 'station', id: l.extId, - name: profile.parseStationName(l.name), + name: l.name ? profile.parseStationName(l.name) : null, location: 'number' === typeof res.latitude ? res : null } From 634b044bc513afd1f9e519d8fdff8bbcf0f99951 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 28 May 2018 20:31:47 +0200 Subject: [PATCH 070/176] nah.sh tests: fix products validation :bug: --- test/nahsh.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/nahsh.js b/test/nahsh.js index 3d0b491d..c7b377af 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -58,10 +58,9 @@ const assertIsKielHbf = (t, s) => { // todo: DRY with assertValidStationProducts // todo: DRY with other tests -const assertValidProducts = (t, p) => { - for (let product of allProducts) { - product = product.product // wat - t.equal(typeof p[product], 'boolean', 'product ' + p + ' must be a boolean') +const assertValidProducts = (t, products) => { + for (let p of allProducts) { + t.equal(typeof products[p.id], 'boolean', `product ${p.id} must be a boolean`) } } From a356a26e2fbd34940d7b3634b1c0552d41d0108b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 28 May 2018 20:32:10 +0200 Subject: [PATCH 071/176] throw if 0 products enabled :boom: This should have been the case previously, but the test didn't fail. --- format/products-filter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/format/products-filter.js b/format/products-filter.js index 8ee8ffca..e4252f8f 100644 --- a/format/products-filter.js +++ b/format/products-filter.js @@ -16,12 +16,14 @@ const createFormatProductsFilter = (profile) => { if (!isObj(filter)) throw new Error('products filter must be an object') filter = Object.assign({}, defaultProducts, filter) - let res = 0 + let res = 0, products = 0 for (let product in filter) { if (!hasProp(filter, product) || filter[product] !== true) continue if (!byProduct[product]) throw new Error('unknown product ' + product) + products++ for (let bitmask of byProduct[product].bitmasks) res += bitmask } + if (products === 0) throw new Error('no products used') return { type: 'PROD', From a17123401dda49f7bc45371c8a47de19bfd54a4d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 May 2018 15:51:58 +0200 Subject: [PATCH 072/176] fix tests failing because of weird endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB: `journeyLeg()` sometimes doesn't give the `direction` #49 - nah.sh: `departures()` at "Kiel Hbf" includes neighboring stations - ÖBB: the endpoint always returns IDs of individual stops Although these hacks make the tests less strict, we will finally be able to use the more thorough tests from the `tests-rewrite` branch. --- test/db.js | 11 ++++++++++- test/nahsh.js | 8 +++++--- test/oebb.js | 30 +++++++++++++++++++----------- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/test/db.js b/test/db.js index ea492d2a..cf605f6b 100644 --- a/test/db.js +++ b/test/db.js @@ -12,7 +12,8 @@ const createClient = require('..') const dbProfile = require('../p/db') const products = require('../p/db/products') const { - station: createValidateStation + station: createValidateStation, + journeyLeg: createValidateJourneyLeg } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') @@ -193,7 +194,15 @@ test('journey leg details', co(function* (t) { t.ok(p.line.name, 'precondition failed') const leg = yield client.journeyLeg(p.id, p.line.name, {when}) + const validateJourneyLeg = createValidateJourneyLeg(cfg) + const validate = createValidate(cfg, { + journeyLeg: (validate, leg, name) => { + if (!leg.direction) leg.direction = 'foo' // todo, see #49 + validateJourneyLeg(validate, leg, name) + } + }) validate(t, leg, 'journeyLeg', 'leg') + t.end() })) diff --git a/test/nahsh.js b/test/nahsh.js index cb3cf021..89a99a2c 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -191,8 +191,10 @@ test('journey leg details for Flensburg to Husum', co(function* (t) { t.end() })) -test('departures at Kiel Hbf', co(function* (t) { - const departures = yield client.departures(kielHbf, { +test('departures at Kiel Räucherei', co(function* (t) { + const kielRaeucherei = '3440091' + + const departures = yield client.departures(kielRaeucherei, { duration: 30, when }) @@ -200,7 +202,7 @@ test('departures at Kiel Hbf', co(function* (t) { test: t, departures, validate, - id: kielHbf + id: kielRaeucherei }) t.end() })) diff --git a/test/oebb.js b/test/oebb.js index 73929b01..d2bf9632 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -19,7 +19,6 @@ const testJourneysStationToAddress = require('./lib/journeys-station-to-address' const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') -const testDepartures = require('./lib/departures') const testJourneysWithDetour = require('./lib/journeys-with-detour') const when = createWhen('Europe/Vienna', 'de-AT') @@ -221,20 +220,29 @@ test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) { t.end() })) -test.skip('departures at Wien Renngasse', co(function* (t) { +test('departures at Wien Leibenfrostgasse', co(function* (t) { const wienLeibenfrostgasse = '1390469' - const departures = yield client.departures(wienLeibenfrostgasse, { + const ids = [ + wienLeibenfrostgasse, // station + '904029', // stop "Wien Leibenfrostgasse (Phorusgasse)s" + '904030' // stop "Wien Leibenfrostgasse (Ziegelofengasse)" + ] + + const deps = yield client.departures(wienLeibenfrostgasse, { duration: 15, when }) - // todo: fix this - // ÖBB HAFAS data is just too detailed :P - yield testDepartures({ - test: t, - departures, - validate, - id: wienLeibenfrostgasse - }) + validate(t, deps, 'departures', 'departures') + t.ok(deps.length > 0, 'must be >0 departures') + // todo: move into deps validator + t.deepEqual(deps, deps.sort((a, b) => t.when > b.when)) + + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const msg = `deps[${i}].station.id is invalid` + t.ok(ids.includes(dep.station.id, msg)) + } + t.end() })) From c82ad234e07677dce869f77458c854e5393dbae4 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 28 May 2018 20:34:24 +0200 Subject: [PATCH 073/176] journeys: opt.when -> opt.departure/opt.arrival :boom: --- docs/journeys.md | 5 ++++- index.js | 27 +++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/journeys.md b/docs/journeys.md index 016f052c..b566edb3 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -40,7 +40,10 @@ With `opt`, you can override the default options, which look like this: ```js { - when: new Date(), + // Use either `departure` or `arrival` to specify a date/time. + departure: new Date(), + arrival: null, + earlierThan: null, // ref to get journeys earlier than the last query laterThan: null, // ref to get journeys later than the last query results: 5, // how many journeys? diff --git a/index.js b/index.js index 69f831ea..cb3f312f 100644 --- a/index.js +++ b/index.js @@ -69,8 +69,8 @@ const createClient = (profile, request = _request) => { if (!isNonEmptyString(opt.earlierThan)) { throw new Error('opt.earlierThan must be a non-empty string.') } - if ('when' in opt) { - throw new Error('opt.earlierThan and opt.when are mutually exclusive.') + if (('departure' in opt) || ('arrival' in opt)) { + throw new Error('opt.earlierThan and opt.departure/opt.arrival are mutually exclusive.') } journeysRef = opt.earlierThan } @@ -78,8 +78,8 @@ const createClient = (profile, request = _request) => { if (!isNonEmptyString(opt.laterThan)) { throw new Error('opt.laterThan must be a non-empty string.') } - if ('when' in opt) { - throw new Error('opt.laterThan and opt.when are mutually exclusive.') + if (('departure' in opt) || ('arrival' in opt)) { + throw new Error('opt.laterThan and opt.departure/opt.arrival are mutually exclusive.') } journeysRef = opt.laterThan } @@ -97,8 +97,19 @@ const createClient = (profile, request = _request) => { polylines: false // return leg shapes? }, opt) if (opt.via) opt.via = profile.formatLocation(profile, opt.via, 'opt.via') - opt.when = new Date(opt.when || Date.now()) - if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') + + if (opt.when !== undefined) { + throw new Error('opt.when is not supported anymore. Use opt.departure/opt.arrival.') + } + let when = new Date(), outFrwd = true + if (opt.departure !== undefined && opt.departure !== null) { + when = new Date(opt.departure) + if (Number.isNaN(+when)) throw new Error('opt.departure is invalid') + } else if (opt.arrival !== undefined && opt.arrival !== null) { + when = new Date(opt.arrival) + if (Number.isNaN(+when)) throw new Error('opt.arrival is invalid') + outFrwd = false + } const filters = [ profile.formatProductsFilter(opt.products || {}) @@ -132,10 +143,10 @@ const createClient = (profile, request = _request) => { arrLocL: [to], jnyFltrL: filters, getTariff: !!opt.tickets, + outFrwd, // todo: what is req.gisFltrL? getPT: true, // todo: what is this? - outFrwd: true, // todo: what is this? getIV: false, // todo: walk & bike as alternatives? getPolyline: !!opt.polylines } @@ -172,7 +183,7 @@ const createClient = (profile, request = _request) => { }) } - return more(opt.when, journeysRef) + return more(when, journeysRef) } const locations = (query, opt = {}) => { From a59b340d67b4adb6c4a5766f8b3b2704272da420 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 28 May 2018 20:35:01 +0200 Subject: [PATCH 074/176] adapt tests to 1c467cd --- test/db.js | 16 ++++++++-------- test/insa.js | 12 ++++++------ test/nahsh.js | 16 ++++++++-------- test/oebb.js | 18 +++++++++--------- test/vbb.js | 35 +++++++++++++++++++++++------------ 5 files changed, 54 insertions(+), 43 deletions(-) diff --git a/test/db.js b/test/db.js index 53b29e34..20328df6 100644 --- a/test/db.js +++ b/test/db.js @@ -101,7 +101,7 @@ const regensburgHbf = '8000309' test('Berlin Jungfernheide to München Hbf', co(function* (t) { const journeys = yield client.journeys(jungfernh, münchenHbf, { - when, passedStations: true + departure: when, passedStations: true }) t.ok(Array.isArray(journeys)) @@ -144,7 +144,7 @@ test('Berlin Jungfernheide to Torfstraße 17', co(function* (t) { const journeys = yield client.journeys(jungfernh, { type: 'location', address: 'Torfstraße 17', latitude: 52.5416823, longitude: 13.3491223 - }, {when}) + }, {departure: when}) t.ok(Array.isArray(journeys)) t.ok(journeys.length >= 1, 'no journeys') @@ -173,7 +173,7 @@ test('Berlin Jungfernheide to ATZE Musiktheater', co(function* (t) { const journeys = yield client.journeys(jungfernh, { type: 'location', id: '991598902', name: 'ATZE Musiktheater', latitude: 52.542417, longitude: 13.350437 - }, {when}) + }, {departure: when}) t.ok(Array.isArray(journeys)) t.ok(journeys.length >= 1, 'no journeys') @@ -207,7 +207,7 @@ test('journeys: via works – with detour', co(function* (t) { const [journey] = yield client.journeys(westhafen, wedding, { via: württembergallee, results: 1, - when, + departure: when, passedStations: true }) @@ -228,7 +228,7 @@ test('journeys: via works – without detour', co(function* (t) { const [journey] = yield client.journeys(ruhleben, zoo, { via: kastanienallee, results: 1, - when, + departure: when, passedStations: true }) @@ -242,7 +242,7 @@ test('journeys: via works – without detour', co(function* (t) { test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { const model = yield client.journeys(jungfernh, münchenHbf, { - results: 3, when + results: 3, departure: when }) t.equal(typeof model.earlierRef, 'string') @@ -253,12 +253,12 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { // when and earlierThan/laterThan should be mutually exclusive t.throws(() => { client.journeys(jungfernh, münchenHbf, { - when, earlierThan: model.earlierRef + departure: when, earlierThan: model.earlierRef }) }) t.throws(() => { client.journeys(jungfernh, münchenHbf, { - when, laterThan: model.laterRef + departure: when, laterThan: model.laterRef }) }) diff --git a/test/insa.js b/test/insa.js index 3830c809..f510e28a 100644 --- a/test/insa.js +++ b/test/insa.js @@ -70,7 +70,7 @@ test('Magdeburg Hbf to Magdeburg-Buckau', co(function*(t) { const magdeburgHbf = '8010224' const magdeburgBuckau = '8013456' const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { - when, + departure: when, passedStations: true }) @@ -110,7 +110,7 @@ test('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { } const journeys = yield client.journeys(magdeburgHbf, sternStr, { - when + departure: when }) t.ok(Array.isArray(journeys)) @@ -147,7 +147,7 @@ test('Kloster Unser Lieben Frauen to Magdeburg Hbf', co(function*(t) { } const magdeburgHbf = '8010224' const journeys = yield client.journeys(kloster, magdeburgHbf, { - when + departure: when }) t.ok(Array.isArray(journeys)) @@ -185,7 +185,7 @@ test('journeys: via works – with detour', co(function* (t) { const [journey] = yield client.journeys(hasselbachplatzSternstrasse, stendal, { via: dessau, results: 1, - when, + departure: when, passedStations: true }) @@ -208,7 +208,7 @@ test('journeys: via works – without detour', co(function* (t) { const [journey] = yield client.journeys(hasselbachplatzSternstrasse, universitaet, { via: breiterWeg, results: 1, - when, + departure: when, passedStations: true }) @@ -270,7 +270,7 @@ test('journey leg details', co(function* (t) { const magdeburgHbf = '8010224' const magdeburgBuckau = '8013456' const [journey] = yield client.journeys(magdeburgHbf, magdeburgBuckau, { - results: 1, when + results: 1, departure: when }) const p = journey.legs[0] diff --git a/test/nahsh.js b/test/nahsh.js index c7b377af..50a561b7 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -99,7 +99,7 @@ const schleswig = '8005362' test('Kiel Hbf to Flensburg', co(function* (t) { const journeys = yield client.journeys(kielHbf, flensburg, { - when, passedStations: true + departure: when, passedStations: true }) t.ok(Array.isArray(journeys)) @@ -148,7 +148,7 @@ test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { address: 'Husum, Zingel 10' } - const journeys = yield client.journeys(kielHbf, zingel, {when}) + const journeys = yield client.journeys(kielHbf, zingel, {departure: when}) t.ok(Array.isArray(journeys)) t.ok(journeys.length >= 1, 'no journeys') @@ -185,7 +185,7 @@ test('Holstentor to Kiel Hbf', co(function* (t) { name: 'Hansestadt Lübeck, Holstentor (Denkmal)', id: '970003547' } - const journeys = yield client.journeys(holstentor, kielHbf, {when}) + const journeys = yield client.journeys(holstentor, kielHbf, {departure: when}) t.ok(Array.isArray(journeys)) t.ok(journeys.length >= 1, 'no journeys') @@ -219,7 +219,7 @@ test('Husum to Lübeck Hbf with stopover at Husum', co(function* (t) { const [journey] = yield client.journeys(husum, luebeckHbf, { via: kielHbf, results: 1, - when + departure: when }) const i1 = journey.legs.findIndex(leg => leg.destination.id === kielHbf) @@ -234,7 +234,7 @@ test('Husum to Lübeck Hbf with stopover at Husum', co(function* (t) { test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { const model = yield client.journeys(kielHbf, flensburg, { - results: 3, when + results: 3, departure: when }) t.equal(typeof model.earlierRef, 'string') @@ -245,12 +245,12 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { // when and earlierThan/laterThan should be mutually exclusive t.throws(() => { client.journeys(kielHbf, flensburg, { - when, earlierThan: model.earlierRef + departure: when, earlierThan: model.earlierRef }) }) t.throws(() => { client.journeys(kielHbf, flensburg, { - when, laterThan: model.laterRef + departure: when, laterThan: model.laterRef }) }) @@ -284,7 +284,7 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { test('leg details for Flensburg to Husum', co(function* (t) { const journeys = yield client.journeys(flensburg, husum, { - results: 1, when + results: 1, departure: when }) const p = journeys[0].legs[0] diff --git a/test/oebb.js b/test/oebb.js index 2c7c9623..a45dc0b1 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -120,7 +120,7 @@ const grazHbf = '8100173' test('Salzburg Hbf to Wien Westbahnhof', co(function* (t) { const journeys = yield client.journeys(salzburgHbf, wienWestbahnhof, { - when, passedStations: true + departure: when, passedStations: true }) t.ok(Array.isArray(journeys)) @@ -169,7 +169,7 @@ test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co(function* (t) { address: '1220 Wien, Wagramer Straße 5' } - const journeys = yield client.journeys(salzburgHbf, wagramerStr, {when}) + const journeys = yield client.journeys(salzburgHbf, wagramerStr, {departure: when}) t.ok(Array.isArray(journeys)) t.ok(journeys.length >= 1, 'no journeys') @@ -206,7 +206,7 @@ test('Albertina to Salzburg Hbf', co(function* (t) { name: 'Albertina', id: '975900003' } - const journeys = yield client.journeys(albertina, salzburgHbf, {when}) + const journeys = yield client.journeys(albertina, salzburgHbf, {departure: when}) t.ok(Array.isArray(journeys)) t.ok(journeys.length >= 1, 'no journeys') @@ -246,7 +246,7 @@ test('journeys: via works – with detour', co(function* (t) { const [journey] = yield client.journeys(stephansplatz, schottenring, { via: donauinsel, results: 1, - when, + departure: when, passedStations: true }) @@ -269,7 +269,7 @@ test('journeys: via works – without detour', co(function* (t) { const [journey] = yield client.journeys(karlsplatz, praterstern, { via: museumsquartier, results: 1, - when, + departure: when, passedStations: true }) @@ -283,7 +283,7 @@ test('journeys: via works – without detour', co(function* (t) { test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t) { const model = yield client.journeys(salzburgHbf, wienWestbahnhof, { - results: 3, when + results: 3, departure: when }) t.equal(typeof model.earlierRef, 'string') @@ -294,12 +294,12 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t // when and earlierThan/laterThan should be mutually exclusive t.throws(() => { client.journeys(salzburgHbf, wienWestbahnhof, { - when, earlierThan: model.earlierRef + departure: when, earlierThan: model.earlierRef }) }) t.throws(() => { client.journeys(salzburgHbf, wienWestbahnhof, { - when, laterThan: model.laterRef + departure: when, laterThan: model.laterRef }) }) @@ -333,7 +333,7 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) { const journeys = yield client.journeys(wienWestbahnhof, muenchenHbf, { - results: 1, when + results: 1, departure: when }) const p = journeys[0].legs[0] diff --git a/test/vbb.js b/test/vbb.js index c94dc9d5..1b3c1a2a 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -60,7 +60,7 @@ const bismarckstr = '900000024201' test('journeys – station to station', co(function* (t) { const journeys = yield client.journeys(spichernstr, amrumerStr, { - results: 3, when, passedStations: true + results: 3, departure: when, passedStations: true }) t.ok(Array.isArray(journeys)) @@ -108,7 +108,7 @@ test('journeys – station to station', co(function* (t) { test('journeys – only subway', co(function* (t) { const journeys = yield client.journeys(spichernstr, bismarckstr, { - results: 20, when, + results: 20, departure: when, products: { suburban: false, subway: true, @@ -136,9 +136,10 @@ test('journeys – only subway', co(function* (t) { })) test('journeys – fails with no product', co(function* (t) { + t.plan(1) try { client.journeys(spichernstr, bismarckstr, { - when, + departure: when, products: { suburban: false, subway: false, @@ -159,7 +160,7 @@ test('journeys – fails with no product', co(function* (t) { test('earlier/later journeys', co(function* (t) { const model = yield client.journeys(spichernstr, bismarckstr, { - results: 3, when + results: 3, departure: when }) t.equal(typeof model.earlierRef, 'string') @@ -167,15 +168,25 @@ test('earlier/later journeys', co(function* (t) { t.equal(typeof model.laterRef, 'string') t.ok(model.laterRef) - // when and earlierThan/laterThan should be mutually exclusive + // departure/arrival and earlierThan/laterThan should be mutually exclusive t.throws(() => { client.journeys(spichernstr, bismarckstr, { - when, earlierThan: model.earlierRef + departure: when, earlierThan: model.earlierRef }) }) t.throws(() => { client.journeys(spichernstr, bismarckstr, { - when, laterThan: model.laterRef + departure: when, laterThan: model.laterRef + }) + }) + t.throws(() => { + client.journeys(spichernstr, bismarckstr, { + arrival: when, earlierThan: model.earlierRef + }) + }) + t.throws(() => { + client.journeys(spichernstr, bismarckstr, { + arrival: when, laterThan: model.laterRef }) }) @@ -209,7 +220,7 @@ test('earlier/later journeys', co(function* (t) { test('journey leg details', co(function* (t) { const journeys = yield client.journeys(spichernstr, amrumerStr, { - results: 1, when + results: 1, departure: when }) const p = journeys[0].legs[0] @@ -238,7 +249,7 @@ test('journeys – station to address', co(function* (t) { type: 'location', address: 'Torfstr. 17, Berlin', latitude: 52.541797, longitude: 13.350042 - }, {results: 1, when}) + }, {results: 1, departure: when}) t.ok(Array.isArray(journeys)) t.strictEqual(journeys.length, 1) @@ -267,7 +278,7 @@ test('journeys – station to POI', co(function* (t) { id: '900980720', name: 'Berlin, Atze Musiktheater für Kinder', latitude: 52.543333, longitude: 13.351686 - }, {results: 1, when}) + }, {results: 1, departure: when}) t.ok(Array.isArray(journeys)) t.strictEqual(journeys.length, 1) @@ -298,7 +309,7 @@ test('journeys: via works – with detour', co(function* (t) { const [journey] = yield client.journeys(westhafen, wedding, { via: württembergallee, results: 1, - when, + departure: when, passedStations: true }) @@ -319,7 +330,7 @@ test('journeys: via works – without detour', co(function* (t) { const [journey] = yield client.journeys(ruhleben, zoo, { via: kastanienallee, results: 1, - when, + departure: when, passedStations: true }) From 0169e837313767cf46a2bafc8880e27ce5991407 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 28 May 2018 21:09:11 +0200 Subject: [PATCH 075/176] journeys: add test for opt.arrival --- test/vbb.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/vbb.js b/test/vbb.js index 1b3c1a2a..cc159894 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -158,6 +158,23 @@ test('journeys – fails with no product', co(function* (t) { } })) +test('journeys before date/time', co(function* (t) { + const journeys = yield client.journeys(spichernstr, bismarckstr, { + results: 3, arrival: when + }) + + for (let i = 0; i < journeys.length; i++) { + const j = journeys[i] + const name = `journeys[${i}]` + + const lastLeg = j.legs[j.legs.length - 1] + const arr = +new Date(lastLeg.arrival) + t.ok(arr <= when, name + '.arrival is after `when`') + } + + t.end() +})) + test('earlier/later journeys', co(function* (t) { const model = yield client.journeys(spichernstr, bismarckstr, { results: 3, departure: when From 856a751b513f6cb8d8a3afb4c2b8b6e22a397747 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 May 2018 16:15:26 +0200 Subject: [PATCH 076/176] journeys: make opt.departure & opt.arrival mutually exclusive --- index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index cb3f312f..1db64568 100644 --- a/index.js +++ b/index.js @@ -62,7 +62,10 @@ const createClient = (profile, request = _request) => { to = profile.formatLocation(profile, to, 'to') if (('earlierThan' in opt) && ('laterThan' in opt)) { - throw new Error('opt.laterThan and opt.laterThan are mutually exclusive.') + throw new Error('opt.earlierThan and opt.laterThan are mutually exclusive.') + } + if (('departure' in opt) && ('arrival' in opt)) { + throw new Error('opt.departure and opt.arrival are mutually exclusive.') } let journeysRef = null if ('earlierThan' in opt) { From 0775a27d1d2e33bc4b537402ce070cdaaf79d7d9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 1 Jun 2018 11:12:54 +0200 Subject: [PATCH 077/176] 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 a7c550ab69b8ed755ced1c7676690d93282a0507 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 12:04:21 +0200 Subject: [PATCH 078/176] send Accept: application/json :bug: --- lib/request.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/request.js b/lib/request.js index 3cde8fbb..7ecaabba 100644 --- a/lib/request.js +++ b/lib/request.js @@ -20,6 +20,7 @@ const request = (profile, data) => { headers: { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate', + 'Accept': 'application/json', 'user-agent': 'https://github.com/public-transport/hafas-client' }, query: {} From 0136189aa4d0137cb647fec6cece3f399ab0eb4a Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 12:59:50 +0200 Subject: [PATCH 079/176] tests: fix isRoughlyEqual calls :white_check_mark: --- test/db.js | 8 ++++---- test/insa.js | 8 ++++---- test/nahsh.js | 8 ++++---- test/oebb.js | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/test/db.js b/test/db.js index 20328df6..7741ffa4 100644 --- a/test/db.js +++ b/test/db.js @@ -56,8 +56,8 @@ const isJungfernheide = (s) => { (s.id === '008011167' || s.id === jungfernh) && s.name === 'Berlin Jungfernheide' && s.location && - isRoughlyEqual(s.location.latitude, 52.530408, .0005) && - isRoughlyEqual(s.location.longitude, 13.299424, .0005) + isRoughlyEqual(.0005, s.location.latitude, 52.530408) && + isRoughlyEqual(.0005, s.location.longitude, 13.299424) } const assertIsJungfernheide = (t, s) => { @@ -65,8 +65,8 @@ const assertIsJungfernheide = (t, s) => { t.ok(s.id === '008011167' || s.id === jungfernh, 'id should be 8011167') t.equal(s.name, 'Berlin Jungfernheide') t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 52.530408, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 13.299424, .0005)) + t.ok(isRoughlyEqual(.0005, s.location.latitude, 52.530408)) + t.ok(isRoughlyEqual(.0005, s.location.longitude, 13.299424)) } // todo: DRY with assertValidStationProducts diff --git a/test/insa.js b/test/insa.js index f510e28a..7e2b1b38 100644 --- a/test/insa.js +++ b/test/insa.js @@ -40,8 +40,8 @@ const isMagdeburgHbf = s => { (s.id === '8010224' || s.id === '008010224') && s.name === 'Magdeburg Hbf' && s.location && - isRoughlyEqual(s.location.latitude, 52.130352, 0.001) && - isRoughlyEqual(s.location.longitude, 11.626891, 0.001) + isRoughlyEqual(.001, s.location.latitude, 52.130352) && + isRoughlyEqual(.001, s.location.longitude, 11.626891) ) } @@ -50,8 +50,8 @@ const assertIsMagdeburgHbf = (t, s) => { t.ok(s.id === '8010224' || s.id === '008010224', 'id should be 8010224') t.equal(s.name, 'Magdeburg Hbf') t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 52.130352, 0.001)) - t.ok(isRoughlyEqual(s.location.longitude, 11.626891, 0.001)) + t.ok(isRoughlyEqual(.001, s.location.latitude, 52.130352)) + t.ok(isRoughlyEqual(.001, s.location.longitude, 11.626891)) } // todo: DRY with assertValidStationProducts diff --git a/test/nahsh.js b/test/nahsh.js index 50a561b7..6ee4d33f 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -43,8 +43,8 @@ const isKielHbf = (s) => { (s.id === '8000199') && s.name === 'Kiel Hbf' && s.location && - isRoughlyEqual(s.location.latitude, 54.314982, .0005) && - isRoughlyEqual(s.location.longitude, 10.131976, .0005) + isRoughlyEqual(.0005, s.location.latitude, 54.314982) && + isRoughlyEqual(.0005, s.location.longitude, 10.131976) } const assertIsKielHbf = (t, s) => { @@ -52,8 +52,8 @@ const assertIsKielHbf = (t, s) => { t.ok(s.id === '8000199', 'id should be 8000199') t.equal(s.name, 'Kiel Hbf') t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 54.314982, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 10.131976, .0005)) + t.ok(isRoughlyEqual(.0005, s.location.latitude, 54.314982)) + t.ok(isRoughlyEqual(.0005, s.location.longitude, 10.131976)) } // todo: DRY with assertValidStationProducts diff --git a/test/oebb.js b/test/oebb.js index a45dc0b1..9db6a42f 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -60,8 +60,8 @@ const isSalzburgHbf = (s) => { (s.id === '008100002' || s.id === '8100002') && s.name === 'Salzburg Hbf' && s.location && - isRoughlyEqual(s.location.latitude, 47.812851, .0005) && - isRoughlyEqual(s.location.longitude, 13.045604, .0005) + isRoughlyEqual(.0005, s.location.latitude, 47.812851) && + isRoughlyEqual(.0005, s.location.longitude, 13.045604) } const assertIsSalzburgHbf = (t, s) => { @@ -69,8 +69,8 @@ const assertIsSalzburgHbf = (t, s) => { t.ok(s.id === '008100002' || s.id === '8100002', 'id should be 8100002') t.equal(s.name, 'Salzburg Hbf') t.ok(s.location) - t.ok(isRoughlyEqual(s.location.latitude, 47.812851, .0005)) - t.ok(isRoughlyEqual(s.location.longitude, 13.045604, .0005)) + t.ok(isRoughlyEqual(.0005, s.location.latitude, 47.812851)) + t.ok(isRoughlyEqual(.0005, s.location.longitude, 13.045604)) } // todo: DRY with assertValidStationProducts From f3609ebd800e3e9a99ed1bd1e0c717ab48bf363c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 15:41:00 +0200 Subject: [PATCH 080/176] 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 081/176] 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 082/176] 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 083/176] 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 084/176] 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 21c273cec7e71612b6a32b89068a211e56a8fa5d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 7 Jun 2018 18:52:12 +0200 Subject: [PATCH 085/176] journey leg passed stations: parse & expose delays --- parse/departure.js | 2 ++ parse/journey-leg.js | 2 ++ parse/stopover.js | 18 +++++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/parse/departure.js b/parse/departure.js index 158eb1ea..1a496bc7 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -30,6 +30,8 @@ const createParseDeparture = (profile, stations, lines, remarks) => { res.delay = Math.round((realtime - planned) / 1000) } else res.delay = null + // todo: DRY with parseStopover + // todo: DRY with parseJourneyLeg if (d.stbStop.aCncl || d.stbStop.dCncl) { res.cancelled = true Object.defineProperty(res, 'canceled', {value: true}) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index bbc3f9da..331f0c36 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -23,6 +23,8 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => arrival: arr.toISO() } + // todo: DRY with parseDeparture + // todo: DRY with parseStopover if (pt.dep.dTimeR && pt.dep.dTimeS) { const realtime = profile.parseDateTime(profile, j.date, pt.dep.dTimeR) const planned = profile.parseDateTime(profile, j.date, pt.dep.dTimeS) diff --git a/parse/stopover.js b/parse/stopover.js index 8315c814..52f69b17 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -7,16 +7,32 @@ const createParseStopover = (profile, stations, lines, remarks, date) => { const res = { station: stations[parseInt(st.locX)] || null, arrival: null, - departure: null + arrivalDelay: null, + departure: null, + departureDelay: null } + + // todo: DRY with parseDeparture + // todo: DRY with parseJourneyLeg if (st.aTimeR || st.aTimeS) { const arr = profile.parseDateTime(profile, date, st.aTimeR || st.aTimeS) res.arrival = arr.toISO() } + if (st.aTimeR && st.aTimeS) { + const realtime = profile.parseDateTime(profile, date, st.aTimeR) + const planned = profile.parseDateTime(profile, date, st.aTimeS) + res.arrivalDelay = Math.round((realtime - planned) / 1000) + } + if (st.dTimeR || st.dTimeS) { const dep = profile.parseDateTime(profile, date, st.dTimeR || st.dTimeS) res.departure = dep.toISO() } + if (st.dTimeR && st.dTimeS) { + const realtime = profile.parseDateTime(profile, date, st.dTimeR) + const planned = profile.parseDateTime(profile, date, st.dTimeS) + res.departureDelay = Math.round((realtime - planned) / 1000) + } // mark stations the train passes without stopping if(st.dInS === false && st.aOutS === false) res.passBy = true From 665bed9f79ae2e4fe85c324308870ca920dd82bb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 4 Jun 2018 19:00:19 +0200 Subject: [PATCH 086/176] rename location(id) -> station(id) :boom: see #17 for reasons closes #17 --- index.js | 4 ++-- p/db/example.js | 2 +- p/insa/example.js | 2 +- p/nahsh/example.js | 2 +- p/oebb/example.js | 2 +- p/vbb/example.js | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 1db64568..4826e28d 100644 --- a/index.js +++ b/index.js @@ -221,7 +221,7 @@ const createClient = (profile, request = _request) => { }) } - const location = (station) => { + const station = (station) => { if ('object' === typeof station) station = profile.formatStation(station.id) else if ('string' === typeof station) station = profile.formatStation(station) else throw new Error('station must be an object or a string.') @@ -368,7 +368,7 @@ const createClient = (profile, request = _request) => { }) } - const client = {departures, journeys, locations, location, nearby} + const client = {departures, journeys, locations, station, nearby} if (profile.journeyLeg) client.journeyLeg = journeyLeg if (profile.radar) client.radar = radar Object.defineProperty(client, 'profile', {value: profile}) diff --git a/p/db/example.js b/p/db/example.js index f740bea9..f9fe23e6 100644 --- a/p/db/example.js +++ b/p/db/example.js @@ -10,7 +10,7 @@ client.journeys('8011167', '8000261', {results: 1, tickets: true}) // client.departures('8011167', {duration: 1}) // client.locations('Berlin Jungfernheide') // client.locations('Atze Musiktheater', {poi: true, addressses: false, fuzzy: false}) -// client.location('8000309') // Regensburg Hbf +// client.station('8000309') // Regensburg Hbf // client.nearby(52.4751309, 13.3656537, {results: 1}) .then((data) => { diff --git a/p/insa/example.js b/p/insa/example.js index 8cbe8679..2260f34b 100644 --- a/p/insa/example.js +++ b/p/insa/example.js @@ -10,7 +10,7 @@ client.journeys('008010226', '008013456', {results: 1}) // client.departures('008010226', { duration: 5 }) // client.locations('Magdeburg Hbf', {results: 2}) // client.locations('Kunstmuseum Kloster Unser Lieben Frauen Magdeburg', {results: 2}) -// client.location('008010226') // Magdeburg-Neustadt +// client.station('008010226') // Magdeburg-Neustadt // client.nearby({ // type: 'location', // latitude: 52.148842, diff --git a/p/nahsh/example.js b/p/nahsh/example.js index ada76608..f3ec6237 100644 --- a/p/nahsh/example.js +++ b/p/nahsh/example.js @@ -10,7 +10,7 @@ client.journeys('8000103', '8000199', {results: 10, tickets: true}) // client.departures('8000199', {duration: 10}) // client.journeyLeg('1|30161|5|100|14032018', 'Bus 52') // client.locations('Schleswig', {results: 1}) -// client.location('706990') // Kiel Holunderbusch +// client.station('706990') // Kiel Holunderbusch // client.nearby({type: 'location', latitude: 54.295691, longitude: 10.116424}, {distance: 60}) // client.radar(54.4, 10.0, 54.2, 10.2, {results: 10}) diff --git a/p/oebb/example.js b/p/oebb/example.js index 2dcccf39..4f780f66 100644 --- a/p/oebb/example.js +++ b/p/oebb/example.js @@ -9,7 +9,7 @@ const client = createClient(oebbProfile) client.journeys('1291501', '8100002', {results: 1}) // client.departures('8100002', {duration: 1}) // client.locations('Salzburg', {results: 2}) -// client.location('8100173') // Graz Hbf +// client.station('8100173') // Graz Hbf // client.nearby(47.812851, 13.045604, {distance: 60}) // client.radar({ // north: 47.827203, diff --git a/p/vbb/example.js b/p/vbb/example.js index 82563af6..1234692a 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -9,7 +9,7 @@ const client = createClient(vbbProfile) client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // client.departures('900000013102', {duration: 1}) // client.locations('Alexanderplatz', {results: 2}) -// client.location('900000042101') // Spichernstr +// client.station('900000042101') // Spichernstr // client.nearby(52.5137344, 13.4744798, {distance: 60}) // client.radar({ // north: 52.52411, From 2d11cfae1ef224b9bed7ca90c456007938bc6c7d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 4 Jun 2018 19:01:09 +0200 Subject: [PATCH 087/176] adapt docs to 3c9c5cb :memo: --- docs/readme.md | 2 +- docs/{location.md => station.md} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename docs/{location.md => station.md} (86%) diff --git a/docs/readme.md b/docs/readme.md index ea55aa82..fa511bb8 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -4,7 +4,7 @@ - [`journeyLeg(ref, lineName, [opt])`](journey-leg.md) – get details for a leg of a journey - [`departures(station, [opt])`](departures.md) – query the next departures at a station - [`locations(query, [opt])`](locations.md) – find stations, POIs and addresses -- [`location(id)`](location.md) – get details about a location +- [`station(id)`](station.md) – get details about a station - [`nearby(location, [opt])`](nearby.md) – show stations & POIs around - [`radar(north, west, south, east, [opt])`](radar.md) – find all vehicles currently in a certain area diff --git a/docs/location.md b/docs/station.md similarity index 86% rename from docs/location.md rename to docs/station.md index 72cf9a40..1a0dd31b 100644 --- a/docs/location.md +++ b/docs/station.md @@ -1,9 +1,9 @@ -# `location(station)` +# `station(id)` -`station` must be in one of these formats: +`id` must be in one of these formats: ```js -// a station ID, in a format compatible to the profile you use +// a station ID, in a format compatible with the profile you use '900000123456' // an FPTF `station` object @@ -29,7 +29,7 @@ const vbbProfile = require('hafas-client/p/vbb') const client = createClient(vbbProfile) -client.location('900000042101') // U Spichernstr. +client.station('900000042101') // U Spichernstr. .then(console.log) .catch(console.error) ``` From 46c05eba05cfc688c100c2f5f5d04c82bba2e395 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 4 Jun 2018 19:01:21 +0200 Subject: [PATCH 088/176] adapt tests to 3c9c5cb :white_check_mark: --- test/db.js | 4 ++-- test/insa.js | 4 ++-- test/nahsh.js | 4 ++-- test/oebb.js | 4 ++-- test/vbb.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/db.js b/test/db.js index 7741ffa4..ff578620 100644 --- a/test/db.js +++ b/test/db.js @@ -367,8 +367,8 @@ test('locations named Jungfernheide', co(function* (t) { t.end() })) -test('location', co(function* (t) { - const loc = yield client.location(regensburgHbf) +test('station', co(function* (t) { + const loc = yield client.station(regensburgHbf) assertValidStation(t, loc) t.equal(loc.id, regensburgHbf) diff --git a/test/insa.js b/test/insa.js index 7e2b1b38..003b5be7 100644 --- a/test/insa.js +++ b/test/insa.js @@ -312,9 +312,9 @@ test('locations named Magdeburg', co(function*(t) { t.end() })) -test('location', co(function*(t) { +test('station', co(function*(t) { const magdeburgBuckau = '8013456' - const loc = yield client.location(magdeburgBuckau) + const loc = yield client.station(magdeburgBuckau) assertValidStation(t, loc) t.equal(loc.id, magdeburgBuckau) diff --git a/test/nahsh.js b/test/nahsh.js index 6ee4d33f..51198bb8 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -369,8 +369,8 @@ test('locations named Kiel', co(function* (t) { t.end() })) -test('location', co(function* (t) { - const loc = yield client.location(schleswig) +test('station', co(function* (t) { + const loc = yield client.station(schleswig) assertValidStation(t, loc) t.equal(loc.id, schleswig) diff --git a/test/oebb.js b/test/oebb.js index 9db6a42f..d57b4a7e 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -418,8 +418,8 @@ test('locations named Salzburg', co(function* (t) { t.end() })) -test('location', co(function* (t) { - const loc = yield client.location(grazHbf) +test('station', co(function* (t) { + const loc = yield client.station(grazHbf) assertValidStation(t, loc) t.equal(loc.id, grazHbf) diff --git a/test/vbb.js b/test/vbb.js index cc159894..96a8d4ab 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -456,8 +456,8 @@ test('locations', co(function* (t) { t.end() })) -test('location', co(function* (t) { - const loc = yield client.location(spichernstr) +test('station', co(function* (t) { + const loc = yield client.station(spichernstr) assertValidStation(t, loc) t.equal(loc.id, spichernstr) From d3d23140fd1f4f2d1d78b2f9f7712c72ef7ce3a7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 11 Jun 2018 11:29:32 +0200 Subject: [PATCH 089/176] (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 090/176] 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 091/176] 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 092/176] 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 } } From 8881d8a1a42b3143534b1b9127e8b5db2e7a1c94 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 13 Jun 2018 18:59:56 +0200 Subject: [PATCH 093/176] parse fns: individual params -> data object :boom: From now on, all higher-level parse fns will be able to access more of the response data while keeping their signature. - parseLine - parseDeparture - parseJourney - parseJourneyLeg - parseLocation - parseMovement - parsePolyline - parseNearby - parseStopover --- docs/writing-a-profile.md | 4 ++-- index.js | 34 +++++++++++++++++++++++++--------- lib/request.js | 6 ++++-- p/db/index.js | 4 ++-- p/nahsh/index.js | 12 ++++++------ p/oebb/index.js | 8 ++++---- p/vbb/index.js | 16 ++++++++-------- parse/departure.js | 2 +- parse/journey-leg.js | 12 +++++++----- parse/journey.js | 4 ++-- parse/line.js | 2 +- parse/location.js | 4 ++-- parse/movement.js | 10 ++++++---- parse/nearby.js | 6 +++--- parse/polyline.js | 2 +- parse/stopover.js | 4 ++-- 16 files changed, 76 insertions(+), 54 deletions(-) diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 8d5e0236..5d6955d7 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -37,8 +37,8 @@ Assuming the endpoint returns all lines names prefixed with `foo `, We can strip // get the default line parser const createParseLine = require('hafas-client/parse/line') -const createParseLineWithoutFoo = (profile, operators) => { - const parseLine = createParseLine(profile, operators) +const createParseLineWithoutFoo = (profile, data) => { + const parseLine = createParseLine(profile, data) // wrapper function with additional logic const parseLineWithoutFoo = (l) => { diff --git a/index.js b/index.js index 4826e28d..ca120390 100644 --- a/index.js +++ b/index.js @@ -51,7 +51,11 @@ 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, { + locations: d.locations, + lines: d.lines, + remarks: d.remarks + }) return d.jnyL.map(parse) .sort((a, b) => new Date(a.when) - new Date(b.when)) }) @@ -163,8 +167,12 @@ const createClient = (profile, request = _request) => { .then((d) => { 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, { + locations: d.locations, + lines: d.lines, + remarks: d.remarks, + polylines: opt.polylines && d.common.polyL || [] + }) if (!journeys.earlierRef) journeys.earlierRef = d.outCtxScrB @@ -217,7 +225,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!d.match || !Array.isArray(d.match.locL)) return [] const parse = profile.parseLocation - return d.match.locL.map(loc => parse(profile, loc, d.lines)) + return d.match.locL.map(loc => parse(profile, {lines: d.lines}, loc)) }) } @@ -237,7 +245,7 @@ const createClient = (profile, request = _request) => { // todo: proper stack trace? throw new Error('invalid response') } - return profile.parseLocation(profile, d.locL[0], d.lines) + return profile.parseLocation(profile, {lines: d.lines}, d.locL[0]) }) } @@ -309,8 +317,12 @@ 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, { + locations: d.locations, + lines: d.lines, + remarks: d.remarks, + polylines: opt.polyline && d.common.polyL || [] + }) const leg = { // pretend the leg is contained in a journey type: 'JNY', @@ -362,8 +374,12 @@ const createClient = (profile, request = _request) => { .then((d) => { 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, { + locations: d.locations, + lines: d.lines, + remarks: d.remarks, + polylines: opt.polyline && d.common.polyL || [] + }) return d.jnyL.map(parse) }) } diff --git a/lib/request.js b/lib/request.js index 7ecaabba..b02d43fb 100644 --- a/lib/request.js +++ b/lib/request.js @@ -88,11 +88,13 @@ const request = (profile, data) => { d.operators = c.opL.map(op => profile.parseOperator(profile, op)) } if (Array.isArray(c.prodL)) { - const parse = profile.parseLine(profile, d.operators) + const parse = profile.parseLine(profile, { + operators: d.operators + }) d.lines = c.prodL.map(parse) } if (Array.isArray(c.locL)) { - const parse = loc => profile.parseLocation(profile, loc, d.lines) + const parse = loc => profile.parseLocation(profile, {lines: d.lines}, loc) d.locations = c.locL.map(parse) } return d diff --git a/p/db/index.js b/p/db/index.js index b8b4fc60..379c1dc0 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, data) => { + const parseJourney = _createParseJourney(profile, data) // 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..cec70a63 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -21,8 +21,8 @@ const transformReqBody = (body) => { return body } -const parseLocation = (profile, l, lines) => { - const res = _parseLocation(profile, l, lines) +const parseLocation = (profile, data, l) => { + const res = _parseLocation(profile, data, l) // weird fix for empty lines, e.g. IC/EC at Flensburg Hbf if (res.lines) { res.lines = res.lines.filter(x => x.id && x.name) @@ -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, data) => { + const parseJourney = _createParseJourney(profile, data) 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, data) => { + const _parseMovement = _createParseMovement(profile, data) 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..1cf6b51f 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -25,10 +25,10 @@ const transformReqBody = (body) => { return body } -const parseLocation = (profile, l, lines) => { +const parseLocation = (profile, data, l) => { // ÖBB has some 'stations' **in austria** with no departures/products, // like station entrances, that are actually POIs. - const res = _parseLocation(profile, l, lines) + const res = _parseLocation(profile, data, l) if ( res.type === 'station' && !res.products && @@ -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, data) => { + const _parseMovement = _createParseMovement(profile, data) const parseMovement = (m) => { const res = _parseMovement(m) // filter out POIs diff --git a/p/vbb/index.js b/p/vbb/index.js index 1366e49f..79149d39 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -23,8 +23,8 @@ const transformReqBody = (body) => { return body } -const createParseLine = (profile, operators) => { - const parseLine = _createParseLine(profile, operators) +const createParseLine = (profile, data) => { + const parseLine = _createParseLine(profile, data) const parseLineWithMoreDetails = (l) => { const res = parseLine(l) @@ -42,8 +42,8 @@ const createParseLine = (profile, operators) => { return parseLineWithMoreDetails } -const parseLocation = (profile, l, lines) => { - const res = _parseLocation(profile, l, lines) +const parseLocation = (profile, data, l) => { + const res = _parseLocation(profile, data, l) if (res.type === 'station') { res.name = shorten(res.name) @@ -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, data) => { + const parseJourney = _createParseJourney(profile, data) 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, data) => { + const parseDeparture = _createParseDeparture(profile, data) 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 1a496bc7..17ae4a35 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -6,7 +6,7 @@ // 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 createParseDeparture = (profile, {stations, lines, remarks}) => { const findRemark = rm => remarks[parseInt(rm.remX)] || null const parseDeparture = (d) => { diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 331f0c36..9e181e3f 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -4,7 +4,9 @@ const parseDateTime = require('./date-time') const clone = obj => Object.assign({}, obj) -const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => { +const createParseJourneyLeg = (profile, data) => { + const {locations, lines, remarks, polylines} = data + // todo: finish parse/remark.js first const applyRemark = (j, rm) => {} @@ -17,8 +19,8 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => 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) const res = { - origin: clone(stations[parseInt(pt.dep.locX)]) || null, - destination: clone(stations[parseInt(pt.arr.locX)]), + origin: clone(locations[parseInt(pt.dep.locX)]) || null, + destination: clone(locations[parseInt(pt.arr.locX)]), departure: dep.toISO(), arrival: arr.toISO() } @@ -40,7 +42,7 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => let p = pt.jny.polyG.polyXL p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - const parse = profile.parsePolyline(stations) + const parse = profile.parsePolyline(data) res.polyline = p && parse(p) || null } @@ -57,7 +59,7 @@ 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, data, 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) diff --git a/parse/journey.js b/parse/journey.js index 24aaf8c8..dc141f20 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, data) => { + const parseLeg = profile.parseJourneyLeg(profile, data) // todo: c.sDays // todo: c.conSubscr diff --git a/parse/line.js b/parse/line.js index fddc66e6..2a40760f 100644 --- a/parse/line.js +++ b/parse/line.js @@ -2,7 +2,7 @@ const slugg = require('slugg') -const createParseLine = (profile, operators) => { +const createParseLine = (profile, {operators}) => { const byBitmask = [] for (let product of profile.products) { for (let bitmask of product.bitmasks) { diff --git a/parse/location.js b/parse/location.js index 44f34f5c..4859be21 100644 --- a/parse/location.js +++ b/parse/location.js @@ -7,8 +7,8 @@ const ADDRESS = 'A' // todo: what is s.rRefL? // todo: is passing in profile necessary? -// todo: [breaking] change to createParseLocation(profile, lines) => (l) => loc -const parseLocation = (profile, l, lines) => { +// todo: [breaking] change to createParseLocation(profile, data) => (l) => loc +const parseLocation = (profile, {lines}, l) => { const res = {type: 'location'} if (l.crd) { res.latitude = l.crd.y / 1000000 diff --git a/parse/movement.js b/parse/movement.js index 8a3ab401..c22a75d4 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,6 +1,8 @@ 'use strict' -const createParseMovement = (profile, locations, lines, remarks, polylines = []) => { +const createParseMovement = (profile, data) => { + const {locations, lines, remarks, polylines} = data + // todo: what is m.dirGeo? maybe the speed? // todo: what is m.stopL? // todo: what is m.proc? wut? @@ -8,7 +10,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, data, m.date) const res = { direction: profile.parseStationName(m.dirTxt), @@ -36,13 +38,13 @@ const createParseMovement = (profile, locations, lines, remarks, polylines = []) } if (m.ani.poly) { - const parse = profile.parsePolyline(locations) + const parse = profile.parsePolyline(data) res.polyline = parse(m.ani.poly) } else if (m.ani.polyG) { let p = m.ani.polyG.polyXL p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - const parse = profile.parsePolyline(locations) + const parse = profile.parsePolyline(data) res.polyline = p && parse(p) || null } } diff --git a/parse/nearby.js b/parse/nearby.js index 7c5b3b9a..0a055a54 100644 --- a/parse/nearby.js +++ b/parse/nearby.js @@ -6,9 +6,9 @@ // todo: what is s.wt? // todo: what is s.dur? -// todo: [breaking] change to createParseNearby(profile, lines) => (n) => nearby -const parseNearby = (profile, n, lines) => { - const res = profile.parseLocation(profile, n, lines) +// todo: [breaking] change to createParseNearby(profile, data) => (n) => nearby +const parseNearby = (profile, data, n) => { + const res = profile.parseLocation(profile, data, n) res.distance = n.dist return res } diff --git a/parse/polyline.js b/parse/polyline.js index 5cdcff0c..0fd60339 100644 --- a/parse/polyline.js +++ b/parse/polyline.js @@ -3,7 +3,7 @@ const {toGeoJSON} = require('@mapbox/polyline') const distance = require('gps-distance') -const createParsePolyline = (locations) => { +const createParsePolyline = ({locations}) => { // todo: what is p.delta? // todo: what is p.type? // todo: what is p.crdEncS? diff --git a/parse/stopover.js b/parse/stopover.js index 52f69b17..d9c78e63 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -2,10 +2,10 @@ // todo: arrivalDelay, departureDelay or only delay ? // todo: arrivalPlatform, departurePlatform -const createParseStopover = (profile, stations, lines, remarks, date) => { +const createParseStopover = (profile, {locations, lines, remarks}, date) => { const parseStopover = (st) => { const res = { - station: stations[parseInt(st.locX)] || null, + station: locations[parseInt(st.locX)] || null, arrival: null, arrivalDelay: null, departure: null, From a45d6402724c081c97adc5dfe3faf41f5a207b95 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 13 Jun 2018 19:30:58 +0200 Subject: [PATCH 094/176] request fn: opt param :boom: --- index.js | 14 +++++++------- lib/request.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index ca120390..0658e35e 100644 --- a/index.js +++ b/index.js @@ -36,7 +36,7 @@ const createClient = (profile, request = _request) => { const products = profile.formatProductsFilter(opt.products || {}) const dir = opt.direction ? profile.formatStation(opt.direction) : null - return request(profile, { + return request(profile, opt, { meth: 'StationBoard', req: { type: 'DEP', @@ -159,7 +159,7 @@ const createClient = (profile, request = _request) => { } if (profile.journeysNumF) query.numF = opt.results - return request(profile, { + return request(profile, opt, { cfg: {polyEnc: 'GPA'}, meth: 'TripSearch', req: profile.transformJourneysQuery(query, opt) @@ -210,7 +210,7 @@ const createClient = (profile, request = _request) => { }, opt) const f = profile.formatLocationFilter(opt.stations, opt.addresses, opt.poi) - return request(profile, { + return request(profile, opt, { cfg: {polyEnc: 'GPA'}, meth: 'LocMatch', req: {input: { @@ -234,7 +234,7 @@ const createClient = (profile, request = _request) => { else if ('string' === typeof station) station = profile.formatStation(station) else throw new Error('station must be an object or a string.') - return request(profile, { + return request(profile, opt, { meth: 'LocDetails', req: { locL: [station] @@ -267,7 +267,7 @@ const createClient = (profile, request = _request) => { stations: true, // return stations? }, opt) - return request(profile, { + return request(profile, opt, { cfg: {polyEnc: 'GPA'}, meth: 'LocGeoPos', req: { @@ -305,7 +305,7 @@ const createClient = (profile, request = _request) => { opt.when = new Date(opt.when || Date.now()) if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') - return request(profile, { + return request(profile, opt, { cfg: {polyEnc: 'GPA'}, meth: 'JourneyDetails', req: { @@ -353,7 +353,7 @@ const createClient = (profile, request = _request) => { if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const durationPerStep = opt.duration / Math.max(opt.frames, 1) * 1000 - return request(profile, { + return request(profile, opt, { meth: 'JourneyGeoPos', req: { maxJny: opt.results, diff --git a/lib/request.js b/lib/request.js index b02d43fb..fe56efc4 100644 --- a/lib/request.js +++ b/lib/request.js @@ -11,7 +11,7 @@ const {fetch} = require('fetch-ponyfill')({Promise}) const md5 = input => createHash('md5').update(input).digest() -const request = (profile, data) => { +const request = (profile, opt, data) => { const body = profile.transformReqBody({lang: 'en', svcReqL: [data]}) const req = profile.transformReq({ method: 'post', From 6ca7924f7b85f7bf5eb740d19f424f32e9897f18 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 13 Jun 2018 19:54:16 +0200 Subject: [PATCH 095/176] parsePolyline: profile param :boom: --- parse/journey-leg.js | 2 +- parse/movement.js | 4 ++-- parse/polyline.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 9e181e3f..6a73aef1 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -42,7 +42,7 @@ const createParseJourneyLeg = (profile, data) => { let p = pt.jny.polyG.polyXL p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - const parse = profile.parsePolyline(data) + const parse = profile.parsePolyline(profile, data) res.polyline = p && parse(p) || null } diff --git a/parse/movement.js b/parse/movement.js index c22a75d4..bc467838 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -38,13 +38,13 @@ const createParseMovement = (profile, data) => { } if (m.ani.poly) { - const parse = profile.parsePolyline(data) + const parse = profile.parsePolyline(profile, data) res.polyline = parse(m.ani.poly) } else if (m.ani.polyG) { let p = m.ani.polyG.polyXL p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - const parse = profile.parsePolyline(data) + const parse = profile.parsePolyline(profile, data) res.polyline = p && parse(p) || null } } diff --git a/parse/polyline.js b/parse/polyline.js index 0fd60339..2a939c3d 100644 --- a/parse/polyline.js +++ b/parse/polyline.js @@ -3,7 +3,7 @@ const {toGeoJSON} = require('@mapbox/polyline') const distance = require('gps-distance') -const createParsePolyline = ({locations}) => { +const createParsePolyline = (profile, {locations}) => { // todo: what is p.delta? // todo: what is p.type? // todo: what is p.crdEncS? From b6fbaa58251020f7240b0fdab3a2ac3dd164e37c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 13 Jun 2018 19:59:44 +0200 Subject: [PATCH 096/176] parse fns: opt param :boom: - parseLine - parseDeparture - parseJourney - parseJourneyLeg - parseLocation - parseMovement - parseNearby - parseStopover --- docs/writing-a-profile.md | 4 ++-- index.js | 14 +++++++------- lib/request.js | 4 ++-- p/db/index.js | 4 ++-- p/nahsh/index.js | 8 ++++---- p/oebb/index.js | 4 ++-- p/vbb/index.js | 16 ++++++++-------- parse/departure.js | 2 +- parse/journey-leg.js | 6 +++--- parse/journey.js | 4 ++-- parse/line.js | 2 +- parse/location.js | 4 ++-- parse/movement.js | 6 +++--- parse/nearby.js | 4 ++-- parse/polyline.js | 2 +- parse/stopover.js | 2 +- 16 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 5d6955d7..9c9af5fb 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -37,8 +37,8 @@ Assuming the endpoint returns all lines names prefixed with `foo `, We can strip // get the default line parser const createParseLine = require('hafas-client/parse/line') -const createParseLineWithoutFoo = (profile, data) => { - const parseLine = createParseLine(profile, data) +const createParseLineWithoutFoo = (profile, opt, data) => { + const parseLine = createParseLine(profile, opt, data) // wrapper function with additional logic const parseLineWithoutFoo = (l) => { diff --git a/index.js b/index.js index 0658e35e..cc81854f 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, { + const parse = profile.parseDeparture(profile, opt, { locations: d.locations, lines: d.lines, remarks: d.remarks @@ -167,7 +167,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.outConL)) return [] - const parse = profile.parseJourney(profile, { + const parse = profile.parseJourney(profile, opt, { locations: d.locations, lines: d.lines, remarks: d.remarks, @@ -225,7 +225,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!d.match || !Array.isArray(d.match.locL)) return [] const parse = profile.parseLocation - return d.match.locL.map(loc => parse(profile, {lines: d.lines}, loc)) + return d.match.locL.map(loc => parse(profile, opt, {lines: d.lines}, loc)) }) } @@ -245,7 +245,7 @@ const createClient = (profile, request = _request) => { // todo: proper stack trace? throw new Error('invalid response') } - return profile.parseLocation(profile, {lines: d.lines}, d.locL[0]) + return profile.parseLocation(profile, opt, {lines: d.lines}, d.locL[0]) }) } @@ -287,7 +287,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.locL)) return [] const parse = profile.parseNearby - return d.locL.map(loc => parse(profile, loc)) + return d.locL.map(loc => parse(profile, opt, loc)) }) } @@ -317,7 +317,7 @@ const createClient = (profile, request = _request) => { } }) .then((d) => { - const parse = profile.parseJourneyLeg(profile, { + const parse = profile.parseJourneyLeg(profile, opt, { locations: d.locations, lines: d.lines, remarks: d.remarks, @@ -374,7 +374,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.jnyL)) return [] - const parse = profile.parseMovement(profile, { + const parse = profile.parseMovement(profile, opt, { locations: d.locations, lines: d.lines, remarks: d.remarks, diff --git a/lib/request.js b/lib/request.js index fe56efc4..d0e51d53 100644 --- a/lib/request.js +++ b/lib/request.js @@ -88,13 +88,13 @@ const request = (profile, opt, data) => { d.operators = c.opL.map(op => profile.parseOperator(profile, op)) } if (Array.isArray(c.prodL)) { - const parse = profile.parseLine(profile, { + const parse = profile.parseLine(profile, opt, { operators: d.operators }) d.lines = c.prodL.map(parse) } if (Array.isArray(c.locL)) { - const parse = loc => profile.parseLocation(profile, {lines: d.lines}, loc) + const parse = loc => profile.parseLocation(profile, opt, {lines: d.lines}, loc) d.locations = c.locL.map(parse) } return d diff --git a/p/db/index.js b/p/db/index.js index 379c1dc0..8747b6ca 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -34,8 +34,8 @@ const transformJourneysQuery = (query, opt) => { return query } -const createParseJourney = (profile, data) => { - const parseJourney = _createParseJourney(profile, data) +const createParseJourney = (profile, opt, data) => { + const parseJourney = _createParseJourney(profile, opt, data) // 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 cec70a63..ca10f853 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -21,8 +21,8 @@ const transformReqBody = (body) => { return body } -const parseLocation = (profile, data, l) => { - const res = _parseLocation(profile, data, l) +const parseLocation = (profile, opt, data, l) => { + const res = _parseLocation(profile, opt, data, l) // weird fix for empty lines, e.g. IC/EC at Flensburg Hbf if (res.lines) { res.lines = res.lines.filter(x => x.id && x.name) @@ -36,8 +36,8 @@ const parseLocation = (profile, data, l) => { return res } -const createParseJourney = (profile, data) => { - const parseJourney = _createParseJourney(profile, data) +const createParseJourney = (profile, opt, data) => { + const parseJourney = _createParseJourney(profile, opt, data) const parseJourneyWithTickets = (j) => { const res = parseJourney(j) diff --git a/p/oebb/index.js b/p/oebb/index.js index 1cf6b51f..03143463 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -25,10 +25,10 @@ const transformReqBody = (body) => { return body } -const parseLocation = (profile, data, l) => { +const parseLocation = (profile, opt, data, l) => { // ÖBB has some 'stations' **in austria** with no departures/products, // like station entrances, that are actually POIs. - const res = _parseLocation(profile, data, l) + const res = _parseLocation(profile, opt, data, l) if ( res.type === 'station' && !res.products && diff --git a/p/vbb/index.js b/p/vbb/index.js index 79149d39..b58f1f19 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -23,8 +23,8 @@ const transformReqBody = (body) => { return body } -const createParseLine = (profile, data) => { - const parseLine = _createParseLine(profile, data) +const createParseLine = (profile, opt, data) => { + const parseLine = _createParseLine(profile, opt, data) const parseLineWithMoreDetails = (l) => { const res = parseLine(l) @@ -42,8 +42,8 @@ const createParseLine = (profile, data) => { return parseLineWithMoreDetails } -const parseLocation = (profile, data, l) => { - const res = _parseLocation(profile, data, l) +const parseLocation = (profile, opt, data, l) => { + const res = _parseLocation(profile, opt, data, l) if (res.type === 'station') { res.name = shorten(res.name) @@ -56,8 +56,8 @@ const parseLocation = (profile, data, l) => { return res } -const createParseJourney = (profile, data) => { - const parseJourney = _createParseJourney(profile, data) +const createParseJourney = (profile, opt, data) => { + const parseJourney = _createParseJourney(profile, opt, data) const parseJourneyWithTickets = (j) => { const res = parseJourney(j) @@ -86,8 +86,8 @@ const createParseJourney = (profile, data) => { return parseJourneyWithTickets } -const createParseDeparture = (profile, data) => { - const parseDeparture = _createParseDeparture(profile, data) +const createParseDeparture = (profile, opt, data) => { + const parseDeparture = _createParseDeparture(profile, opt, data) 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 17ae4a35..89682f70 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -6,7 +6,7 @@ // 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 createParseDeparture = (profile, opt, {stations, lines, remarks}) => { const findRemark = rm => remarks[parseInt(rm.remX)] || null const parseDeparture = (d) => { diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 6a73aef1..6f7eec3e 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -4,7 +4,7 @@ const parseDateTime = require('./date-time') const clone = obj => Object.assign({}, obj) -const createParseJourneyLeg = (profile, data) => { +const createParseJourneyLeg = (profile, opt, data) => { const {locations, lines, remarks, polylines} = data // todo: finish parse/remark.js first @@ -42,7 +42,7 @@ const createParseJourneyLeg = (profile, data) => { let p = pt.jny.polyG.polyXL p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - const parse = profile.parsePolyline(profile, data) + const parse = profile.parsePolyline(profile, opt, data) res.polyline = p && parse(p) || null } @@ -59,7 +59,7 @@ const createParseJourneyLeg = (profile, data) => { if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS if (passed && pt.jny.stopL) { - const parse = profile.parseStopover(profile, data, j.date) + const parse = profile.parseStopover(profile, opt, data, 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) diff --git a/parse/journey.js b/parse/journey.js index dc141f20..c6188da5 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -2,8 +2,8 @@ const clone = obj => Object.assign({}, obj) -const createParseJourney = (profile, data) => { - const parseLeg = profile.parseJourneyLeg(profile, data) +const createParseJourney = (profile, opt, data) => { + const parseLeg = profile.parseJourneyLeg(profile, opt, data) // todo: c.sDays // todo: c.conSubscr diff --git a/parse/line.js b/parse/line.js index 2a40760f..716bb69a 100644 --- a/parse/line.js +++ b/parse/line.js @@ -2,7 +2,7 @@ const slugg = require('slugg') -const createParseLine = (profile, {operators}) => { +const createParseLine = (profile, opt, {operators}) => { const byBitmask = [] for (let product of profile.products) { for (let bitmask of product.bitmasks) { diff --git a/parse/location.js b/parse/location.js index 4859be21..643f622c 100644 --- a/parse/location.js +++ b/parse/location.js @@ -7,8 +7,8 @@ const ADDRESS = 'A' // todo: what is s.rRefL? // todo: is passing in profile necessary? -// todo: [breaking] change to createParseLocation(profile, data) => (l) => loc -const parseLocation = (profile, {lines}, l) => { +// todo: [breaking] change to createParseLocation(profile, opt, data) => (l) => loc +const parseLocation = (profile, opt, {lines}, l) => { const res = {type: 'location'} if (l.crd) { res.latitude = l.crd.y / 1000000 diff --git a/parse/movement.js b/parse/movement.js index bc467838..5f942f86 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -10,7 +10,7 @@ const createParseMovement = (profile, data) => { // 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, data, m.date) + const pStopover = profile.parseStopover(profile, opt, data, m.date) const res = { direction: profile.parseStationName(m.dirTxt), @@ -38,13 +38,13 @@ const createParseMovement = (profile, data) => { } if (m.ani.poly) { - const parse = profile.parsePolyline(profile, data) + const parse = profile.parsePolyline(profile, opt, data) res.polyline = parse(m.ani.poly) } else if (m.ani.polyG) { let p = m.ani.polyG.polyXL p = Array.isArray(p) && polylines[p[0]] // todo: there can be >1 polyline - const parse = profile.parsePolyline(profile, data) + const parse = profile.parsePolyline(profile, opt, data) res.polyline = p && parse(p) || null } } diff --git a/parse/nearby.js b/parse/nearby.js index 0a055a54..1bc31897 100644 --- a/parse/nearby.js +++ b/parse/nearby.js @@ -7,8 +7,8 @@ // todo: what is s.dur? // todo: [breaking] change to createParseNearby(profile, data) => (n) => nearby -const parseNearby = (profile, data, n) => { - const res = profile.parseLocation(profile, data, n) +const parseNearby = (profile, opt, data, n) => { + const res = profile.parseLocation(profile, opt, data, n) res.distance = n.dist return res } diff --git a/parse/polyline.js b/parse/polyline.js index 2a939c3d..c2d5e15e 100644 --- a/parse/polyline.js +++ b/parse/polyline.js @@ -3,7 +3,7 @@ const {toGeoJSON} = require('@mapbox/polyline') const distance = require('gps-distance') -const createParsePolyline = (profile, {locations}) => { +const createParsePolyline = (profile, opt, {locations}) => { // todo: what is p.delta? // todo: what is p.type? // todo: what is p.crdEncS? diff --git a/parse/stopover.js b/parse/stopover.js index d9c78e63..f34e6aee 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, {locations, lines, remarks}, date) => { +const createParseStopover = (profile, opt, {locations, lines, remarks}, date) => { const parseStopover = (st) => { const res = { station: locations[parseInt(st.locX)] || null, From 3e18f5d41518c1f8a13b7aaf7f66e8642844dc64 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 13 Jun 2018 20:25:56 +0200 Subject: [PATCH 097/176] gitignore lockfile, add & remove todos, minor changes --- .gitignore | 2 ++ docs/departures.md | 1 + docs/writing-a-profile.md | 2 +- index.js | 1 + parse/date-time.js | 2 -- parse/location.js | 2 -- parse/operator.js | 1 - parse/remark.js | 1 - readme.md | 2 +- test/lib/validators.js | 4 ++++ 10 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index d6fb75eb..e4ed3d2b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ Thumbs.db .nvm-version node_modules npm-debug.log + +package-lock.json diff --git a/docs/departures.md b/docs/departures.md index 1a16535a..25953889 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -23,6 +23,7 @@ With `opt`, you can override the default options, which look like this: ```js { + // todo: products when: new Date(), direction: null, // only show departures heading to this station duration: 10 // show departures for the next n minutes diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 8d5e0236..288f7dd0 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -12,7 +12,7 @@ This guide is about writing such a profile. If you just want to use an already s ## 0. How do the profiles work? -A profile contains of three things: +A profile may consist of three things: - **mandatory details about the HAFAS endpoint** - `endpoint`: The protocol, host and path of the endpoint. diff --git a/index.js b/index.js index 4826e28d..cdd86fdc 100644 --- a/index.js +++ b/index.js @@ -333,6 +333,7 @@ const createClient = (profile, request = _request) => { opt = Object.assign({ results: 256, // maximum number of vehicles duration: 30, // compute frames for the next n seconds + // todo: what happens with `frames: 0`? frames: 3, // nr of frames to compute products: null, // optionally an object of booleans polylines: false // return a track shape for each vehicle? diff --git a/parse/date-time.js b/parse/date-time.js index 78635354..63ea681a 100644 --- a/parse/date-time.js +++ b/parse/date-time.js @@ -2,8 +2,6 @@ const {DateTime} = require('luxon') -const validDate = /^(\d{4})-(\d{2})-(\d{2})$/ - const parseDateTime = (profile, date, time) => { const pDate = [date.substr(-8, 4), date.substr(-4, 2), date.substr(-2, 2)] if (!pDate[0] || !pDate[1] || !pDate[2]) { diff --git a/parse/location.js b/parse/location.js index 44f34f5c..490d9ad2 100644 --- a/parse/location.js +++ b/parse/location.js @@ -5,8 +5,6 @@ const STATION = 'S' const ADDRESS = 'A' // todo: what is s.rRefL? -// todo: is passing in profile necessary? - // todo: [breaking] change to createParseLocation(profile, lines) => (l) => loc const parseLocation = (profile, l, lines) => { const res = {type: 'location'} diff --git a/parse/operator.js b/parse/operator.js index b3682199..372b525e 100644 --- a/parse/operator.js +++ b/parse/operator.js @@ -2,7 +2,6 @@ const slugg = require('slugg') -// todo: is passing in profile necessary? const parseOperator = (profile, a) => { return { type: 'operator', diff --git a/parse/remark.js b/parse/remark.js index 0b8c7a52..5b173391 100644 --- a/parse/remark.js +++ b/parse/remark.js @@ -1,6 +1,5 @@ 'use strict' -// todo: is passing in profile necessary? const parseRemark = (profile, r) => { return null // todo } diff --git a/readme.md b/readme.md index 70e885b1..2f1c5ab6 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ HAFAS endpoint | wrapper library | docs | example code | source code [Nahverkehrsverbund Schleswig-Holstein (NAH.SH)](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) | [`nahsh-hafas`](https://github.com/juliuste/nahsh-hafas) | [docs](p/nahsh/readme.md) | [example code](p/nahsh/example.js) | [src](p/nahsh/index.js) [![npm version](https://img.shields.io/npm/v/hafas-client.svg)](https://www.npmjs.com/package/hafas-client) -[![build status](https://img.shields.io/travis/public-transport/hafas-client.svg)](https://travis-ci.org/public-transport/hafas-client) +[![build status](https://img.shields.io/travis/public-transport/hafas-client.svg?branch=master)](https://travis-ci.org/public-transport/hafas-client) ![ISC-licensed](https://img.shields.io/github/license/public-transport/hafas-client.svg) [![chat on gitter](https://badges.gitter.im/public-transport/Lobby.svg)](https://gitter.im/public-transport/Lobby) [![support me on Patreon](https://img.shields.io/badge/support%20me-on%20patreon-fa7664.svg)](https://patreon.com/derhuerst) diff --git a/test/lib/validators.js b/test/lib/validators.js index ec864a04..88ddcc9c 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -185,6 +185,8 @@ const createValidateJourneyLeg = (cfg) => { a.strictEqual(typeof leg.direction, 'string', msg) a.ok(leg.direction, name + '.direction must not be empty') } + + // todo: validate polyline } return validateJourneyLeg } @@ -277,6 +279,8 @@ const validateMovement = (val, m, name = 'movement') => { anyOf(['location', 'station'], val, f.destination, fName + '.destination') a.strictEqual(typeof f.t, 'number', fName + '.frames must be a number') } + + // todo: validate polyline } const validateMovements = (val, ms, name = 'movements') => { From 6611f262bf98381f01b34d7ae5cfb2a8e49ee001 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 13 Jun 2018 20:35:06 +0200 Subject: [PATCH 098/176] leg.passed -> leg.stopovers :boom: Some didn't unterstand what "passed" means in this context. "stopovers" is a lot less ambiguous; Also, it aligns with `parseStopover` and FPTF. --- docs/journey-leg.md | 2 +- docs/journeys.md | 2 +- parse/journey-leg.js | 4 ++-- test/lib/journeys-with-detour.js | 4 ++-- test/lib/validators.js | 10 +++++----- test/nahsh.js | 4 ++-- test/oebb.js | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/journey-leg.md b/docs/journey-leg.md index e21293c7..e98f93f5 100644 --- a/docs/journey-leg.md +++ b/docs/journey-leg.md @@ -114,7 +114,7 @@ The response looked like this: } }, direction: 'S Spandau', - passed: [ /* … */ ] + stopovers: [ /* … */ ] } ``` diff --git a/docs/journeys.md b/docs/journeys.md index b566edb3..f104bd88 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -159,7 +159,7 @@ The response may look like this: } }, direction: 'S Potsdam Hauptbahnhof', - passed: [ { + stopovers: [ { station: { type: 'station', id: '900000003201', diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 331f0c36..45038d02 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -58,9 +58,9 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => if (passed && pt.jny.stopL) { const parse = profile.parseStopover(profile, stations, lines, remarks, j.date) - const passedStations = pt.jny.stopL.map(parse) + const stopovers = 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) + res.stopovers = stopovers.filter((x) => !x.passBy) } if (Array.isArray(pt.jny.remL)) { for (let remark of pt.jny.remL) applyRemark(j, remark) diff --git a/test/lib/journeys-with-detour.js b/test/lib/journeys-with-detour.js index 2358a898..0e454f3a 100644 --- a/test/lib/journeys-with-detour.js +++ b/test/lib/journeys-with-detour.js @@ -11,8 +11,8 @@ const testJourneysWithDetour = co(function* (cfg) { validate(t, journeys, 'journeys', 'journeys') const leg = journeys[0].legs.some((leg) => { - return leg.passed && leg.passed.some((passed) => { - return detourIds.includes(passed.station.id) + return leg.stopovers && leg.stopovers.some((stopover) => { + return detourIds.includes(stopover.station.id) }) }) t.ok(leg, detourIds.join('/') + ' is not being passed') diff --git a/test/lib/validators.js b/test/lib/validators.js index 88ddcc9c..3fd6ec6b 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -171,12 +171,12 @@ const createValidateJourneyLeg = (cfg) => { a.ok(leg.departurePlatform, name + '.departurePlatform must not be empty') } - if ('passed' in leg) { - a.ok(Array.isArray(leg.passed), name + '.passed must be an array') - a.ok(leg.passed.length > 0, name + '.passed must not be empty') + if ('stopovers' in leg) { + a.ok(Array.isArray(leg.stopovers), name + '.stopovers must be an array') + a.ok(leg.stopovers.length > 0, name + '.stopovers must not be empty') - for (let i = 0; i < leg.passed.length; i++) { - val.stopover(val, leg.passed[i], name + `.passed[${i}]`) + for (let i = 0; i < leg.stopovers.length; i++) { + val.stopover(val, leg.stopovers[i], name + `.stopovers[${i}]`) } } diff --git a/test/nahsh.js b/test/nahsh.js index ab82635b..0e1d88a4 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -159,8 +159,8 @@ test('Husum to Lübeck Hbf with stopover at Kiel Hbf', co(function* (t) { validate(t, journeys, 'journeys', 'journeys') const leg = journeys[0].legs.some((leg) => { - return leg.passed && leg.passed.some((passed) => { - return passed.station.id === kielHbf + return leg.stopovers && leg.stopovers.some((stopover) => { + return stopover.station.id === kielHbf }) }) t.ok(leg, 'Kiel Hbf is not being passed') diff --git a/test/oebb.js b/test/oebb.js index c88c85e5..169aba61 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -188,8 +188,8 @@ test('journeys: via works – without detour', co(function* (t) { t.notOk(l1, 'transfer at Museumsquartier') const l2 = journeys[0].legs.some((leg) => { - return leg.passed && leg.passed.some((passed) => { - return passed.station.id === museumsquartierPassed + return leg.stopovers && leg.stopovers.some((stopover) => { + return stopover.station.id === museumsquartierPassed }) }) t.ok(l2, 'Museumsquartier is not being passed') From ebe4fa64d871f711ced99d528c0171b180edc135 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 13 Jun 2018 20:39:33 +0200 Subject: [PATCH 099/176] opt.passedStations -> opt.stopovers :boom: --- docs/journey-leg.md | 2 +- docs/journeys.md | 4 ++-- index.js | 8 ++++---- parse/journey-leg.js | 6 ++++-- test/db.js | 6 ++++-- test/insa.js | 4 ++-- test/nahsh.js | 4 ++-- test/oebb.js | 6 +++--- test/vbb.js | 4 ++-- 9 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/journey-leg.md b/docs/journey-leg.md index e98f93f5..6060b36a 100644 --- a/docs/journey-leg.md +++ b/docs/journey-leg.md @@ -25,7 +25,7 @@ With `opt`, you can override the default options, which look like this: ```js { when: new Date(), - passedStations: true, // return stations on the way? + stopovers: true, // return stations on the way? polyline: false // return a shape for the leg? } ``` diff --git a/docs/journeys.md b/docs/journeys.md index f104bd88..7a9e9131 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -48,7 +48,7 @@ With `opt`, you can override the default options, which look like this: laterThan: null, // ref to get journeys later than the last query results: 5, // how many journeys? via: null, // let journeys pass this station - passedStations: false, // return stations on the way? + stopovers: false, // return stations on the way? transfers: 5, // maximum of 5 transfers transferTime: 0, // minimum time for a single transfer in minutes accessibility: 'none', // 'none', 'partial' or 'complete' @@ -83,7 +83,7 @@ const client = createClient(vbbProfile) // Hauptbahnhof to Heinrich-Heine-Str. client.journeys('900000003201', '900000100008', { results: 1, - passedStations: true + stopovers: true }) .then(console.log) .catch(console.error) diff --git a/index.js b/index.js index cdd86fdc..d31dd065 100644 --- a/index.js +++ b/index.js @@ -90,7 +90,7 @@ const createClient = (profile, request = _request) => { opt = Object.assign({ results: 5, // how many journeys? via: null, // let journeys pass this station? - passedStations: false, // return stations on the way? + stopovers: false, // return stations on the way? transfers: 5, // maximum of 5 transfers transferTime: 0, // minimum time for a single transfer in minutes // todo: does this work with every endpoint? @@ -138,7 +138,7 @@ const createClient = (profile, request = _request) => { outDate: profile.formatDate(profile, when), outTime: profile.formatTime(profile, when), ctxScr: journeysRef, - getPasslist: !!opt.passedStations, + getPasslist: !!opt.stopovers, maxChg: opt.transfers, minChgTime: opt.transferTime, depLocL: [from], @@ -291,7 +291,7 @@ const createClient = (profile, request = _request) => { throw new Error('lineName must be a non-empty string.') } opt = Object.assign({ - passedStations: true, // return stations on the way? + stopovers: true, // return stations on the way? polyline: false }, opt) opt.when = new Date(opt.when || Date.now()) @@ -318,7 +318,7 @@ const createClient = (profile, request = _request) => { arr: maxBy(d.journey.stopL, 'idx'), jny: d.journey } - return parse(d.journey, leg, !!opt.passedStations) + return parse(d.journey, leg, !!opt.stopovers) }) } diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 45038d02..d7e5eb77 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -13,7 +13,9 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => // todo: what is pt.jny.dirFlg? // todo: how does pt.freq work? // todo: what is pt.himL? - const parseJourneyLeg = (j, pt, passed = true) => { // j = journey, pt = part + + // j = journey, pt = part + const parseJourneyLeg = (j, pt, parseStopovers = true) => { 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) const res = { @@ -56,7 +58,7 @@ const createParseJourneyLeg = (profile, stations, lines, remarks, polylines) => if (pt.dep.dPlatfS) res.departurePlatform = pt.dep.dPlatfS if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS - if (passed && pt.jny.stopL) { + if (parseStopovers && pt.jny.stopL) { const parse = profile.parseStopover(profile, stations, lines, remarks, j.date) const stopovers = pt.jny.stopL.map(parse) // filter stations the train passes without stopping, as this doesn't comply with fptf (yet) diff --git a/test/db.js b/test/db.js index 2d183500..de2af4bf 100644 --- a/test/db.js +++ b/test/db.js @@ -76,7 +76,9 @@ const regensburgHbf = '8000309' test('journeys – Berlin Schwedter Str. to München Hbf', co(function* (t) { const journeys = yield client.journeys(blnSchwedterStr, münchenHbf, { - results: 3, departure: when, passedStations: true + results: 3, + departure: when, + stopovers: true }) yield testJourneysStationToStation({ @@ -160,7 +162,7 @@ test('journeys: via works – with detour', co(function* (t) { via: württembergallee, results: 1, departure: when, - passedStations: true + stopovers: true }) yield testJourneysWithDetour({ diff --git a/test/insa.js b/test/insa.js index 82bd9bfb..7f622154 100644 --- a/test/insa.js +++ b/test/insa.js @@ -44,7 +44,7 @@ test('journeys – Magdeburg Hbf to Magdeburg-Buckau', co(function* (t) { const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { results: 3, departure: when, - passedStations: true + stopovers: true }) yield testJourneysStationToStation({ @@ -125,7 +125,7 @@ test('journeys: via works – with detour', co(function* (t) { via: dessau, results: 1, departure: when, - passedStations: true + stopovers: true }) yield testJourneysWithDetour({ diff --git a/test/nahsh.js b/test/nahsh.js index 0e1d88a4..157b6afe 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -69,7 +69,7 @@ test('journeys – Kiel Hbf to Flensburg', co(function* (t) { const journeys = yield client.journeys(kielHbf, flensburg, { results: 3, departure: when, - passedStations: true + stopovers: true }) yield testJourneysStationToStation({ @@ -153,7 +153,7 @@ test('Husum to Lübeck Hbf with stopover at Kiel Hbf', co(function* (t) { via: kielHbf, results: 1, departure: when, - passedStations: true + stopovers: true }) validate(t, journeys, 'journeys', 'journeys') diff --git a/test/oebb.js b/test/oebb.js index 169aba61..62414cc7 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -62,7 +62,7 @@ test.skip('journeys – Salzburg Hbf to Wien Westbahnhof', co(function* (t) { const journeys = yield client.journeys(salzburgHbf, wienFickeystr, { results: 3, departure: when, - passedStations: true + stopovers: true }) yield testJourneysStationToStation({ @@ -150,7 +150,7 @@ test('journeys: via works – with detour', co(function* (t) { via: donauinsel, results: 1, departure: when, - passedStations: true + stopovers: true }) yield testJourneysWithDetour({ @@ -174,7 +174,7 @@ test('journeys: via works – without detour', co(function* (t) { via: museumsquartier, results: 1, departure: when, - passedStations: true + stopovers: true }) validate(t, journeys, 'journeys', 'journeys') diff --git a/test/vbb.js b/test/vbb.js index 8cebcb5f..f74eaf4a 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -113,7 +113,7 @@ test('journeys – Spichernstr. to Bismarckstr.', co(function* (t) { const journeys = yield client.journeys(spichernstr, bismarckstr, { results: 3, departure: when, - passedStations: true + stopovers: true }) yield testJourneysStationToStation({ @@ -252,7 +252,7 @@ test('journeys: via works – with detour', co(function* (t) { via: württembergallee, results: 1, departure: when, - passedStations: true + stopovers: true }) yield testJourneysWithDetour({ From 96f9f93957910887a7d7f0564088056a3f98907e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 22 Jun 2018 13:38:47 +0200 Subject: [PATCH 100/176] parseStopover: arrivalPlatform, departurePlatform --- parse/stopover.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parse/stopover.js b/parse/stopover.js index 52f69b17..0264c321 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -1,15 +1,15 @@ 'use strict' -// todo: arrivalDelay, departureDelay or only delay ? -// todo: arrivalPlatform, departurePlatform const createParseStopover = (profile, stations, lines, remarks, date) => { const parseStopover = (st) => { const res = { station: stations[parseInt(st.locX)] || null, arrival: null, arrivalDelay: null, + arrivalPlatform: st.aPlatfR || st.aPlatfS || null, departure: null, - departureDelay: null + departureDelay: null, + departurePlatform: st.dPlatfR || st.dPlatfS || null } // todo: DRY with parseDeparture From 3e672eeabd3a5b6b5d0ac9dadf7aee3b67f5abfa Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 25 Jun 2018 11:33:26 +0200 Subject: [PATCH 101/176] stopover.type, stopover.station -> stopover.stop :boom: To be compatible with `friendly-public-transport-format@1.1.1`. --- docs/journeys.md | 6 +++--- parse/stopover.js | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/journeys.md b/docs/journeys.md index 7a9e9131..81b74e24 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -160,7 +160,7 @@ The response may look like this: }, direction: 'S Potsdam Hauptbahnhof', stopovers: [ { - station: { + stop: { type: 'station', id: '900000003201', name: 'S+U Berlin Hauptbahnhof', @@ -171,7 +171,7 @@ The response may look like this: departure: null, cancelled: true }, { - station: { + stop: { type: 'station', id: '900000003102', name: 'S Bellevue', @@ -181,7 +181,7 @@ The response may look like this: arrival: '2017-12-17T19:09:00.000+01:00', departure: '2017-12-17T19:09:00.000+01:00' }, /* … */ { - station: { + stop: { type: 'station', id: '900000024101', name: 'S Charlottenburg', diff --git a/parse/stopover.js b/parse/stopover.js index 0264c321..763b27e8 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -3,7 +3,8 @@ const createParseStopover = (profile, stations, lines, remarks, date) => { const parseStopover = (st) => { const res = { - station: stations[parseInt(st.locX)] || null, + type: 'stopover', + stop: stations[parseInt(st.locX)] || null, arrival: null, arrivalDelay: null, arrivalPlatform: st.aPlatfR || st.aPlatfS || null, From ce880c06bd779f2276ba4a91463a16cf81528d04 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 22 Jun 2018 13:41:35 +0200 Subject: [PATCH 102/176] adapt tests & docs to 021ae45 & 3e672ee :memo: :white_check_mark: --- docs/journeys.md | 8 ++++++-- test/lib/validators.js | 13 ++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/journeys.md b/docs/journeys.md index 81b74e24..972fe55d 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -179,7 +179,9 @@ The response may look like this: products: { /* … */ } }, arrival: '2017-12-17T19:09:00.000+01:00', - departure: '2017-12-17T19:09:00.000+01:00' + arrivalPlatform: '1', + departure: '2017-12-17T19:09:00.000+01:00', + departurePlatform: '1' }, /* … */ { stop: { type: 'station', @@ -189,7 +191,9 @@ The response may look like this: products: { /* … */ } }, arrival: '2017-12-17T19:17:00.000+01:00', - departure: '2017-12-17T19:17:00.000+01:00' + arrivalPlatform: '5', + departure: '2017-12-17T19:17:00.000+01:00', + departurePlatform: '5' } ] } ] }, diff --git a/test/lib/validators.js b/test/lib/validators.js index 3fd6ec6b..b3c5bd5d 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -99,7 +99,18 @@ const createValidateStopover = (cfg) => { a.strictEqual(typeof s.departureDelay, 'number', msg) } - val.station(val, s.station, name + '.station') + if (is(s.arrivalPlatform)) { + const msg = name + '.arrivalPlatform must ' + a.strictEqual(typeof s.arrivalPlatform, 'string', msg + 'be a string') + a.ok(s.arrivalPlatform, msg + 'not be empty') + } + if (is(s.departureDelay)) { + const msg = name + '.departurePlatform must ' + a.strictEqual(typeof s.departurePlatform, 'string', msg + 'be a string') + a.ok(s.departurePlatform, msg + 'not be empty') + } + + val.station(val, s.stop, name + '.stop') } return validateStopover } From 8c726dce01cb120027fa799e4e5b926dc837e658 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 19 Jun 2018 09:11:26 +0200 Subject: [PATCH 103/176] changelog for 3.0.0 :memo: --- docs/changelog.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 3bc5021a..611bb464 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,23 @@ # Changelog +## `3.0.0` + +This version is not fully backwords-compatible. Check out [the migration guide](migration-to-3.md). + +- b7c1ee3 profiles: new products markup ([guide](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/writing-a-profile.md#3-products)) +- 40b559f `radar(n, w, s, e)` → `radar({n, w, s, e})` 💥 +- 005f3f8 remove `journey.departure`, `journey.arrival`, … 💥 +- 0ef0301 validate `opt.when` 💥 +- 431574b parse polylines using `profile.parsePolyLine` 💥 – [docs for the output format](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/journey-leg.md#polyline-option) +- a356a26 throw if 0 products enabled 💥 +- c82ad23 `journeys()`: `opt.when` → `opt.departure`/`opt.arrival` 💥 +- 21c273c `journeys()`/`journeyLeg()`: leg stopovers: parse & expose delays +- 665bed9 `location(id)` → `station(id)` 💥 +- 6611f26 `journeys()`/`journeyLeg()`: `leg.passed` → `leg.stopovers` 💥 +- ebe4fa6 `journeys()`/`journeyLeg()`: `opt.passedStations` → `opt.stopovers` 💥 +- 3e672ee `journeys()`/`journeyLeg()`: `stopover.station` → `stopover.stop` 💥 +- 021ae45: `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms + ## `2.7.0` - `journeys()`: `polylines` option From 108eda44505847766b770e475e993b49da9d9a65 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 25 Jun 2018 11:39:21 +0200 Subject: [PATCH 104/176] fix tests broken by 3e672ee :green_heart: --- test/lib/journeys-with-detour.js | 2 +- test/oebb.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lib/journeys-with-detour.js b/test/lib/journeys-with-detour.js index 0e454f3a..9b725a9b 100644 --- a/test/lib/journeys-with-detour.js +++ b/test/lib/journeys-with-detour.js @@ -12,7 +12,7 @@ const testJourneysWithDetour = co(function* (cfg) { const leg = journeys[0].legs.some((leg) => { return leg.stopovers && leg.stopovers.some((stopover) => { - return detourIds.includes(stopover.station.id) + return detourIds.includes(stopover.stop.id) }) }) t.ok(leg, detourIds.join('/') + ' is not being passed') diff --git a/test/oebb.js b/test/oebb.js index 62414cc7..a55a8a47 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -189,7 +189,7 @@ test('journeys: via works – without detour', co(function* (t) { const l2 = journeys[0].legs.some((leg) => { return leg.stopovers && leg.stopovers.some((stopover) => { - return stopover.station.id === museumsquartierPassed + return stopover.stop.id === museumsquartierPassed }) }) t.ok(l2, 'Museumsquartier is not being passed') From 3c00ed29d07bead69e661c4d66d30b0481d65776 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 25 Jun 2018 11:48:29 +0200 Subject: [PATCH 105/176] fix test broken by 3e672ee :green_heart: --- test/nahsh.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/nahsh.js b/test/nahsh.js index 157b6afe..a0fe19db 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -160,7 +160,7 @@ test('Husum to Lübeck Hbf with stopover at Kiel Hbf', co(function* (t) { const leg = journeys[0].legs.some((leg) => { return leg.stopovers && leg.stopovers.some((stopover) => { - return stopover.station.id === kielHbf + return stopover.stop.id === kielHbf }) }) t.ok(leg, 'Kiel Hbf is not being passed') From 6d9ffeda94b122e55e1c966edfced176f8a79c25 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 25 Jun 2018 13:12:14 +0200 Subject: [PATCH 106/176] readme: how to use with react-native :memo: Hopefully prevents situations like in #56 in the future. https://github.com/public-transport/hafas-client/issues/56#issuecomment-399915315 --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index 2f1c5ab6..8467b3b2 100644 --- a/readme.md +++ b/readme.md @@ -30,6 +30,10 @@ There's [a company called HaCon](http://hacon.de) that sells [a public transport npm install hafas-client ``` +### with [react-native](https://facebook.github.io/react-native/) + +`hafas-client` as well its dependencies use [Node-builtin modules](https://nodejs.org/dist/latest/docs/api/) and [Node globals](https://nodejs.org/api/globals.html). To be able to use it within react-native, follow [the instructions at `node-libs-react-native`](https://github.com/parshap/node-libs-react-native/blob/master/README.md#usage). + ## API From a8aa2652dfa1213c7d5708b91210ff5f6b42af57 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 25 Jun 2018 13:54:30 +0200 Subject: [PATCH 107/176] parseJourneyLeg: fix remarks indices :bug: --- parse/journey-leg.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index e0378adc..7173675d 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -5,12 +5,21 @@ const findRemark = require('./find-remark') const clone = obj => Object.assign({}, obj) +const locX = Symbol('locX') + const applyRemarks = (leg, hints, warnings, refs) => { for (let ref of refs) { const remark = findRemark(hints, warnings, ref) + if (!remark) continue + if ('number' === typeof ref.fLocX && 'number' === typeof ref.tLocX) { - for (let i = ref.fLocX; i <= ref.tLocX; i++) { - const stopover = leg.passed[i] + const fromI = leg.stopovers.findIndex(s => s[locX] === ref.fLocX) + const toI = leg.stopovers.findIndex(s => s[locX] === ref.tLocX) + if (fromI < 0 || toI < 0) continue + + for (let i = fromI; i <= toI; i++) { + const stopover = leg.stopovers[i] + if (!stopover) continue if (Array.isArray(stopover.remarks)) { stopover.remarks.push(remark) } else { @@ -79,15 +88,22 @@ const createParseJourneyLeg = (profile, stations, lines, hints, warnings, polyli if (pt.arr.aPlatfS) res.arrivalPlatform = pt.arr.aPlatfS if (parseStopovers && pt.jny.stopL) { - const parse = profile.parseStopover(profile, stations, lines, remarks, j.date) - const stopovers = pt.jny.stopL.map(parse) - // filter stations the train passes without stopping, as this doesn't comply with fptf (yet) - res.stopovers = stopovers.filter((x) => !x.passBy) + const parse = profile.parseStopover(profile, stations, lines, hints, warnings, j.date) + const stopL = pt.jny.stopL + res.stopovers = stopL.map(parse) // todo: is there a `pt.jny.remL`? if (Array.isArray(pt.jny.msgL)) { + for (let i = 0; i < stopL.length; i++) { + Object.defineProperty(res.stopovers[i], locX, { + value: stopL[i].locX + }) + } applyRemarks(res, hints, warnings, pt.jny.msgL) } + + // filter stations the train passes without stopping, as this doesn't comply with fptf (yet) + res.stopovers = res.stopovers.filter((x) => !x.passBy) } const freq = pt.jny.freq || {} From 6e60cc8bda062035ba9b326fffca276b92a1f3bd Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 25 Jun 2018 13:59:42 +0200 Subject: [PATCH 108/176] parse Komfort-Checkin & ICE Sprinter hints --- parse/hint.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/parse/hint.js b/parse/hint.js index bbc5a04e..40b69d4a 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -80,6 +80,16 @@ const hints = Object.assign(Object.create(null), { type: 'hint', code: 'elevator', text: 'elevator available' + }, + ck: { + type: 'hint', + code: 'komfort-checkin', + text: 'Komfort-Checkin available' + }, + it: { + type: 'hint', + code: 'ice-sprinter', + text: 'ICE Sprinter service' } }) From 9305a129a81db9b842b811c664e26548dcfee23a Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 25 Jun 2018 18:33:22 +0200 Subject: [PATCH 109/176] journeys: startWithWalking with default true :sparkles: --- docs/changelog.md | 1 + docs/journeys.md | 4 +++- index.js | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 611bb464..95b0e004 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -17,6 +17,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - ebe4fa6 `journeys()`/`journeyLeg()`: `opt.passedStations` → `opt.stopovers` 💥 - 3e672ee `journeys()`/`journeyLeg()`: `stopover.station` → `stopover.stop` 💥 - 021ae45: `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms +- 85e0bdf `journeys()`: `startWithWalking` option with default `true` ✨ ## `2.7.0` diff --git a/docs/journeys.md b/docs/journeys.md index 972fe55d..0e8a5bc8 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -64,7 +64,9 @@ With `opt`, you can override the default options, which look like this: regional: true }, tickets: false, // return tickets? only available with some profiles - polylines: false // return a shape for each leg? + polylines: false, // return a shape for each leg? + // Consider walking to nearby stations at the beginning of a journey? + startWithWalking: true } ``` diff --git a/index.js b/index.js index 91d644f4..ca0f35e7 100644 --- a/index.js +++ b/index.js @@ -97,7 +97,9 @@ const createClient = (profile, request = _request) => { accessibility: 'none', // 'none', 'partial' or 'complete' bike: false, // only bike-friendly journeys tickets: false, // return tickets? - polylines: false // return leg shapes? + polylines: false, // return leg shapes? + // Consider walking to nearby stations at the beginning of a journey? + startWithWalking: true }, opt) if (opt.via) opt.via = profile.formatLocation(profile, opt.via, 'opt.via') @@ -147,6 +149,7 @@ const createClient = (profile, request = _request) => { jnyFltrL: filters, getTariff: !!opt.tickets, outFrwd, + ushrp: !!opt.startWithWalking, // todo: what is req.gisFltrL? getPT: true, // todo: what is this? From 4c86b625c73b1f4cacf48dfedbcb4b2170b144dd Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 11:47:43 +0200 Subject: [PATCH 110/176] parse fn signatures: bugfixes :bug: --- parse/departure.js | 4 ++-- parse/movement.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/parse/departure.js b/parse/departure.js index 89682f70..0a21f893 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -6,14 +6,14 @@ // 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, opt, {stations, lines, remarks}) => { +const createParseDeparture = (profile, opt, {locations, lines, remarks}) => { const findRemark = rm => remarks[parseInt(rm.remX)] || null const parseDeparture = (d) => { const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) const res = { journeyId: d.jid, - station: stations[parseInt(d.stbStop.locX)] || null, + station: locations[parseInt(d.stbStop.locX)] || null, when: when.toISO(), direction: profile.parseStationName(d.dirTxt), line: lines[parseInt(d.prodX)] || null, diff --git a/parse/movement.js b/parse/movement.js index 5f942f86..8cf6f3bd 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,6 +1,6 @@ 'use strict' -const createParseMovement = (profile, data) => { +const createParseMovement = (profile, opt, data) => { const {locations, lines, remarks, polylines} = data // todo: what is m.dirGeo? maybe the speed? From b02f012b1884336370dddfb5418a4d0310f1bfa8 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 11:48:30 +0200 Subject: [PATCH 111/176] parse fn signatures: bugfixes :bug: --- index.js | 3 ++- p/nahsh/index.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index cc81854f..163dd31c 100644 --- a/index.js +++ b/index.js @@ -234,6 +234,7 @@ const createClient = (profile, request = _request) => { else if ('string' === typeof station) station = profile.formatStation(station) else throw new Error('station must be an object or a string.') + const opt = {} return request(profile, opt, { meth: 'LocDetails', req: { @@ -287,7 +288,7 @@ const createClient = (profile, request = _request) => { .then((d) => { if (!Array.isArray(d.locL)) return [] const parse = profile.parseNearby - return d.locL.map(loc => parse(profile, opt, loc)) + return d.locL.map(loc => parse(profile, opt, d, loc)) }) } diff --git a/p/nahsh/index.js b/p/nahsh/index.js index ca10f853..cab9da69 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -78,8 +78,8 @@ const createParseJourney = (profile, opt, data) => { return parseJourneyWithTickets } -const createParseMovement = (profile, data) => { - const _parseMovement = _createParseMovement(profile, data) +const createParseMovement = (profile, opt, data) => { + const _parseMovement = _createParseMovement(profile, opt, data) const parseMovement = (m) => { const res = _parseMovement(m) // filter out empty nextStops entries From 479bac428f73407c09bf6a3dd7f305e51acd8b64 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 12:25:30 +0200 Subject: [PATCH 112/176] parse fn signatures: bugfix :bug: --- p/oebb/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p/oebb/index.js b/p/oebb/index.js index 03143463..a78d88bc 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -44,8 +44,8 @@ const parseLocation = (profile, opt, data, l) => { return res } -const createParseMovement = (profile, data) => { - const _parseMovement = _createParseMovement(profile, data) +const createParseMovement = (profile, opt, data) => { + const _parseMovement = _createParseMovement(profile, opt, data) const parseMovement = (m) => { const res = _parseMovement(m) // filter out POIs From 00ea10d9c8445fc71d48e5f644fa10ee4f6b180f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 14:57:02 +0200 Subject: [PATCH 113/176] journey leg, departure, movement: journeyId -> tripId :boom: --- docs/changelog.md | 1 + docs/departures.md | 8 ++++---- parse/departure.js | 2 +- parse/hint.js | 2 +- parse/movement.js | 2 +- test/lib/validators.js | 4 ++-- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 95b0e004..a15661a2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 3e672ee `journeys()`/`journeyLeg()`: `stopover.station` → `stopover.stop` 💥 - 021ae45: `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms - 85e0bdf `journeys()`: `startWithWalking` option with default `true` ✨ +- 2e6aefe journey leg, departure, movement: `journeyId` -> `tripId` 💥 ## `2.7.0` diff --git a/docs/departures.md b/docs/departures.md index 25953889..7277255c 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -34,7 +34,7 @@ With `opt`, you can override the default options, which look like this: *Note:* As stated in the [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1), the `when` field includes the current delay. The `delay` field, if present, expresses how much the former differs from the schedule. -You may pass the `journeyId` field into [`journeyLeg(ref, lineName, [opt])`](journey-leg.md) to get details on the vehicle's journey. +You may pass the `tripId` field into [`journeyLeg(ref, lineName, [opt])`](journey-leg.md) to get details on the vehicle's journey. As an example, we're going to use the [VBB profile](../p/vbb): @@ -54,7 +54,7 @@ The response may look like this: ```js [ { - journeyId: '1|31431|28|86|17122017', + tripId: '1|31431|28|86|17122017', trip: 31431, station: { type: 'station', @@ -98,7 +98,7 @@ The response may look like this: }, direction: 'S Spandau' }, { - journeyId: '1|30977|8|86|17122017', + tripId: '1|30977|8|86|17122017', trip: 30977, station: { /* … */ }, when: null, @@ -121,7 +121,7 @@ The response may look like this: }, direction: 'S Westkreuz' }, { - journeyId: '1|28671|4|86|17122017', + tripId: '1|28671|4|86|17122017', trip: 28671, station: { type: 'station', diff --git a/parse/departure.js b/parse/departure.js index 26173e00..7835767a 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -14,7 +14,7 @@ const createParseDeparture = (profile, opt, data) => { const parseDeparture = (d) => { const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) const res = { - journeyId: d.jid, + tripId: d.jid, station: locations[parseInt(d.stbStop.locX)] || null, when: when.toISO(), direction: profile.parseStationName(d.dirTxt), diff --git a/parse/hint.js b/parse/hint.js index 40b69d4a..03f7d277 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -151,7 +151,7 @@ const parseHint = (profile, h) => { type: 'status', code: 'alternative-trip', text, - journeyId: h.jid + tripId: h.jid } } if (h.type === 'A') { diff --git a/parse/movement.js b/parse/movement.js index 49012026..81166d1d 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -14,7 +14,7 @@ const createParseMovement = (profile, opt, data) => { const res = { direction: profile.parseStationName(m.dirTxt), - journeyId: m.jid || null, + tripId: m.jid || null, trip: m.jid && +m.jid.split('|')[1] || null, // todo: this seems brittle line: lines[m.prodX] || null, location: m.pos ? { diff --git a/test/lib/validators.js b/test/lib/validators.js index b3c5bd5d..4a7c9bbb 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -231,8 +231,8 @@ const createValidateDeparture = (cfg) => { a.ok(isObj(dep), name + ' must be an object') // todo: let hafas-client add a .type field - a.strictEqual(typeof dep.journeyId, 'string', name + '.journeyId must be a string') - a.ok(dep.journeyId, name + '.journeyId must not be empty') + a.strictEqual(typeof dep.tripId, 'string', name + '.tripId must be a string') + a.ok(dep.tripId, name + '.tripId must not be empty') a.strictEqual(typeof dep.trip, 'number', name + '.trip must be a number') val.station(val, dep.station, name + '.station') From 1d5f4ff31c3e551ece36a78f76e419e10bd66883 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 15:49:50 +0200 Subject: [PATCH 114/176] parse arrivals --- index.js | 10 +++++- lib/default-profile.js | 2 ++ lib/validate-profile.js | 1 + parse/arrival-or-departure.js | 57 +++++++++++++++++++++++++++++++++++ parse/arrival.js | 10 ++++++ parse/departure.js | 51 ++----------------------------- 6 files changed, 82 insertions(+), 49 deletions(-) create mode 100644 parse/arrival-or-departure.js create mode 100644 parse/arrival.js diff --git a/index.js b/index.js index 4c80b975..c3a2d4fd 100644 --- a/index.js +++ b/index.js @@ -22,11 +22,15 @@ const createClient = (profile, request = _request) => { } validateProfile(profile) - const departures = (station, opt = {}) => { + const _stationBoard = (station, type, opt = {}) => { if (isObj(station)) station = profile.formatStation(station.id) else if ('string' === typeof station) station = profile.formatStation(station) else throw new Error('station must be an object or a string.') + if ('string' !== typeof type || !type) { + throw new Error('type must be a non-empty string.') + } + opt = Object.assign({ direction: null, // only show departures heading to this station duration: 10 // show departures for the next n minutes @@ -49,6 +53,10 @@ const createClient = (profile, request = _request) => { getPasslist: false } }) + } + + const departures = (station, opt = {}) => { + return _stationBoard(station, 'DEP', opt) .then((d) => { if (!Array.isArray(d.jnyL)) return [] // todo: throw err? const parse = profile.parseDeparture(profile, opt, { diff --git a/lib/default-profile.js b/lib/default-profile.js index c7dbd27b..64bc34dd 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -2,6 +2,7 @@ const parseDateTime = require('../parse/date-time') const parseDeparture = require('../parse/departure') +const parseArrival = require('../parse/arrival') const parseJourneyLeg = require('../parse/journey-leg') const parseJourney = require('../parse/journey') const parseLine = require('../parse/line') @@ -39,6 +40,7 @@ const defaultProfile = { parseDateTime, parseDeparture, + parseArrival, parseJourneyLeg, parseJourney, parseLine, diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 38ed6cd8..5f8dae6b 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -11,6 +11,7 @@ const types = { parseDateTime: 'function', parseDeparture: 'function', + parseArrival: 'function', parseJourneyLeg: 'function', parseJourney: 'function', parseLine: 'function', diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js new file mode 100644 index 00000000..fef3d84f --- /dev/null +++ b/parse/arrival-or-departure.js @@ -0,0 +1,57 @@ +'use strict' + +const findRemark = require('./find-remark') + +// todo: what is d.jny.dirFlg? +// todo: d.stbStop.dProgType/d.stbStop.aProgType + +const createParseArrOrDep = (profile, opt, data, prefix) => { + const {locations, lines, hints, warnings} = data + if (prefix !== 'a' && prefix !== 'd') throw new Error('invalid prefix') + + const parseArrOrDep = (d) => { + const t = d.stbStop[prefix + 'TimeR'] || d.stbStop[prefix + 'TimeS'] + const when = profile.parseDateTime(profile, d.date, t) + + const res = { + tripId: d.jid, + station: locations[parseInt(d.stbStop.locX)] || null, + when: when.toISO(), + direction: profile.parseStationName(d.dirTxt), + line: lines[parseInt(d.prodX)] || null, + remarks: (d.remL + ? d.remL.map(ref => findRemark(hints, warnings, ref)) + : [] + ), + // todo: res.trip from rawLine.prodCtx.num? + trip: +d.jid.split('|')[1] // todo: this seems brittle + } + + // todo: DRY with parseStopover + // todo: DRY with parseJourneyLeg + const tR = d.stbStop[prefix + 'TimeR'] + const tP = d.stbStop[prefix + 'TimeS'] + if (tR && tP) { + const realtime = profile.parseDateTime(profile, d.date, tR) + const planned = profile.parseDateTime(profile, d.date, tP) + res.delay = Math.round((realtime - planned) / 1000) + } else res.delay = null + + // todo: DRY with parseStopover + // todo: DRY with parseJourneyLeg + if (d.stbStop[prefix + 'Cncl']) { + res.cancelled = true + Object.defineProperty(res, 'canceled', {value: true}) + res.when = res.delay = null + + const when = profile.parseDateTime(profile, d.date, tP) + res.formerScheduledWhen = when.toISO() + } + + return res + } + + return parseArrOrDep +} + +module.exports = createParseArrOrDep diff --git a/parse/arrival.js b/parse/arrival.js new file mode 100644 index 00000000..e2c2b554 --- /dev/null +++ b/parse/arrival.js @@ -0,0 +1,10 @@ +'use strict' + +const createParseArrOrDep = require('./arrival-or-departure') + +const ARRIVAL = 'a' +const createParseArrival = (profile, opt, data) => { + return createParseArrOrDep(profile, opt, data, ARRIVAL) +} + +module.exports = createParseArrival diff --git a/parse/departure.js b/parse/departure.js index 7835767a..dc86f7a5 100644 --- a/parse/departure.js +++ b/parse/departure.js @@ -1,55 +1,10 @@ 'use strict' -const findRemark = require('./find-remark') - -// todos from public-transport/hafas-client#2 -// - stdStop.dPlatfS, stdStop.dPlatfR -// todo: what is d.jny.dirFlg? -// todo: d.stbStop.dProgType -// todo: d.freq, d.freq.jnyL, see https://github.com/public-transport/hafas-client/blob/9203ed1481f08baacca41ac5e3c19bf022f01b0b/parse.js#L115 +const createParseArrOrDep = require('./arrival-or-departure') +const DEPARTURE = 'd' const createParseDeparture = (profile, opt, data) => { - const {locations, lines, hints, warnings} = data - - const parseDeparture = (d) => { - const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR || d.stbStop.dTimeS) - const res = { - tripId: d.jid, - station: locations[parseInt(d.stbStop.locX)] || null, - when: when.toISO(), - direction: profile.parseStationName(d.dirTxt), - line: lines[parseInt(d.prodX)] || null, - remarks: (d.remL - ? d.remL.map(ref => findRemark(hints, warnings, ref)) - : [] - ), - trip: +d.jid.split('|')[1] // todo: this seems brittle - } - // todo: res.trip from rawLine.prodCtx.num? - - // todo: DRY with parseStopover - // todo: DRY with parseJourneyLeg - if (d.stbStop.dTimeR && d.stbStop.dTimeS) { - const realtime = profile.parseDateTime(profile, d.date, d.stbStop.dTimeR) - const planned = profile.parseDateTime(profile, d.date, d.stbStop.dTimeS) - res.delay = Math.round((realtime - planned) / 1000) - } else res.delay = null - - // todo: DRY with parseStopover - // todo: DRY with parseJourneyLeg - if (d.stbStop.aCncl || d.stbStop.dCncl) { - res.cancelled = true - Object.defineProperty(res, 'canceled', {value: true}) - res.when = res.delay = null - - const when = profile.parseDateTime(profile, d.date, d.stbStop.dTimeS) - res.formerScheduledWhen = when.toISO() - } - - return res - } - - return parseDeparture + return createParseArrOrDep(profile, opt, data, DEPARTURE) } module.exports = createParseDeparture From ac9819b1dd547b2249a76211c8182010dfa095c0 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 17:11:25 +0200 Subject: [PATCH 115/176] query arrivals :sparkles:, docs :memo: --- docs/arrivals.md | 3 +++ docs/readme.md | 1 + index.js | 21 ++++++++++++--------- p/db/example.js | 1 + p/insa/example.js | 1 + p/nahsh/example.js | 1 + p/oebb/example.js | 1 + p/vbb/example.js | 1 + 8 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 docs/arrivals.md diff --git a/docs/arrivals.md b/docs/arrivals.md new file mode 100644 index 00000000..683c47ed --- /dev/null +++ b/docs/arrivals.md @@ -0,0 +1,3 @@ +# `arrivals(station, [opt])` + +Just like [`departures(station, [opt])`](departures.md), except that it gives arrival times instead of departure times. diff --git a/docs/readme.md b/docs/readme.md index fa511bb8..cce0f86a 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -3,6 +3,7 @@ - [`journeys(from, to, [opt])`](journeys.md) – get journeys between locations - [`journeyLeg(ref, lineName, [opt])`](journey-leg.md) – get details for a leg of a journey - [`departures(station, [opt])`](departures.md) – query the next departures at a station +- [`arrivals(station, [opt])`](arrivals.md) – query the next arrivals at a station - [`locations(query, [opt])`](locations.md) – find stations, POIs and addresses - [`station(id)`](station.md) – get details about a station - [`nearby(location, [opt])`](nearby.md) – show stations & POIs around diff --git a/index.js b/index.js index c3a2d4fd..1d0cde19 100644 --- a/index.js +++ b/index.js @@ -22,7 +22,7 @@ const createClient = (profile, request = _request) => { } validateProfile(profile) - const _stationBoard = (station, type, opt = {}) => { + const _stationBoard = (station, type, parser, opt = {}) => { if (isObj(station)) station = profile.formatStation(station.id) else if ('string' === typeof station) station = profile.formatStation(station) else throw new Error('station must be an object or a string.') @@ -43,7 +43,7 @@ const createClient = (profile, request = _request) => { return request(profile, opt, { meth: 'StationBoard', req: { - type: 'DEP', + type, date: profile.formatDate(profile, opt.when), time: profile.formatTime(profile, opt.when), stbLoc: station, @@ -53,13 +53,9 @@ const createClient = (profile, request = _request) => { getPasslist: false } }) - } - - const departures = (station, opt = {}) => { - return _stationBoard(station, 'DEP', opt) .then((d) => { - if (!Array.isArray(d.jnyL)) return [] // todo: throw err? - const parse = profile.parseDeparture(profile, opt, { + if (!Array.isArray(d.jnyL)) return [] + const parse = parser(profile, opt, { locations: d.locations, lines: d.lines, hints: d.hints, @@ -70,6 +66,13 @@ const createClient = (profile, request = _request) => { }) } + const departures = (station, opt = {}) => { + return _stationBoard(station, 'DEP', profile.parseDeparture, opt) + } + const arrivals = (station, opt = {}) => { + return _stationBoard(station, 'ARR', profile.parseArrival, opt) + } + const journeys = (from, to, opt = {}) => { from = profile.formatLocation(profile, from, 'from') to = profile.formatLocation(profile, to, 'to') @@ -401,7 +404,7 @@ const createClient = (profile, request = _request) => { }) } - const client = {departures, journeys, locations, station, nearby} + const client = {departures, arrivals, journeys, locations, station, nearby} if (profile.journeyLeg) client.journeyLeg = journeyLeg if (profile.radar) client.radar = radar Object.defineProperty(client, 'profile', {value: profile}) diff --git a/p/db/example.js b/p/db/example.js index f9fe23e6..2f99d919 100644 --- a/p/db/example.js +++ b/p/db/example.js @@ -8,6 +8,7 @@ const client = createClient(dbProfile) // Berlin Jungfernheide to München Hbf client.journeys('8011167', '8000261', {results: 1, tickets: true}) // client.departures('8011167', {duration: 1}) +// client.arrivals('8011167', {duration: 10}) // client.locations('Berlin Jungfernheide') // client.locations('Atze Musiktheater', {poi: true, addressses: false, fuzzy: false}) // client.station('8000309') // Regensburg Hbf diff --git a/p/insa/example.js b/p/insa/example.js index 2260f34b..190f8fa9 100644 --- a/p/insa/example.js +++ b/p/insa/example.js @@ -8,6 +8,7 @@ const client = createClient(insaProfile) // from Magdeburg-Neustadt to Magdeburg-Buckau client.journeys('008010226', '008013456', {results: 1}) // client.departures('008010226', { duration: 5 }) +// client.arrivals('8010226', {duration: 10}) // client.locations('Magdeburg Hbf', {results: 2}) // client.locations('Kunstmuseum Kloster Unser Lieben Frauen Magdeburg', {results: 2}) // client.station('008010226') // Magdeburg-Neustadt diff --git a/p/nahsh/example.js b/p/nahsh/example.js index f3ec6237..986e01be 100644 --- a/p/nahsh/example.js +++ b/p/nahsh/example.js @@ -8,6 +8,7 @@ const client = createClient(nahshProfile) // Flensburg Hbf to Kiel Hbf client.journeys('8000103', '8000199', {results: 10, tickets: true}) // client.departures('8000199', {duration: 10}) +// client.arrivals('8000199', {duration: 5}) // client.journeyLeg('1|30161|5|100|14032018', 'Bus 52') // client.locations('Schleswig', {results: 1}) // client.station('706990') // Kiel Holunderbusch diff --git a/p/oebb/example.js b/p/oebb/example.js index 4f780f66..208bc367 100644 --- a/p/oebb/example.js +++ b/p/oebb/example.js @@ -8,6 +8,7 @@ const client = createClient(oebbProfile) // Wien Westbahnhof to Salzburg Hbf client.journeys('1291501', '8100002', {results: 1}) // client.departures('8100002', {duration: 1}) +// client.arrivals('8100002', {duration: 10}) // client.locations('Salzburg', {results: 2}) // client.station('8100173') // Graz Hbf // client.nearby(47.812851, 13.045604, {distance: 60}) diff --git a/p/vbb/example.js b/p/vbb/example.js index 1234692a..e2e4027a 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -8,6 +8,7 @@ const client = createClient(vbbProfile) // Hauptbahnhof to Charlottenburg client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // client.departures('900000013102', {duration: 1}) +// client.arrivals('900000013102', {duration: 10}) // client.locations('Alexanderplatz', {results: 2}) // client.station('900000042101') // Spichernstr // client.nearby(52.5137344, 13.4744798, {distance: 60}) From 3ade1af7a2af3126f52b64aecd71218b471dddeb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 17:30:46 +0200 Subject: [PATCH 116/176] basic tests for arrivals :white_check_mark: --- test/db.js | 15 +++++++++++++++ test/insa.js | 15 +++++++++++++++ test/lib/validators.js | 17 +++++++++++++---- test/nahsh.js | 17 +++++++++++++++++ test/oebb.js | 2 ++ test/vbb.js | 15 +++++++++++++++ 6 files changed, 77 insertions(+), 4 deletions(-) diff --git a/test/db.js b/test/db.js index de2af4bf..9bcdc7a6 100644 --- a/test/db.js +++ b/test/db.js @@ -22,6 +22,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -240,6 +241,20 @@ test('departures with station object', co(function* (t) { t.end() })) +test('arrivals at Berlin Schwedter Str.', co(function* (t) { + const arrivals = yield client.arrivals(blnSchwedterStr, { + duration: 5, when + }) + + yield testArrivals({ + test: t, + arrivals, + validate, + id: blnSchwedterStr + }) + t.end() +})) + test('nearby Berlin Jungfernheide', co(function* (t) { const nearby = yield client.nearby({ type: 'location', diff --git a/test/insa.js b/test/insa.js index 7f622154..365740a1 100644 --- a/test/insa.js +++ b/test/insa.js @@ -16,6 +16,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') const isObj = o => o !== null && 'object' === typeof o && !Array.isArray(o) @@ -195,6 +196,20 @@ test('departures with station object', co(function* (t) { t.end() })) +test('arrivals at Magdeburg Leiterstr.', co(function*(t) { + const arrivals = yield client.arrivals(leiterstr, { + duration: 5, when + }) + + yield testArrivals({ + test: t, + arrivals, + validate, + id: leiterstr + }) + t.end() +})) + // todo: nearby test('locations named Magdeburg', co(function*(t) { diff --git a/test/lib/validators.js b/test/lib/validators.js index 4a7c9bbb..f879c8d5 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -226,8 +226,8 @@ const validateJourneys = (val, js, name = 'journeys') => { } } -const createValidateDeparture = (cfg) => { - const validateDeparture = (val, dep, name = 'departure') => { +const createValidateArrivalOrDeparture = (cfg) => { + const validateArrivalOrDeparture = (val, dep, name = 'arrOrDep') => { a.ok(isObj(dep), name + ' must be an object') // todo: let hafas-client add a .type field @@ -247,9 +247,16 @@ const createValidateDeparture = (cfg) => { a.strictEqual(typeof dep.direction, 'string', name + '.direction must be a string') a.ok(dep.direction, name + '.direction must not be empty') } - return validateDeparture + return validateArrivalOrDeparture } +const validateArrivals = (val, deps, name = 'arrivals') => { + a.ok(Array.isArray(deps), name + ' must be an array') + a.ok(deps.length > 0, name + ' must not be empty') + for (let i = 0; i < deps.length; i++) { + val.arrival(val, deps[i], name + `[${i}]`) + } +} const validateDepartures = (val, deps, name = 'departures') => { a.ok(Array.isArray(deps), name + ' must be an array') a.ok(deps.length > 0, name + ' must not be empty') @@ -314,8 +321,10 @@ module.exports = { journeyLeg: createValidateJourneyLeg, journey: () => validateJourney, journeys: () => validateJourneys, - departure: createValidateDeparture, + arrival: createValidateArrivalOrDeparture, + departure: createValidateArrivalOrDeparture, departures: () => validateDepartures, + arrivals: () => validateArrivals, movement: () => validateMovement, movements: () => validateMovements } diff --git a/test/nahsh.js b/test/nahsh.js index a0fe19db..a502908b 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -20,6 +20,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testArrivals = require('./lib/arrivals') const when = createWhen('Europe/Berlin', 'de-DE') @@ -229,6 +230,22 @@ test('departures with station object', co(function* (t) { t.end() })) +test('arrivals at Kiel Räucherei', co(function* (t) { + const kielRaeucherei = '3440091' + + const arrivals = yield client.arrivals(kielRaeucherei, { + duration: 30, when + }) + + yield testArrivals({ + test: t, + arrivals, + validate, + id: kielRaeucherei + }) + t.end() +})) + test('nearby Kiel Hbf', co(function* (t) { const kielHbfPosition = { type: 'location', diff --git a/test/oebb.js b/test/oebb.js index a55a8a47..d97581d3 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -265,6 +265,8 @@ test('departures with station object', co(function* (t) { t.end() })) +// todo: arrivals + test('nearby Salzburg Hbf', co(function* (t) { const nearby = yield client.nearby({ type: 'location', diff --git a/test/vbb.js b/test/vbb.js index f74eaf4a..36601c6d 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -26,6 +26,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') const when = createWhen('Europe/Berlin', 'de-DE') @@ -303,6 +304,20 @@ test('departures at 7-digit station', co(function* (t) { t.end() })) +test('arrivals', co(function* (t) { + const arrivals = yield client.arrivals(spichernstr, { + duration: 5, when + }) + + yield testArrivals({ + test: t, + arrivals, + validate, + id: spichernstr + }) + t.end() +})) + test('nearby', co(function* (t) { const berlinerStr = '900000044201' const landhausstr = '900000043252' From f6c824eecb459181ea90ddf41bf1a1e8b64539ec Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 26 Jun 2018 18:10:41 +0200 Subject: [PATCH 117/176] fix tests :green_heart: --- test/lib/arrivals.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/lib/arrivals.js diff --git a/test/lib/arrivals.js b/test/lib/arrivals.js new file mode 100644 index 00000000..9483514a --- /dev/null +++ b/test/lib/arrivals.js @@ -0,0 +1,21 @@ +'use strict' + +const co = require('./co') + +const testArrivals = co(function* (cfg) { + const {test: t, arrivals: arrs, validate, id} = cfg + + validate(t, arrs, 'arrivals', 'arrivals') + t.ok(arrs.length > 0, 'must be >0 arrivals') + for (let i = 0; i < arrs.length; i++) { + const dep = arrs[i] + const name = `arrs[${i}]` + + t.equal(dep.station.id, id, name + '.station.id is invalid') + } + + // todo: move into arrivals validator + t.deepEqual(arrs, arrs.sort((a, b) => t.when > b.when)) +}) + +module.exports = testArrivals From eeb9ec25358ca6d0fa298ea599d95d31620927ac Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 27 Jun 2018 11:13:55 +0200 Subject: [PATCH 118/176] add todos, migrating to 3.x guide, related repos :memo: --- docs/changelog.md | 38 +++++++++++++++++++------------- docs/migrating-to-3.md | 49 ++++++++++++++++++++++++++++++++++++++++++ index.js | 2 +- parse/journey-leg.js | 1 + readme.md | 4 ++++ 5 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 docs/migrating-to-3.md diff --git a/docs/changelog.md b/docs/changelog.md index a15661a2..19bf6d8a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,23 +2,31 @@ ## `3.0.0` -This version is not fully backwords-compatible. Check out [the migration guide](migration-to-3.md). +This version is not fully backwords-compatible. Check out [the migration guide](migrating-to-3.md). + +### new features ✨ + +- 0db84ce #61 parse remarks for stopovers and journey legs +- ac9819b `arrivals()` method – [docs](arrivals.md) +- 21c273c `journeys()`/`journeyLeg()`: leg stopovers: parse & expose delays +- 021ae45 `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms +- 85e0bdf `journeys()`: `startWithWalking` option with default `true` + +### breaking changes 💥 - b7c1ee3 profiles: new products markup ([guide](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/writing-a-profile.md#3-products)) -- 40b559f `radar(n, w, s, e)` → `radar({n, w, s, e})` 💥 -- 005f3f8 remove `journey.departure`, `journey.arrival`, … 💥 -- 0ef0301 validate `opt.when` 💥 -- 431574b parse polylines using `profile.parsePolyLine` 💥 – [docs for the output format](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/journey-leg.md#polyline-option) -- a356a26 throw if 0 products enabled 💥 -- c82ad23 `journeys()`: `opt.when` → `opt.departure`/`opt.arrival` 💥 -- 21c273c `journeys()`/`journeyLeg()`: leg stopovers: parse & expose delays -- 665bed9 `location(id)` → `station(id)` 💥 -- 6611f26 `journeys()`/`journeyLeg()`: `leg.passed` → `leg.stopovers` 💥 -- ebe4fa6 `journeys()`/`journeyLeg()`: `opt.passedStations` → `opt.stopovers` 💥 -- 3e672ee `journeys()`/`journeyLeg()`: `stopover.station` → `stopover.stop` 💥 -- 021ae45: `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms -- 85e0bdf `journeys()`: `startWithWalking` option with default `true` ✨ -- 2e6aefe journey leg, departure, movement: `journeyId` -> `tripId` 💥 +- 40b559f change `radar(n, w, s, e)` signature to `radar({north, west, south, east})` +- 005f3f8 remove `journey.departure`, `journey.arrival`, … +- 0ef0301 validate `opt.when` +- 431574b parse polylines using `profile.parsePolyLine` – [docs for the output format](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/journey-leg.md#polyline-option) +- a356a26 throw if 0 products enabled +- c82ad23 `journeys()`: `opt.when` → `opt.departure`/`opt.arrival` +- 665bed9 rename `location(id)` to `station(id)` +- 6611f26 `journeys()`/`journeyLeg()`: `leg.passed` → `leg.stopovers` +- ebe4fa6 `journeys()`/`journeyLeg()`: `opt.passedStations` → `opt.stopovers` +- 3e672ee `journeys()`/`journeyLeg()`: `stopover.station` → `stopover.stop` +- 2e6aefe journey leg, departure, movement: `journeyId` -> `tripId` +- 8881d8a & b6fbaa5: change parsers signature to `parse…(profile, opt, data)` ## `2.7.0` diff --git a/docs/migrating-to-3.md b/docs/migrating-to-3.md new file mode 100644 index 00000000..668e67a5 --- /dev/null +++ b/docs/migrating-to-3.md @@ -0,0 +1,49 @@ +# Migrating to `hafas-client@3` + +## If you use the `journeys()` or `journeyLeg()` methods… + +- …instead of `journey.departure`, use `journey.legs[0].departure`. 005f3f8 +- …instead of `journey.arrival`, use `journey.legs[last].arrival`. 005f3f8 +- …rename `opt.passedStations` to `opt.stopovers`. ebe4fa6 +- …rename `leg.journeyId` to `leg.tripId`. 2e6aefe +- …rename `leg.passed` to `leg.stopovers`. 6611f26 +- …rename `leg.stopovers[].station` to `leg.stopovers[].stop`. 3e672ee + +## If you use the `journeys()` method and `opt.when`… + +…use `opt.departure` instead. Use `opt.arrival` to get journeys arriving before the specified date+time. This replaces the `opt.when` & `opt.whenRepresents` options from `hafas-client@2`. c82ad23 + +## If you use the `journeys()` and `opt.polylines` or `journeyLeg()` and `opt.polyline`… + +…`leg.polyline` will be [parsed for you now](https://github.com/public-transport/hafas-client/blob/f6c824eecb459181ea90ddf41bf1a1e8b64539ec/docs/journey-leg.md#polyline-option). + +## If you use the `departures()` method… + +…rename `departure.journeyId` to `departure.tripId`. 2e6aefe + +## If you use the `location()` method… + +…change the `location(id)` call to `station(id)`. 665bed9 + +## If you use the `radar()` method… + +- …change the `radar(north, west, south, east)` call to `radar({north, west, south, east})`. 40b559f +- …rename `movement.journeyId` to `movement.tripId`. 2e6aefe + +## If you use `hafas-client` with a custom profile… + +…write your profile in [the new format](writing-a-profile.md). Then, you can pass it into `hafas-client` just like before. #32/b7c1ee3 + +## If you use `hafas-client` with custom parse functions… + +…change the following parsers to the `parse…(profile, opt, data)` signature. 8881d8a/b6fbaa5 + +- `parseDeparture` +- `parseJourney` +- `parseJourneyLeg` +- `parseLine` +- `parseMovement` +- `parseLocation` +- `parseNearby` +- `parsePolyline` +- `parseStopover` diff --git a/index.js b/index.js index 1d0cde19..a83218d9 100644 --- a/index.js +++ b/index.js @@ -50,7 +50,7 @@ const createClient = (profile, request = _request) => { dirLoc: dir, jnyFltrL: [products], dur: opt.duration, - getPasslist: false + getPasslist: false // todo: what is this? } }) .then((d) => { diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 58d51096..c92d8204 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -37,6 +37,7 @@ const applyRemarks = (leg, hints, warnings, refs) => { const createParseJourneyLeg = (profile, opt, data) => { const {locations, lines, hints, warnings, polylines} = data // todo: pt.status + // todo: pt.status, pt.isPartCncl // todo: pt.sDays // todo: pt.dep.dProgType, pt.arr.dProgType // todo: what is pt.jny.dirFlg? diff --git a/readme.md b/readme.md index 8467b3b2..c7129099 100644 --- a/readme.md +++ b/readme.md @@ -177,7 +177,11 @@ The returned [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript - [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas#vbb-hafas) – JavaScript client for Berlin & Brandenburg public transport HAFAS API. - [`hafas-departures-in-direction`](https://github.com/derhuerst/hafas-departures-in-direction#hafas-departures-in-direction) – Pass in a HAFAS client, get departures in a certain direction. - [`hafas-collect-departures-at`](https://github.com/derhuerst/hafas-collect-departures-at#hafas-collect-departures-at) – Utility to collect departures, using any HAFAS client. +- [`hafas-monitor-departures`](https://github.com/derhuerst/hafas-monitor-departures#hafas-monitor-departures) – Pass in a HAFAS client, fetch all departures at any set of stations. +- [`hafas-departures-in-direction`](https://github.com/derhuerst/hafas-departures-in-direction#hafas-departures-in-direction) – Pass in a HAFAS client, get departures in a certain direction. - [`hafas-discover-stations`](https://github.com/derhuerst/hafas-discover-stations#hafas-discover-stations) – Pass in a HAFAS client, discover stations by querying departures. +- [`hafas-record-delays`](https://github.com/derhuerst/hafas-record-delays#hafas-record-delays) – Record delays from hafas-monitor-departures into a LevelDB. +- [`hafas-estimate-station-weight`](https://github.com/derhuerst/hafas-estimate-station-weight#hafas-estimate-station-weight) – Pass in a HAFAS client, estimate the importance of a station. - [`hafas-rest-api`](https://github.com/derhuerst/hafas-rest-api#hafas-rest-api) – Expose a HAFAS client via an HTTP REST API. - [List of european long-distance transport operators, available API endpoints, GTFS feeds and client modules.](https://github.com/public-transport/european-transport-operators) - [Collection of european transport JavaScript modules.](https://github.com/public-transport/european-transport-modules) From d3815f80d730436109568a0e129fee45005caea7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 27 Jun 2018 17:17:04 +0200 Subject: [PATCH 119/176] arrivals/departures: parse & expose platforms :sparkles: --- docs/changelog.md | 1 + parse/arrival-or-departure.js | 7 +++++++ test/lib/validators.js | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 19bf6d8a..2f72255e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -10,6 +10,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - ac9819b `arrivals()` method – [docs](arrivals.md) - 21c273c `journeys()`/`journeyLeg()`: leg stopovers: parse & expose delays - 021ae45 `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms +- 84bce0c `arrivals()`/`departures()`: parse & expose platforms - 85e0bdf `journeys()`: `startWithWalking` option with default `true` ### breaking changes 💥 diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js index fef3d84f..e4cc0adb 100644 --- a/parse/arrival-or-departure.js +++ b/parse/arrival-or-departure.js @@ -37,6 +37,13 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { res.delay = Math.round((realtime - planned) / 1000) } else res.delay = null + // todo: DRY with parseStopover + // todo: DRY with parseJourneyLeg + const pR = d.stbStop[prefix + 'PlatfR'] + const pP = d.stbStop[prefix + 'PlatfS'] + res.platform = pR || pP || null + // todo: `formerScheduledPlatform` + // todo: DRY with parseStopover // todo: DRY with parseJourneyLeg if (d.stbStop[prefix + 'Cncl']) { diff --git a/test/lib/validators.js b/test/lib/validators.js index f879c8d5..237674b4 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -243,6 +243,12 @@ const createValidateArrivalOrDeparture = (cfg) => { a.strictEqual(typeof dep.delay, 'number', msg) } + if (dep.platform !== null) { + const msg = name + '.platform must ' + a.strictEqual(typeof dep.platform, 'string', msg + 'be a string') + a.ok(dep.platform, name + 'not be empty') + } + val.line(val, dep.line, name + '.line') a.strictEqual(typeof dep.direction, 'string', name + '.direction must be a string') a.ok(dep.direction, name + '.direction must not be empty') From 471f075deaa06e1340a56701172d531995d953d2 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 28 Jun 2018 12:08:54 +0200 Subject: [PATCH 120/176] fix nearby examples :memo: --- p/db/example.js | 6 +++++- p/nahsh/example.js | 6 +++++- p/oebb/example.js | 6 +++++- p/vbb/example.js | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/p/db/example.js b/p/db/example.js index 2f99d919..b456e927 100644 --- a/p/db/example.js +++ b/p/db/example.js @@ -12,7 +12,11 @@ client.journeys('8011167', '8000261', {results: 1, tickets: true}) // client.locations('Berlin Jungfernheide') // client.locations('Atze Musiktheater', {poi: true, addressses: false, fuzzy: false}) // client.station('8000309') // Regensburg Hbf -// client.nearby(52.4751309, 13.3656537, {results: 1}) +// client.nearby({ +// type: 'location', +// latitude: 52.4751309, +// longitude: 13.3656537 +// }, {results: 1}) .then((data) => { console.log(require('util').inspect(data, {depth: null})) diff --git a/p/nahsh/example.js b/p/nahsh/example.js index 986e01be..65c2781b 100644 --- a/p/nahsh/example.js +++ b/p/nahsh/example.js @@ -12,7 +12,11 @@ client.journeys('8000103', '8000199', {results: 10, tickets: true}) // client.journeyLeg('1|30161|5|100|14032018', 'Bus 52') // client.locations('Schleswig', {results: 1}) // client.station('706990') // Kiel Holunderbusch -// client.nearby({type: 'location', latitude: 54.295691, longitude: 10.116424}, {distance: 60}) +// client.nearby({ +// type: 'location', +// latitude: 54.295691, +// longitude: 10.116424 +// }, {distance: 60}) // client.radar(54.4, 10.0, 54.2, 10.2, {results: 10}) .then((data) => { diff --git a/p/oebb/example.js b/p/oebb/example.js index 208bc367..4c533023 100644 --- a/p/oebb/example.js +++ b/p/oebb/example.js @@ -11,7 +11,11 @@ client.journeys('1291501', '8100002', {results: 1}) // client.arrivals('8100002', {duration: 10}) // client.locations('Salzburg', {results: 2}) // client.station('8100173') // Graz Hbf -// client.nearby(47.812851, 13.045604, {distance: 60}) +// client.nearby({ +// type: 'location', +// latitude: 47.812851, +// longitude: 13.045604 +// }, {distance: 60}) // client.radar({ // north: 47.827203, // west: 13.001261, diff --git a/p/vbb/example.js b/p/vbb/example.js index e2e4027a..af234c8a 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -11,7 +11,11 @@ client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // client.arrivals('900000013102', {duration: 10}) // client.locations('Alexanderplatz', {results: 2}) // client.station('900000042101') // Spichernstr -// client.nearby(52.5137344, 13.4744798, {distance: 60}) +// client.nearby({ +// type: 'location', +// latitude: 52.5137344, +// longitude: 13.4744798 +// }, {distance: 60}) // client.radar({ // north: 52.52411, // west: 13.41002, From 58f183506ecd47adaed0869efb2eacef12578991 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 28 Jun 2018 13:00:33 +0200 Subject: [PATCH 121/176] option to parse station lines, default off :boom:, adapt docs :memo: --- docs/changelog.md | 1 + docs/departures.md | 3 ++- docs/locations.md | 1 + docs/migrating-to-3.md | 4 ++++ docs/nearby.md | 1 + docs/readme.md | 2 +- docs/station.md | 9 ++++++++- index.js | 13 +++++++++---- p/db/example.js | 2 +- p/insa/example.js | 2 +- p/nahsh/example.js | 2 +- p/oebb/example.js | 2 +- p/vbb/example.js | 4 ++-- parse/location.js | 6 +++++- 14 files changed, 38 insertions(+), 14 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2f72255e..49504595 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -28,6 +28,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 3e672ee `journeys()`/`journeyLeg()`: `stopover.station` → `stopover.stop` - 2e6aefe journey leg, departure, movement: `journeyId` -> `tripId` - 8881d8a & b6fbaa5: change parsers signature to `parse…(profile, opt, data)` +- cabe5fa: option to parse & expose `station.lines`, default off ## `2.7.0` diff --git a/docs/departures.md b/docs/departures.md index 7277255c..d331da07 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -26,7 +26,8 @@ With `opt`, you can override the default options, which look like this: // todo: products when: new Date(), direction: null, // only show departures heading to this station - duration: 10 // show departures for the next n minutes + duration: 10, // show departures for the next n minutes + stationLines: false // parse & expose lines of the station? } ``` diff --git a/docs/locations.md b/docs/locations.md index 8b9d17ab..8337823d 100644 --- a/docs/locations.md +++ b/docs/locations.md @@ -11,6 +11,7 @@ With `opt`, you can override the default options, which look like this: , stations: true , addresses: true , poi: true // points of interest + , stationLines: false // parse & expose lines of the station? } ``` diff --git a/docs/migrating-to-3.md b/docs/migrating-to-3.md index 668e67a5..4bda6e90 100644 --- a/docs/migrating-to-3.md +++ b/docs/migrating-to-3.md @@ -47,3 +47,7 @@ - `parseNearby` - `parsePolyline` - `parseStopover` + +## If you use `station.lines` array anywhere… + +…add the `stationLines: true` option to the method call, e.g. `hafas.departures('123', {stationLines: true}). cabe5fa diff --git a/docs/nearby.md b/docs/nearby.md index 397162f0..c75d0f03 100644 --- a/docs/nearby.md +++ b/docs/nearby.md @@ -11,6 +11,7 @@ With `opt`, you can override the default options, which look like this: distance: null, // maximum walking distance in meters poi: false, // return points of interest? stations: true, // return stations? + stationLines: false // parse & expose lines of the station? } ``` diff --git a/docs/readme.md b/docs/readme.md index cce0f86a..61699d2d 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -5,7 +5,7 @@ - [`departures(station, [opt])`](departures.md) – query the next departures at a station - [`arrivals(station, [opt])`](arrivals.md) – query the next arrivals at a station - [`locations(query, [opt])`](locations.md) – find stations, POIs and addresses -- [`station(id)`](station.md) – get details about a station +- [`station(id, [opt])`](station.md) – get details about a station - [`nearby(location, [opt])`](nearby.md) – show stations & POIs around - [`radar(north, west, south, east, [opt])`](radar.md) – find all vehicles currently in a certain area diff --git a/docs/station.md b/docs/station.md index 1a0dd31b..db780f5b 100644 --- a/docs/station.md +++ b/docs/station.md @@ -1,4 +1,4 @@ -# `station(id)` +# `station(id, [opt])` `id` must be in one of these formats: @@ -19,6 +19,13 @@ } ``` +With `opt`, you can override the default options, which look like this: + +```js +{ + stationLines: false // parse & expose lines of the station? +} + ## Response As an example, we're going to use the [VBB profile](../p/vbb): diff --git a/index.js b/index.js index a83218d9..dfe795e2 100644 --- a/index.js +++ b/index.js @@ -33,7 +33,8 @@ const createClient = (profile, request = _request) => { opt = Object.assign({ direction: null, // only show departures heading to this station - duration: 10 // show departures for the next n minutes + duration: 10, // show departures for the next n minutes + stationLines: false // parse & expose lines of the station? }, opt) opt.when = new Date(opt.when || Date.now()) if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') @@ -222,7 +223,8 @@ const createClient = (profile, request = _request) => { results: 10, // how many search results? stations: true, addresses: true, - poi: true // points of interest + poi: true, // points of interest + stationLines: false // parse & expose lines of the station? }, opt) const f = profile.formatLocationFilter(opt.stations, opt.addresses, opt.poi) @@ -245,12 +247,14 @@ const createClient = (profile, request = _request) => { }) } - const station = (station) => { + const station = (station, opt = {}) => { if ('object' === typeof station) station = profile.formatStation(station.id) else if ('string' === typeof station) station = profile.formatStation(station) else throw new Error('station must be an object or a string.') - const opt = {} + opt = Object.assign({ + stationLines: false // parse & expose lines of the station? + }, opt) return request(profile, opt, { meth: 'LocDetails', req: { @@ -282,6 +286,7 @@ const createClient = (profile, request = _request) => { distance: null, // maximum walking distance in meters poi: false, // return points of interest? stations: true, // return stations? + stationLines: false // parse & expose lines of the station? }, opt) return request(profile, opt, { diff --git a/p/db/example.js b/p/db/example.js index b456e927..9d7ef481 100644 --- a/p/db/example.js +++ b/p/db/example.js @@ -8,7 +8,7 @@ const client = createClient(dbProfile) // Berlin Jungfernheide to München Hbf client.journeys('8011167', '8000261', {results: 1, tickets: true}) // client.departures('8011167', {duration: 1}) -// client.arrivals('8011167', {duration: 10}) +// client.arrivals('8011167', {duration: 10, stationLines: true}) // client.locations('Berlin Jungfernheide') // client.locations('Atze Musiktheater', {poi: true, addressses: false, fuzzy: false}) // client.station('8000309') // Regensburg Hbf diff --git a/p/insa/example.js b/p/insa/example.js index 190f8fa9..9397c65c 100644 --- a/p/insa/example.js +++ b/p/insa/example.js @@ -8,7 +8,7 @@ const client = createClient(insaProfile) // from Magdeburg-Neustadt to Magdeburg-Buckau client.journeys('008010226', '008013456', {results: 1}) // client.departures('008010226', { duration: 5 }) -// client.arrivals('8010226', {duration: 10}) +// client.arrivals('8010226', {duration: 10, stationLines: true}) // client.locations('Magdeburg Hbf', {results: 2}) // client.locations('Kunstmuseum Kloster Unser Lieben Frauen Magdeburg', {results: 2}) // client.station('008010226') // Magdeburg-Neustadt diff --git a/p/nahsh/example.js b/p/nahsh/example.js index 65c2781b..62ff2f3c 100644 --- a/p/nahsh/example.js +++ b/p/nahsh/example.js @@ -8,7 +8,7 @@ const client = createClient(nahshProfile) // Flensburg Hbf to Kiel Hbf client.journeys('8000103', '8000199', {results: 10, tickets: true}) // client.departures('8000199', {duration: 10}) -// client.arrivals('8000199', {duration: 5}) +// client.arrivals('8000199', {duration: 5, stationLines: true}) // client.journeyLeg('1|30161|5|100|14032018', 'Bus 52') // client.locations('Schleswig', {results: 1}) // client.station('706990') // Kiel Holunderbusch diff --git a/p/oebb/example.js b/p/oebb/example.js index 4c533023..ff6d5b40 100644 --- a/p/oebb/example.js +++ b/p/oebb/example.js @@ -8,7 +8,7 @@ const client = createClient(oebbProfile) // Wien Westbahnhof to Salzburg Hbf client.journeys('1291501', '8100002', {results: 1}) // client.departures('8100002', {duration: 1}) -// client.arrivals('8100002', {duration: 10}) +// client.arrivals('8100002', {duration: 10, stationLines: true}) // client.locations('Salzburg', {results: 2}) // client.station('8100173') // Graz Hbf // client.nearby({ diff --git a/p/vbb/example.js b/p/vbb/example.js index af234c8a..6e249d23 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -8,9 +8,9 @@ const client = createClient(vbbProfile) // Hauptbahnhof to Charlottenburg client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // client.departures('900000013102', {duration: 1}) -// client.arrivals('900000013102', {duration: 10}) +// client.arrivals('900000013102', {duration: 10, stationLines: true}) // client.locations('Alexanderplatz', {results: 2}) -// client.station('900000042101') // Spichernstr +// client.station('900000042101', {stationLines: true}) // Spichernstr // client.nearby({ // type: 'location', // latitude: 52.5137344, diff --git a/parse/location.js b/parse/location.js index 83a528be..2e53f431 100644 --- a/parse/location.js +++ b/parse/location.js @@ -22,7 +22,11 @@ const parseLocation = (profile, opt, {lines}, l) => { if ('pCls' in l) station.products = profile.parseProducts(l.pCls) - if (Array.isArray(l.pRefL) && Array.isArray(lines)) { + if ( + opt.stationLines && + Array.isArray(l.pRefL) && + Array.isArray(lines) + ) { station.lines = [] for (let pRef of l.pRefL) { const line = lines[pRef] From 17aeacf594e919074b341b3f9953cbb84e2f3c9f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 28 Jun 2018 13:32:42 +0200 Subject: [PATCH 122/176] arrivals/departures: fix remarks parsing :bug: --- parse/arrival-or-departure.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js index e4cc0adb..f6f7874a 100644 --- a/parse/arrival-or-departure.js +++ b/parse/arrival-or-departure.js @@ -19,9 +19,10 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { when: when.toISO(), direction: profile.parseStationName(d.dirTxt), line: lines[parseInt(d.prodX)] || null, - remarks: (d.remL - ? d.remL.map(ref => findRemark(hints, warnings, ref)) - : [] + remarks: ([] + .concat(d.remL || [], d.msgL || []) + .map(ref => findRemark(hints, warnings, ref)) + .filter(rem => !!rem) // filter unparsable ), // todo: res.trip from rawLine.prodCtx.num? trip: +d.jid.split('|')[1] // todo: this seems brittle From 31973431ff1a0289e58fb8f4ab308bb1d36a0b92 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 28 Jun 2018 13:45:56 +0200 Subject: [PATCH 123/176] option not to parse remarks --- docs/departures.md | 3 ++- docs/journey-leg.md | 3 ++- docs/journeys.md | 1 + index.js | 7 +++++-- lib/request.js | 5 +++-- parse/arrival-or-departure.js | 13 ++++++++----- parse/journey-leg.js | 3 ++- parse/journey.js | 2 +- parse/stopover.js | 2 +- 9 files changed, 25 insertions(+), 14 deletions(-) diff --git a/docs/departures.md b/docs/departures.md index d331da07..344958b4 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -27,7 +27,8 @@ With `opt`, you can override the default options, which look like this: when: new Date(), direction: null, // only show departures heading to this station duration: 10, // show departures for the next n minutes - stationLines: false // parse & expose lines of the station? + stationLines: false, // parse & expose lines of the station? + remarks: true // parse & expose hints & warnings? } ``` diff --git a/docs/journey-leg.md b/docs/journey-leg.md index 6060b36a..de7fddc8 100644 --- a/docs/journey-leg.md +++ b/docs/journey-leg.md @@ -26,7 +26,8 @@ With `opt`, you can override the default options, which look like this: { when: new Date(), stopovers: true, // return stations on the way? - polyline: false // return a shape for the leg? + polyline: false, // return a shape for the leg? + remarks: true // parse & expose hints & warnings? } ``` diff --git a/docs/journeys.md b/docs/journeys.md index 0e8a5bc8..ebbf55ce 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -65,6 +65,7 @@ With `opt`, you can override the default options, which look like this: }, tickets: false, // return tickets? only available with some profiles polylines: false, // return a shape for each leg? + remarks: true, // parse & expose hints & warnings? // Consider walking to nearby stations at the beginning of a journey? startWithWalking: true } diff --git a/index.js b/index.js index dfe795e2..6dee9941 100644 --- a/index.js +++ b/index.js @@ -34,7 +34,8 @@ const createClient = (profile, request = _request) => { opt = Object.assign({ direction: null, // only show departures heading to this station duration: 10, // show departures for the next n minutes - stationLines: false // parse & expose lines of the station? + stationLines: false, // parse & expose lines of the station? + remarks: true // parse & expose hints & warnings? }, opt) opt.when = new Date(opt.when || Date.now()) if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') @@ -115,6 +116,7 @@ const createClient = (profile, request = _request) => { bike: false, // only bike-friendly journeys tickets: false, // return tickets? polylines: false, // return leg shapes? + remarks: true, // parse & expose hints & warnings? // Consider walking to nearby stations at the beginning of a journey? startWithWalking: true }, opt) @@ -322,7 +324,8 @@ const createClient = (profile, request = _request) => { } opt = Object.assign({ stopovers: true, // return stations on the way? - polyline: false + polyline: false, + remarks: true // parse & expose hints & warnings? }, opt) opt.when = new Date(opt.when || Date.now()) if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') diff --git a/lib/request.js b/lib/request.js index 8102b7fe..4022bed4 100644 --- a/lib/request.js +++ b/lib/request.js @@ -81,11 +81,12 @@ const request = (profile, opt, data) => { const d = b.svcResL[0].res const c = d.common || {} - if (Array.isArray(c.remL)) { + d.hints = [] + if (opt.remarks && Array.isArray(c.remL)) { d.hints = c.remL.map(hint => profile.parseHint(profile, hint)) } d.warnings = [] - if (Array.isArray(c.himL)) { + if (opt.remarks && Array.isArray(c.himL)) { d.warnings = c.himL.map(w => profile.parseWarning(profile, w)) } if (Array.isArray(c.opL)) { diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js index f6f7874a..6aaa5e4e 100644 --- a/parse/arrival-or-departure.js +++ b/parse/arrival-or-departure.js @@ -19,11 +19,7 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { when: when.toISO(), direction: profile.parseStationName(d.dirTxt), line: lines[parseInt(d.prodX)] || null, - remarks: ([] - .concat(d.remL || [], d.msgL || []) - .map(ref => findRemark(hints, warnings, ref)) - .filter(rem => !!rem) // filter unparsable - ), + remarks: [], // todo: res.trip from rawLine.prodCtx.num? trip: +d.jid.split('|')[1] // todo: this seems brittle } @@ -56,6 +52,13 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { res.formerScheduledWhen = when.toISO() } + if (opt.remarks) { + res.remarks = [] + .concat(d.remL || [], d.msgL || []) + .map(ref => findRemark(hints, warnings, ref)) + .filter(rem => !!rem) // filter unparsable + } + return res } diff --git a/parse/journey-leg.js b/parse/journey-leg.js index c92d8204..df56120b 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -95,12 +95,13 @@ const createParseJourneyLeg = (profile, opt, data) => { res.stopovers = stopL.map(parse) // todo: is there a `pt.jny.remL`? - if (Array.isArray(pt.jny.msgL)) { + if (opt.remarks && Array.isArray(pt.jny.msgL)) { for (let i = 0; i < stopL.length; i++) { Object.defineProperty(res.stopovers[i], locX, { value: stopL[i].locX }) } + // todo: apply leg-wide remarks if `parseStopovers` is false applyRemarks(res, hints, warnings, pt.jny.msgL) } diff --git a/parse/journey.js b/parse/journey.js index dfbbd20f..a852469f 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -23,7 +23,7 @@ const createParseJourney = (profile, opt, data) => { legs } - if (Array.isArray(j.msgL)) { + if (opt.remarks && Array.isArray(j.msgL)) { res.remarks = [] for (let ref of j.msgL) { const remark = findRemark(hints, warnings, ref) diff --git a/parse/stopover.js b/parse/stopover.js index b2e5b620..6b55944c 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -59,7 +59,7 @@ const createParseStopover = (profile, opt, data, date) => { } } - if (Array.isArray(st.msgL)) { + if (opt.remarks && Array.isArray(st.msgL)) { res.remarks = [] for (let ref of st.msgL) { const remark = findRemark(hints, warnings, ref) From bb6e42a66275b42cc8cdce83474b26aab048f3dd Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 28 Jun 2018 14:11:41 +0200 Subject: [PATCH 124/176] walking legs: expose distance --- docs/changelog.md | 1 + parse/journey-leg.js | 1 + test/lib/validators.js | 8 +++++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 49504595..d406a19c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 021ae45 `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms - 84bce0c `arrivals()`/`departures()`: parse & expose platforms - 85e0bdf `journeys()`: `startWithWalking` option with default `true` +- f6ae29c journey legs with `type: 'walking'` now have a `distance` in meters ### breaking changes 💥 diff --git a/parse/journey-leg.js b/parse/journey-leg.js index df56120b..5d740b86 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -80,6 +80,7 @@ const createParseJourneyLeg = (profile, opt, data) => { if (pt.type === 'WALK') { res.mode = 'walking' res.public = true + res.distance = pt.gis && pt.gis.dist || null } else if (pt.type === 'JNY') { // todo: pull `public` value from `profile.products` res.id = pt.jny.jid diff --git a/test/lib/validators.js b/test/lib/validators.js index 237674b4..e6ff13e5 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -191,7 +191,13 @@ const createValidateJourneyLeg = (cfg) => { } } - if (leg.mode !== 'walking') { + if (leg.mode === 'walking') { + if (leg.distance !== null) { + const msg = name + '.distance must be ' + a.strictEqual(typeof leg.distance, 'number', msg + 'a number') + a.ok(leg.distance > 0, msg + '> 0') + } + } else { const msg = name + '.direction must be a string' a.strictEqual(typeof leg.direction, 'string', msg) a.ok(leg.direction, name + '.direction must not be empty') From 440ed6d1fb051dd8b032f9dd5e1231c9670fcfa1 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 28 Jun 2018 16:34:49 +0200 Subject: [PATCH 125/176] parseStopover: fix first/last canceled stopovers :bug: [ci skip] --- docs/changelog.md | 4 ++++ parse/stopover.js | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index d406a19c..687c3620 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -31,6 +31,10 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 8881d8a & b6fbaa5: change parsers signature to `parse…(profile, opt, data)` - cabe5fa: option to parse & expose `station.lines`, default off +### bugfixes + +- dd0a9b2 `parseStopover`: fix first/last canceled stopovers 🐛 + ## `2.7.0` - `journeys()`: `polylines` option diff --git a/parse/stopover.js b/parse/stopover.js index 6b55944c..491cef21 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -49,13 +49,17 @@ const createParseStopover = (profile, opt, data, date) => { Object.defineProperty(res, 'canceled', {value: true}) if (st.aCncl) { res.arrival = res.arrivalDelay = null - const arr = profile.parseDateTime(profile, date, st.aTimeS) - res.formerScheduledArrival = arr.toISO() + if (st.aTimeS) { + const arr = profile.parseDateTime(profile, date, st.aTimeS) + res.formerScheduledArrival = arr.toISO() + } } if (st.dCncl) { res.departure = res.departureDelay = null - const arr = profile.parseDateTime(profile, date, st.dTimeS) - res.formerScheduledDeparture = arr.toISO() + if (st.dTimeS) { + const arr = profile.parseDateTime(profile, date, st.dTimeS) + res.formerScheduledDeparture = arr.toISO() + } } } From bdb9c15c7e2c2d86db838afb69000120fe90e5fb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 30 Jun 2018 13:40:39 +0200 Subject: [PATCH 126/176] luxon@1, vbb-short-station-name@1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ed3e4151..48374738 100644 --- a/package.json +++ b/package.json @@ -39,14 +39,14 @@ "fetch-ponyfill": "^6.0.0", "gps-distance": "0.0.4", "lodash": "^4.17.5", - "luxon": "^0.5.8", + "luxon": "^1.3.0", "p-throttle": "^1.1.0", "pinkie-promise": "^2.0.1", "query-string": "^6.0.0", "slugg": "^1.2.0", "vbb-parse-line": "^0.3.1", "vbb-parse-ticket": "^0.2.1", - "vbb-short-station-name": "^0.4.0", + "vbb-short-station-name": "^1.0.1", "vbb-stations": "^6.2.1", "vbb-translate-ids": "^3.1.0" }, From 5f03c8e87858ca578a643bedc697561c85f2f51c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 30 Jun 2018 18:02:48 +0200 Subject: [PATCH 127/176] parse more hints --- parse/hint.js | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/parse/hint.js b/parse/hint.js index 03f7d277..734accfc 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -6,6 +6,11 @@ const hints = Object.assign(Object.create(null), { code: 'bicycle-conveyance', text: 'bicycles conveyed' }, + fr: { + type: 'hint', + code: 'bicycle-conveyance-reservation', + text: 'bicycles conveyed, subject to reservation' + }, nf: { type: 'hint', code: 'no-bicycle-conveyance', @@ -26,11 +31,26 @@ const hints = Object.assign(Object.create(null), { code: 'wifi', text: 'WiFi available' }, + wi: { + type: 'hint', + code: 'wifi', + text: 'WiFi available' + }, sn: { type: 'hint', code: 'snacks', text: 'snacks available for purchase' }, + mb: { + type: 'hint', + code: 'snacks', + text: 'snacks available for purchase' + }, + mp: { + type: 'hint', + code: 'snacks', + text: 'snacks available for purchase at the seat' + }, bf: { type: 'hint', code: 'barrier-free', @@ -56,6 +76,11 @@ const hints = Object.assign(Object.create(null), { code: 'childrens-area', text: `children's area available` }, + kr: { + type: 'hint', + code: 'kids-service', + text: 'DB Kids Service available' + }, ls: { type: 'hint', code: 'power-sockets', @@ -90,6 +115,41 @@ const hints = Object.assign(Object.create(null), { type: 'hint', code: 'ice-sprinter', text: 'ICE Sprinter service' + }, + rp: { + type: 'hint', + code: 'compulsory-reservation', + text: 'compulsory seat reservation' + }, + sk: { + type: 'hint', + code: 'oversize-luggage-forbidden', + text: 'oversize luggage not allowed' + }, + hu: { + type: 'hint', + code: 'animals-forbidden', + text: 'animals not allowed, except guide dogs' + }, + ik: { + type: 'hint', + code: 'baby-cot-required', + text: 'baby cot/child seat required' + }, + ee: { + type: 'hint', + code: 'on-board-entertainment', + text: 'on-board entertainment available' + }, + toilet: { + type: 'hint', + code: 'toilet', + text: 'toilet available' + }, + iz: { + type: 'hint', + code: 'intercity-2', + text: 'Intercity 2' } }) From d9b7df693a29d366264a7b8ac465fdf1af4c8d0d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 30 Jun 2018 18:04:44 +0200 Subject: [PATCH 128/176] parse hint & warning types using icons [ci skip] --- lib/request.js | 6 ++- parse/hint.js | 100 +++++++++++++++++++++++------------------------ parse/warning.js | 11 +++++- 3 files changed, 61 insertions(+), 56 deletions(-) diff --git a/lib/request.js b/lib/request.js index 4022bed4..6cedd1ca 100644 --- a/lib/request.js +++ b/lib/request.js @@ -83,11 +83,13 @@ const request = (profile, opt, data) => { d.hints = [] if (opt.remarks && Array.isArray(c.remL)) { - d.hints = c.remL.map(hint => profile.parseHint(profile, hint)) + const icons = opt.remarks && c.icoL || [] + d.hints = c.remL.map(hint => profile.parseHint(profile, hint, icons)) } d.warnings = [] if (opt.remarks && Array.isArray(c.himL)) { - d.warnings = c.himL.map(w => profile.parseWarning(profile, w)) + const icons = opt.remarks && c.icoL || [] + d.warnings = c.himL.map(w => profile.parseWarning(profile, w, icons)) } if (Array.isArray(c.opL)) { d.operators = c.opL.map(op => profile.parseOperator(profile, op)) diff --git a/parse/hint.js b/parse/hint.js index 734accfc..9bec071a 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -4,173 +4,172 @@ const hints = Object.assign(Object.create(null), { fb: { type: 'hint', code: 'bicycle-conveyance', - text: 'bicycles conveyed' + summary: 'bicycles conveyed' }, fr: { type: 'hint', code: 'bicycle-conveyance-reservation', - text: 'bicycles conveyed, subject to reservation' + summary: 'bicycles conveyed, subject to reservation' }, nf: { type: 'hint', code: 'no-bicycle-conveyance', - text: 'bicycles not conveyed' + summary: 'bicycles not conveyed' }, k2: { type: 'hint', code: '2nd-class-only', - text: '2. class only' + summary: '2. class only' }, eh: { type: 'hint', code: 'boarding-ramp', - text: 'vehicle-mounted boarding ramp available' + summary: 'vehicle-mounted boarding ramp available' }, wv: { type: 'hint', code: 'wifi', - text: 'WiFi available' + summary: 'WiFi available' }, wi: { type: 'hint', code: 'wifi', - text: 'WiFi available' + summary: 'WiFi available' }, sn: { type: 'hint', code: 'snacks', - text: 'snacks available for purchase' + summary: 'snacks available for purchase' }, mb: { type: 'hint', code: 'snacks', - text: 'snacks available for purchase' + summary: 'snacks available for purchase' }, mp: { type: 'hint', code: 'snacks', - text: 'snacks available for purchase at the seat' + summary: 'snacks available for purchase at the seat' }, bf: { type: 'hint', code: 'barrier-free', - text: 'barrier-free' + summary: 'barrier-free' }, rg: { type: 'hint', code: 'barrier-free-vehicle', - text: 'barrier-free vehicle' + summary: 'barrier-free vehicle' }, bt: { type: 'hint', code: 'on-board-bistro', - text: 'Bordbistro available' + summary: 'Bordbistro available' }, br: { type: 'hint', code: 'on-board-restaurant', - text: 'Bordrestaurant available' + summary: 'Bordrestaurant available' }, ki: { type: 'hint', code: 'childrens-area', - text: `children's area available` + summary: `children's area available` }, kr: { type: 'hint', code: 'kids-service', - text: 'DB Kids Service available' + summary: 'DB Kids Service available' }, ls: { type: 'hint', code: 'power-sockets', - text: 'power sockets available' + summary: 'power sockets available' }, ev: { type: 'hint', code: 'replacement-service', - text: 'replacement service' + summary: 'replacement service' }, kl: { type: 'hint', code: 'air-conditioned', - text: 'air-conditioned vehicle' + summary: 'air-conditioned vehicle' }, r0: { type: 'hint', code: 'upward-escalator', - text: 'upward escalator' + summary: 'upward escalator' }, au: { type: 'hint', code: 'elevator', - text: 'elevator available' + summary: 'elevator available' }, ck: { type: 'hint', code: 'komfort-checkin', - text: 'Komfort-Checkin available' + summary: 'Komfort-Checkin available' }, it: { type: 'hint', code: 'ice-sprinter', - text: 'ICE Sprinter service' + summary: 'ICE Sprinter service' }, rp: { type: 'hint', code: 'compulsory-reservation', - text: 'compulsory seat reservation' + summary: 'compulsory seat reservation' }, sk: { type: 'hint', code: 'oversize-luggage-forbidden', - text: 'oversize luggage not allowed' + summary: 'oversize luggage not allowed' }, hu: { type: 'hint', code: 'animals-forbidden', - text: 'animals not allowed, except guide dogs' + summary: 'animals not allowed, except guide dogs' }, ik: { type: 'hint', code: 'baby-cot-required', - text: 'baby cot/child seat required' + summary: 'baby cot/child seat required' }, ee: { type: 'hint', code: 'on-board-entertainment', - text: 'on-board entertainment available' + summary: 'on-board entertainment available' }, toilet: { type: 'hint', code: 'toilet', - text: 'toilet available' + summary: 'toilet available' }, iz: { type: 'hint', code: 'intercity-2', - text: 'Intercity 2' + summary: 'Intercity 2' } }) +const codesByIcon = Object.assign(Object.create(null), { + cancel: 'cancelled' +}) + // todo: is passing in profile necessary? -const parseHint = (profile, h) => { +const parseHint = (profile, h, icons) => { // todo: C const text = h.txtN && h.txtN.trim() || '' + const icon = 'number' === typeof h.icoX && icons[h.icoX] || null + const code = h.code || (icon && icon.res && codesByIcon[icon.res]) || null 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, + code, text } } @@ -191,21 +190,13 @@ const parseHint = (profile, h) => { text } } - // todo: - // { - // "type": "U", - // "code": "", - // "icoX": 3, - // "txtN": "entrance and exit not possible" - // } - if (h.type === 'U') { + if (h.type === 'G' && text.toLowerCase() === 'platform change') { return { type: 'status', - code: h.code || null, + code: 'changed platform', text } } - if (h.type === 'L') { return { type: 'status', @@ -215,13 +206,18 @@ const parseHint = (profile, h) => { } } if (h.type === 'A') { - return hints[h.code && h.code.trim().toLowerCase()] || null + const hint = hints[h.code && h.code.trim().toLowerCase()] + if (hint) { + return Object.assign({text: h.txtN}, hint) + } + return null } - if (h.type === 'R') { + + if (h.type === 'D' || h.type === 'U' || h.type === 'R' || h.type === 'N') { // todo: how can we identify the individual types? return { type: 'status', - code: h.code, + code, text } } diff --git a/parse/warning.js b/parse/warning.js index f6e1ce2f..face0052 100644 --- a/parse/warning.js +++ b/parse/warning.js @@ -4,8 +4,12 @@ const brToNewline = require('br2nl') const parseDateTime = require('./date-time') +const typesByIcon = Object.assign(Object.create(null), { + HimWarn: 'status' +}) + // todo: is passing in profile necessary? -const parseWarning = (profile, w) => { +const parseWarning = (profile, w, icons) => { // todo: hid, act, pub, lead, tckr, icoX, fLocX, tLocX, prod, comp, // todo: cat (1, 2), pubChL // pubChL: @@ -20,8 +24,11 @@ const parseWarning = (profile, w) => { // tDate: '20180713', // tTime: '030000' } ] + const icon = 'number' === typeof h.icoX && icons[h.icoX] || null + const type = icon && icon.res && typesByIcon[icon.res] || 'warning' + return { - type: 'warning', + type, summary: brToNewline(w.head), text: brToNewline(w.text), priority: w.prio, From 2f40c20175b92f92711f08b7248d08c7353faba7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 2 Jul 2018 16:49:21 +0200 Subject: [PATCH 129/176] parse `TRSF` legs as `walking` :bug:, add todos --- parse/journey-leg.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 5d740b86..32bb4b84 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -38,11 +38,10 @@ const createParseJourneyLeg = (profile, opt, data) => { const {locations, lines, hints, warnings, polylines} = data // todo: pt.status // todo: pt.status, pt.isPartCncl + // todo: pt.isRchbl, pt.chRatingRT, pt.chgDurR, pt.minChg // 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? // j = journey, pt = part // todo: pt.planrtTS @@ -77,7 +76,7 @@ const createParseJourneyLeg = (profile, opt, data) => { res.polyline = p && parse(p) || null } - if (pt.type === 'WALK') { + if (pt.type === 'WALK' || pt.type === 'TRSF') { res.mode = 'walking' res.public = true res.distance = pt.gis && pt.gis.dist || null From 4bcae146c1e3d65cc1f763ab6f5e068a31bb85bd Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 2 Jul 2018 16:55:39 +0200 Subject: [PATCH 130/176] fix warnings parsing :bug: --- parse/warning.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/warning.js b/parse/warning.js index face0052..ad5d1147 100644 --- a/parse/warning.js +++ b/parse/warning.js @@ -24,7 +24,7 @@ const parseWarning = (profile, w, icons) => { // tDate: '20180713', // tTime: '030000' } ] - const icon = 'number' === typeof h.icoX && icons[h.icoX] || null + const icon = 'number' === typeof h.icoX && icons[w.icoX] || null const type = icon && icon.res && typesByIcon[icon.res] || 'warning' return { From 65f36039537f226faa748a12ecead2acda8550eb Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 2 Jul 2018 16:59:38 +0200 Subject: [PATCH 131/176] fix warnings parsing part 2 :bug: --- parse/warning.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse/warning.js b/parse/warning.js index ad5d1147..aae5074b 100644 --- a/parse/warning.js +++ b/parse/warning.js @@ -24,7 +24,7 @@ const parseWarning = (profile, w, icons) => { // tDate: '20180713', // tTime: '030000' } ] - const icon = 'number' === typeof h.icoX && icons[w.icoX] || null + const icon = 'number' === typeof w.icoX && icons[w.icoX] || null const type = icon && icon.res && typesByIcon[icon.res] || 'warning' return { From a34999b1c5e52fcbd9b9704228a374255d21ab6c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 2 Jul 2018 18:48:23 +0200 Subject: [PATCH 132/176] parse more hints [ci skip] --- parse/hint.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/parse/hint.js b/parse/hint.js index 9bec071a..eebcad90 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -1,5 +1,7 @@ 'use strict' +const trim = require('lodash/trim') + const hints = Object.assign(Object.create(null), { fb: { type: 'hint', @@ -26,6 +28,16 @@ const hints = Object.assign(Object.create(null), { code: 'boarding-ramp', summary: 'vehicle-mounted boarding ramp available' }, + ro: { + type: 'hint', + code: 'wheelchairs-space', + summary: 'space for wheelchairs' + }, + oa: { + type: 'hint', + code: 'wheelchairs-space-reservation', + summary: 'space for wheelchairs, subject to reservation' + }, wv: { type: 'hint', code: 'wifi', @@ -76,6 +88,11 @@ const hints = Object.assign(Object.create(null), { code: 'childrens-area', summary: `children's area available` }, + kk: { + type: 'hint', + code: 'parents-childrens-compartment', + summary: `parent-and-children compartment available` + }, kr: { type: 'hint', code: 'kids-service', @@ -121,6 +138,21 @@ const hints = Object.assign(Object.create(null), { code: 'compulsory-reservation', summary: 'compulsory seat reservation' }, + rm: { + type: 'hint', + code: 'optional-reservation', + summary: 'optional seat reservation' + }, + scl: { + type: 'hint', + code: 'all-2nd-class-seats-reserved', + summary: 'all 2nd class seats reserved' + }, + acl: { + type: 'hint', + code: 'all-seats-reserved', + summary: 'all seats reserved' + }, sk: { type: 'hint', code: 'oversize-luggage-forbidden', @@ -146,6 +178,11 @@ const hints = Object.assign(Object.create(null), { code: 'toilet', summary: 'toilet available' }, + oc: { + type: 'hint', + code: 'wheelchair-accessible-toilet', + summary: 'wheelchair-accessible toilet available' + }, iz: { type: 'hint', code: 'intercity-2', @@ -160,6 +197,13 @@ const codesByIcon = Object.assign(Object.create(null), { // todo: is passing in profile necessary? const parseHint = (profile, h, icons) => { // todo: C + // todo: + // { type: 'Q', + // code: '', + // icoX: 11, + // txtN: + // 'RE 3132: Berlin Zoologischer Garten - Brandenburg Hbf: Information. A railway carriage is missing', + // sIdx: 4 } const text = h.txtN && h.txtN.trim() || '' const icon = 'number' === typeof h.icoX && icons[h.icoX] || null @@ -186,7 +230,14 @@ const parseHint = (profile, h, icons) => { if (h.type === 'U' && text.toLowerCase() === 'stop cancelled') { return { type: 'status', - code: 'stop-cancelled', + code: 'stop-cancelled', // todo: change to stopover-cancelled + text + } + } + if (h.type === 'U' && trim(text.toLowerCase(), ' ()') === 'additional stop') { + return { + type: 'status', + code: 'additional-stopover', text } } @@ -215,12 +266,14 @@ const parseHint = (profile, h, icons) => { if (h.type === 'D' || h.type === 'U' || h.type === 'R' || h.type === 'N') { // todo: how can we identify the individual types? + // todo: does `D` mean "disturbance"? return { type: 'status', code, text } } + return null } From 2a6f1f918358f1685fea16efda77fdb331e74214 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 5 Jul 2018 12:13:29 +0200 Subject: [PATCH 133/176] transfer flag for walking legs [ci skip] --- parse/journey-leg.js | 1 + 1 file changed, 1 insertion(+) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 32bb4b84..95f02324 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -80,6 +80,7 @@ const createParseJourneyLeg = (profile, opt, data) => { res.mode = 'walking' res.public = true res.distance = pt.gis && pt.gis.dist || null + if (pt.type === 'TRSF') res.transfer = true } else if (pt.type === 'JNY') { // todo: pull `public` value from `profile.products` res.id = pt.jny.jid From 86fc27e34024544ab455f6203f769cd71005d296 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 29 Jun 2018 14:42:40 +0200 Subject: [PATCH 134/176] docs: journeyLeg() -> trip() :memo: --- docs/departures.md | 2 +- docs/journeys.md | 2 +- docs/radar.md | 2 +- docs/readme.md | 2 +- docs/{journey-leg.md => trip.md} | 16 +++++++++------- 5 files changed, 13 insertions(+), 11 deletions(-) rename docs/{journey-leg.md => trip.md} (81%) diff --git a/docs/departures.md b/docs/departures.md index 344958b4..9930b5ad 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -36,7 +36,7 @@ With `opt`, you can override the default options, which look like this: *Note:* As stated in the [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1), the `when` field includes the current delay. The `delay` field, if present, expresses how much the former differs from the schedule. -You may pass the `tripId` field into [`journeyLeg(ref, lineName, [opt])`](journey-leg.md) to get details on the vehicle's journey. +You may pass the `tripId` field into [`trip(id, lineName, [opt])`](trip.md) to get details on the vehicle's trip. As an example, we're going to use the [VBB profile](../p/vbb): diff --git a/docs/journeys.md b/docs/journeys.md index ebbf55ce..1e7868a2 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -272,4 +272,4 @@ departure of last journey 2017-12-17T19:07:00.000+01:00 departure of first (later) journey 2017-12-17T19:19:00.000+01:00 ``` -If you pass `polylines: true`, each journey leg will have a `polyline` field. Refer to [the section in the `journeyLeg()` docs](journey-leg.md#polyline-option) for details. +If you pass `polylines: true`, each journey leg will have a `polyline` field. Refer to [the section in the `trip()` docs](trip.md#polyline-option) for details. diff --git a/docs/radar.md b/docs/radar.md index e42155c0..d10077ac 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -163,4 +163,4 @@ The response may look like this: }, /* … */ ] ``` -If you pass `polylines: true`, each journey leg will have a `polyline` field, as documented in [the corresponding section in the `journeyLeg()` docs](journey-leg.md#polyline-option), with the exception that station info is missing. +If you pass `polylines: true`, each movement will have a `polyline` field, as documented in [the corresponding section in the `trip()` docs](trip.md#polyline-option), with the exception that station info is missing. diff --git a/docs/readme.md b/docs/readme.md index 61699d2d..f7d3a42b 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,7 +1,7 @@ # API documentation - [`journeys(from, to, [opt])`](journeys.md) – get journeys between locations -- [`journeyLeg(ref, lineName, [opt])`](journey-leg.md) – get details for a leg of a journey +- [`trip(id, lineName, [opt])`](trip.md) – get details for a trip - [`departures(station, [opt])`](departures.md) – query the next departures at a station - [`arrivals(station, [opt])`](arrivals.md) – query the next arrivals at a station - [`locations(query, [opt])`](locations.md) – find stations, POIs and addresses diff --git a/docs/journey-leg.md b/docs/trip.md similarity index 81% rename from docs/journey-leg.md rename to docs/trip.md index de7fddc8..3122dd81 100644 --- a/docs/journey-leg.md +++ b/docs/trip.md @@ -1,8 +1,10 @@ -# `journeyLeg(ref, lineName, [opt])` +# `trip(id, lineName, [opt])` -This method can be used to refetch information about a leg of a journey. Note that it is not supported by every profile/endpoint. +This method can be used to refetch information about a trip – a vehicle stopping at a set of stops at specific times. -Let's say you used [`journeys`](journeys.md) and now want to get more up-to-date data about the arrival/departure of a leg. You'd pass in a journey leg `id` like `'1|24983|22|86|18062017'`. `lineName` must be the name of the journey leg's `line.name`. You can get them like this: +*Note*: This method is not supported by every profile/endpoint. + +Let's say you used [`journeys`](journeys.md) and now want to get more up-to-date data about the arrival/departure of a leg. You'd pass in the trip ID from `leg.id`, e.g. `'1|24983|22|86|18062017'`, and the name of the line from `leg.line.name` like this: ```js const createClient = require('hafas-client') @@ -14,7 +16,7 @@ const client = createClient(vbbProfile) client.journeys('900000003201', '900000100008', {results: 1}) .then(([journey]) => { const leg = journey.legs[0] - return client.journeyLeg(leg.id, leg.line.name) + return client.trip(leg.id, leg.line.name) }) .then(console.log) .catch(console.error) @@ -26,7 +28,7 @@ With `opt`, you can override the default options, which look like this: { when: new Date(), stopovers: true, // return stations on the way? - polyline: false, // return a shape for the leg? + polyline: false, // return a shape for the trip? remarks: true // parse & expose hints & warnings? } ``` @@ -43,7 +45,7 @@ const vbbProfile = require('hafas-client/p/vbb') const client = createClient(vbbProfile) -client.journeyLeg('1|31431|28|86|17122017', 'S9', {when: 1513534689273}) +client.trip('1|31431|28|86|17122017', 'S9', {when: 1513534689273}) .then(console.log) .catch(console.error) ``` @@ -121,7 +123,7 @@ The response looked like this: ### `polyline` option -If you pass `polyline: true`, the leg will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1). Every `Point` next to a station will have `properties` containing the station's metadata. +If you pass `polyline: true`, the trip will have a `polyline` field, containing a [GeoJSON](http://geojson.org) [`FeatureCollection`](https://tools.ietf.org/html/rfc7946#section-3.3) of [`Point`s](https://tools.ietf.org/html/rfc7946#appendix-A.1). Every `Point` next to a station will have `properties` containing the station's metadata. We'll look at an example for *U6* from *Alt-Mariendorf* to *Alt-Tegel*, taken from the [VBB profile](../p/vbb): From ab5ca3db8bcaa42097aec084c8401f70d1735a20 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 29 Jun 2018 14:58:43 +0200 Subject: [PATCH 135/176] rename journeyLeg() to trip() :boom:, adapt tests --- docs/changelog.md | 11 ++++++----- docs/migrating-to-3.md | 8 ++++++-- index.js | 10 +++++----- test/db.js | 6 +++--- test/insa.js | 6 +++--- test/nahsh.js | 6 +++--- test/oebb.js | 6 +++--- test/vbb.js | 6 +++--- 8 files changed, 32 insertions(+), 27 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 687c3620..9d35c17d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,8 +8,8 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 0db84ce #61 parse remarks for stopovers and journey legs - ac9819b `arrivals()` method – [docs](arrivals.md) -- 21c273c `journeys()`/`journeyLeg()`: leg stopovers: parse & expose delays -- 021ae45 `journeys()`/`journeyLeg()`: leg stopovers: parse & expose platforms +- 21c273c `journeys()`/`trip()`: leg stopovers: parse & expose delays +- 021ae45 `journeys()`/`trip()`: leg stopovers: parse & expose platforms - 84bce0c `arrivals()`/`departures()`: parse & expose platforms - 85e0bdf `journeys()`: `startWithWalking` option with default `true` - f6ae29c journey legs with `type: 'walking'` now have a `distance` in meters @@ -24,12 +24,13 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - a356a26 throw if 0 products enabled - c82ad23 `journeys()`: `opt.when` → `opt.departure`/`opt.arrival` - 665bed9 rename `location(id)` to `station(id)` -- 6611f26 `journeys()`/`journeyLeg()`: `leg.passed` → `leg.stopovers` -- ebe4fa6 `journeys()`/`journeyLeg()`: `opt.passedStations` → `opt.stopovers` -- 3e672ee `journeys()`/`journeyLeg()`: `stopover.station` → `stopover.stop` +- 6611f26 `journeys()`/`trip()`: `leg.passed` → `leg.stopovers` +- ebe4fa6 `journeys()`/`trip()`: `opt.passedStations` → `opt.stopovers` +- 3e672ee `journeys()`/`trip()`: `stopover.station` → `stopover.stop` - 2e6aefe journey leg, departure, movement: `journeyId` -> `tripId` - 8881d8a & b6fbaa5: change parsers signature to `parse…(profile, opt, data)` - cabe5fa: option to parse & expose `station.lines`, default off +- c8ff217 rename `journeyLeg()` to `trip()` ### bugfixes diff --git a/docs/migrating-to-3.md b/docs/migrating-to-3.md index 4bda6e90..93f54b4c 100644 --- a/docs/migrating-to-3.md +++ b/docs/migrating-to-3.md @@ -1,6 +1,10 @@ # Migrating to `hafas-client@3` -## If you use the `journeys()` or `journeyLeg()` methods… +## If you use the `journeyLeg()` method… + +…change the `journeyLeg(id, lineName)` call to `trip(id, lineName)`. c8ff217 + +## If you use the `journeys()` or `trip()` methods… - …instead of `journey.departure`, use `journey.legs[0].departure`. 005f3f8 - …instead of `journey.arrival`, use `journey.legs[last].arrival`. 005f3f8 @@ -13,7 +17,7 @@ …use `opt.departure` instead. Use `opt.arrival` to get journeys arriving before the specified date+time. This replaces the `opt.when` & `opt.whenRepresents` options from `hafas-client@2`. c82ad23 -## If you use the `journeys()` and `opt.polylines` or `journeyLeg()` and `opt.polyline`… +## If you use the `journeys()` and `opt.polylines` or `trip()` and `opt.polyline`… …`leg.polyline` will be [parsed for you now](https://github.com/public-transport/hafas-client/blob/f6c824eecb459181ea90ddf41bf1a1e8b64539ec/docs/journey-leg.md#polyline-option). diff --git a/index.js b/index.js index 6dee9941..97068587 100644 --- a/index.js +++ b/index.js @@ -315,9 +315,9 @@ const createClient = (profile, request = _request) => { }) } - const journeyLeg = (ref, lineName, opt = {}) => { - if (!isNonEmptyString(ref)) { - throw new Error('ref must be a non-empty string.') + const trip = (id, lineName, opt = {}) => { + if (!isNonEmptyString(id)) { + throw new Error('id must be a non-empty string.') } if (!isNonEmptyString(lineName)) { throw new Error('lineName must be a non-empty string.') @@ -335,7 +335,7 @@ const createClient = (profile, request = _request) => { meth: 'JourneyDetails', req: { // todo: getTrainComposition - jid: ref, + jid: id, name: lineName, date: profile.formatDate(profile, opt.when), getPolyline: !!opt.polyline @@ -413,7 +413,7 @@ const createClient = (profile, request = _request) => { } const client = {departures, arrivals, journeys, locations, station, nearby} - if (profile.journeyLeg) client.journeyLeg = journeyLeg + if (profile.journeyLeg) client.trip = trip if (profile.radar) client.radar = radar Object.defineProperty(client, 'profile', {value: profile}) return client diff --git a/test/db.js b/test/db.js index 9bcdc7a6..c47e0c71 100644 --- a/test/db.js +++ b/test/db.js @@ -189,7 +189,7 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { t.end() })) -test('journey leg details', co(function* (t) { +test('trip details', co(function* (t) { const journeys = yield client.journeys(berlinHbf, münchenHbf, { results: 1, departure: when }) @@ -197,7 +197,7 @@ test('journey leg details', co(function* (t) { const p = journeys[0].legs[0] t.ok(p.id, 'precondition failed') t.ok(p.line.name, 'precondition failed') - const leg = yield client.journeyLeg(p.id, p.line.name, {when}) + const trip = yield client.trip(p.id, p.line.name, {when}) const validateJourneyLeg = createValidateJourneyLeg(cfg) const validate = createValidate(cfg, { @@ -206,7 +206,7 @@ test('journey leg details', co(function* (t) { validateJourneyLeg(validate, leg, name) } }) - validate(t, leg, 'journeyLeg', 'leg') + validate(t, trip, 'journeyLeg', 'trip') t.end() })) diff --git a/test/insa.js b/test/insa.js index 365740a1..5250f96d 100644 --- a/test/insa.js +++ b/test/insa.js @@ -152,7 +152,7 @@ test('earlier/later journeys', co(function* (t) { t.end() })) -test('journey leg details', co(function* (t) { +test('trip details', co(function* (t) { const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { results: 1, departure: when }) @@ -160,9 +160,9 @@ test('journey leg details', co(function* (t) { const p = journeys[0].legs[0] t.ok(p.id, 'precondition failed') t.ok(p.line.name, 'precondition failed') - const leg = yield client.journeyLeg(p.id, p.line.name, {when}) + const trip = yield client.trip(p.id, p.line.name, {when}) - validate(t, leg, 'journeyLeg', 'leg') + validate(t, trip, 'journeyLeg', 'trip') t.end() })) diff --git a/test/nahsh.js b/test/nahsh.js index a502908b..6c29000f 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -184,7 +184,7 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { // todo: with detour test // todo: without detour test -test('journey leg details for Flensburg to Husum', co(function* (t) { +test('trip details', co(function* (t) { const journeys = yield client.journeys(flensburg, husum, { results: 1, departure: when }) @@ -192,9 +192,9 @@ test('journey leg details for Flensburg to Husum', co(function* (t) { const p = journeys[0].legs[0] t.ok(p.id, 'precondition failed') t.ok(p.line.name, 'precondition failed') - const leg = yield client.journeyLeg(p.id, p.line.name, {when}) + const trip = yield client.trip(p.id, p.line.name, {when}) - validate(t, leg, 'journeyLeg', 'leg') + validate(t, trip, 'journeyLeg', 'trip') t.end() })) diff --git a/test/oebb.js b/test/oebb.js index d97581d3..3c2f045c 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -209,7 +209,7 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t t.end() })) -test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) { +test('trip details', co(function* (t) { const journeys = yield client.journeys(wienWestbahnhof, muenchenHbf, { results: 1, departure: when }) @@ -217,9 +217,9 @@ test('leg details for Wien Westbahnhof to München Hbf', co(function* (t) { const p = journeys[0].legs[0] t.ok(p.id, 'precondition failed') t.ok(p.line.name, 'precondition failed') - const leg = yield client.journeyLeg(p.id, p.line.name, {when}) + const trip = yield client.trip(p.id, p.line.name, {when}) - validate(t, leg, 'journeyLeg', 'leg') + validate(t, trip, 'journeyLeg', 'trip') t.end() })) diff --git a/test/vbb.js b/test/vbb.js index 36601c6d..3ce8c9bb 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -187,7 +187,7 @@ test('earlier/later journeys', co(function* (t) { t.end() })) -test('journey leg details', co(function* (t) { +test('trip details', co(function* (t) { const journeys = yield client.journeys(spichernstr, amrumerStr, { results: 1, departure: when }) @@ -195,9 +195,9 @@ test('journey leg details', co(function* (t) { const p = journeys[0].legs[0] t.ok(p.id, 'precondition failed') t.ok(p.line.name, 'precondition failed') - const leg = yield client.journeyLeg(p.id, p.line.name, {when}) + const trip = yield client.trip(p.id, p.line.name, {when}) - validate(t, leg, 'journeyLeg', 'leg') + validate(t, trip, 'journeyLeg', 'trip') t.end() })) From a187f8ee97c8249b6710590e59d07f9f47b4b6f7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 29 Jun 2018 15:05:29 +0200 Subject: [PATCH 136/176] adapt examples to e1bdd56 :memo: --- p/insa/example.js | 2 +- p/nahsh/example.js | 2 +- p/readme.md | 2 +- p/vbb/example.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/p/insa/example.js b/p/insa/example.js index 9397c65c..01ebdaf0 100644 --- a/p/insa/example.js +++ b/p/insa/example.js @@ -26,7 +26,7 @@ client.journeys('008010226', '008013456', {results: 1}) // .then(([journey]) => { // const leg = journey.legs[0] -// return client.journeyLeg(leg.id, leg.line.name) +// return client.trip(leg.id, leg.line.name) // }) .then(data => { diff --git a/p/nahsh/example.js b/p/nahsh/example.js index 62ff2f3c..fad4fbf4 100644 --- a/p/nahsh/example.js +++ b/p/nahsh/example.js @@ -9,7 +9,7 @@ const client = createClient(nahshProfile) client.journeys('8000103', '8000199', {results: 10, tickets: true}) // client.departures('8000199', {duration: 10}) // client.arrivals('8000199', {duration: 5, stationLines: true}) -// client.journeyLeg('1|30161|5|100|14032018', 'Bus 52') +// client.trip('1|30161|5|100|14032018', 'Bus 52') // client.locations('Schleswig', {results: 1}) // client.station('706990') // Kiel Holunderbusch // client.nearby({ diff --git a/p/readme.md b/p/readme.md index dea7cc16..7cb66714 100644 --- a/p/readme.md +++ b/p/readme.md @@ -1,6 +1,6 @@ # Profiles -This directory contains specific customisations for each endpoint, called *profiles*. They **parse data from the API differently, add additional information, or enable non-default methods** (such as [`journeyLeg`](../docs/journey-leg.md)) if they are supported. +This directory contains specific customisations for each endpoint, called *profiles*. They **parse data from the API differently, add additional information, or enable non-default methods** (such as [`trip`](../docs/trip.md)) if they are supported. Each profile has it's own directory. It will be passed into `hafas-client` and is expected to be in a certain structure: diff --git a/p/vbb/example.js b/p/vbb/example.js index 6e249d23..270379ba 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -25,7 +25,7 @@ client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // .then(([journey]) => { // const leg = journey.legs[0] -// return client.journeyLeg(leg.id, leg.line.name, {polyline: true}) +// return client.trip(leg.id, leg.line.name, {polyline: true}) // }) .then((data) => { console.log(require('util').inspect(data, {depth: null})) From 4a454917dd3f8f50bfb741adc521a807e3daa39f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 29 Jun 2018 15:14:26 +0200 Subject: [PATCH 137/176] rename profile.journeyLeg to profile.trip :boom: --- docs/changelog.md | 1 + docs/migrating-to-3.md | 3 ++- docs/profile-boilerplate.js | 2 +- docs/writing-a-profile.md | 2 +- index.js | 2 +- lib/default-profile.js | 2 +- lib/validate-profile.js | 7 +++++++ p/db/index.js | 2 +- p/insa/index.js | 2 +- p/nahsh/index.js | 2 +- p/oebb/index.js | 2 +- p/vbb/index.js | 2 +- 12 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9d35c17d..c42ce562 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -31,6 +31,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 8881d8a & b6fbaa5: change parsers signature to `parse…(profile, opt, data)` - cabe5fa: option to parse & expose `station.lines`, default off - c8ff217 rename `journeyLeg()` to `trip()` +- 8de4447 rename `profile.journeyLeg` to `profile.trip` ### bugfixes diff --git a/docs/migrating-to-3.md b/docs/migrating-to-3.md index 93f54b4c..eb2c7ba2 100644 --- a/docs/migrating-to-3.md +++ b/docs/migrating-to-3.md @@ -36,7 +36,8 @@ ## If you use `hafas-client` with a custom profile… -…write your profile in [the new format](writing-a-profile.md). Then, you can pass it into `hafas-client` just like before. #32/b7c1ee3 +- …write your profile in [the new format](writing-a-profile.md). Then, you can pass it into `hafas-client` just like before. #32/b7c1ee3 +- …rename the `profile.journeyLeg` flag to `profile.trip`. 8de4447 ## If you use `hafas-client` with custom parse functions… diff --git a/docs/profile-boilerplate.js b/docs/profile-boilerplate.js index 6b8ae7b0..97f7291c 100644 --- a/docs/profile-boilerplate.js +++ b/docs/profile-boilerplate.js @@ -37,7 +37,7 @@ const insaProfile = { products: products, - journeyLeg: false, + trip: false, radar: false } diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index ff5d1ccb..01bee324 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -140,7 +140,7 @@ We consider these improvements to be *optional*: - **Check if the endpoint supports the journey legs call.** - In the app, check if you can query details for the status of a single journey leg. It should load realtime delays and the current progress. - - If this feature is supported, add `journeyLeg: true` to the profile. + - If this feature is supported, add `trip: true` to the profile. - **Check if the endpoint supports the live map call.** Does the app have a "live map" showing all vehicles within an area? If so, add `radar: true` to the profile. - **Consider transforming station & line names** into the formats that's most suitable for *local users*. Some examples: - `M13 (Tram)` -> `M13`. With Berlin context, it is obvious that `M13` is a tram. diff --git a/index.js b/index.js index 97068587..752b2b6f 100644 --- a/index.js +++ b/index.js @@ -413,7 +413,7 @@ const createClient = (profile, request = _request) => { } const client = {departures, arrivals, journeys, locations, station, nearby} - if (profile.journeyLeg) client.trip = trip + if (profile.trip) client.trip = trip if (profile.radar) client.radar = radar Object.defineProperty(client, 'profile', {value: profile}) return client diff --git a/lib/default-profile.js b/lib/default-profile.js index 64bc34dd..c7c0f89b 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -66,7 +66,7 @@ const defaultProfile = { filters, journeysNumF: true, // `journeys()` method: support for `numF` field? - journeyLeg: false, + trip: false, radar: false } diff --git a/lib/validate-profile.js b/lib/validate-profile.js index 5f8dae6b..4c7f6374 100644 --- a/lib/validate-profile.js +++ b/lib/validate-profile.js @@ -71,6 +71,13 @@ const validateProfile = (profile) => { } } } + + if ('trip' in profile && 'boolean' !== typeof profile.trip) { + throw new Error('profile.trip must be a boolean.') + } + if ('journeyLeg' in profile) { + throw new Error('profile.journeyLeg has been removed. Use profile.trip.') + } } module.exports = validateProfile diff --git a/p/db/index.js b/p/db/index.js index 8747b6ca..345f94f6 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -102,7 +102,7 @@ const dbProfile = { formatStation, - journeyLeg: true // todo: #49 + trip: true // todo: #49 } module.exports = dbProfile diff --git a/p/insa/index.js b/p/insa/index.js index ed562dee..c02b3445 100644 --- a/p/insa/index.js +++ b/p/insa/index.js @@ -25,7 +25,7 @@ const insaProfile = { products: products, - journeyLeg: true, + trip: true, radar: true } diff --git a/p/nahsh/index.js b/p/nahsh/index.js index cab9da69..9a992266 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -103,7 +103,7 @@ const nahshProfile = { parseJourney: createParseJourney, parseMovement: createParseMovement, - journeyLeg: true, + trip: true, radar: true // todo: see #34 } diff --git a/p/oebb/index.js b/p/oebb/index.js index a78d88bc..c6fbb1e7 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -71,7 +71,7 @@ const oebbProfile = { parseLocation, parseMovement: createParseMovement, - journeyLeg: true, + trip: true, radar: true } diff --git a/p/vbb/index.js b/p/vbb/index.js index b58f1f19..cfb14373 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -142,7 +142,7 @@ const vbbProfile = { formatStation, journeysNumF: false, - journeyLeg: true, + trip: true, radar: true } From c80f355d47cf5d818df45a3dd7adf895a0178a23 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 7 Jul 2018 21:26:40 +0200 Subject: [PATCH 138/176] tests for departures with direction option :white_check_mark: --- test/db.js | 15 ++++++++++++ test/insa.js | 15 ++++++++++++ test/lib/departures-in-direction.js | 37 +++++++++++++++++++++++++++++ test/nahsh.js | 16 +++++++++++++ test/oebb.js | 16 +++++++++++++ test/vbb.js | 14 +++++++++++ 6 files changed, 113 insertions(+) create mode 100644 test/lib/departures-in-direction.js diff --git a/test/db.js b/test/db.js index c47e0c71..f714b207 100644 --- a/test/db.js +++ b/test/db.js @@ -22,6 +22,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testDeparturesInDirection = require('./lib/departures-in-direction') const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') @@ -74,6 +75,7 @@ const westhafen = '008089116' const wedding = '008089131' const württembergallee = '731084' const regensburgHbf = '8000309' +const blnOstbahnhof = '8010255' test('journeys – Berlin Schwedter Str. to München Hbf', co(function* (t) { const journeys = yield client.journeys(blnSchwedterStr, münchenHbf, { @@ -241,6 +243,19 @@ test('departures with station object', co(function* (t) { t.end() })) +test('departures at Berlin Hbf in direction of Berlin Ostbahnhof', co(function* (t) { + yield testDeparturesInDirection({ + test: t, + fetchDepartures: client.departures, + fetchTrip: client.trip, + id: berlinHbf, + directionIds: [blnOstbahnhof, '8089185', '732676'], + when, + validate + }) + t.end() +})) + test('arrivals at Berlin Schwedter Str.', co(function* (t) { const arrivals = yield client.arrivals(blnSchwedterStr, { duration: 5, when diff --git a/test/insa.js b/test/insa.js index 5250f96d..0192d544 100644 --- a/test/insa.js +++ b/test/insa.js @@ -16,6 +16,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testDeparturesInDirection = require('./lib/departures-in-direction') const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') @@ -40,6 +41,7 @@ const leiterstr = '7464' const hasselbachplatzSternstrasse = '000006545' const stendal = '008010334' const dessau = '008010077' +const universitaet = '19686' test('journeys – Magdeburg Hbf to Magdeburg-Buckau', co(function* (t) { const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { @@ -196,6 +198,19 @@ test('departures with station object', co(function* (t) { t.end() })) +test('departures at Leiterstr in direction of Universität', co(function* (t) { + yield testDeparturesInDirection({ + test: t, + fetchDepartures: client.departures, + fetchTrip: client.trip, + id: leiterstr, + directionIds: [universitaet], + when, + validate + }) + t.end() +})) + test('arrivals at Magdeburg Leiterstr.', co(function*(t) { const arrivals = yield client.arrivals(leiterstr, { duration: 5, when diff --git a/test/lib/departures-in-direction.js b/test/lib/departures-in-direction.js new file mode 100644 index 00000000..e6c0b9ff --- /dev/null +++ b/test/lib/departures-in-direction.js @@ -0,0 +1,37 @@ +'use strict' + +const co = require('./co') + +const testDeparturesInDirection = co(function* (cfg) { + const { + test: t, + fetchDepartures, + fetchTrip, + id, + directionIds, + when, + validate + } = cfg + + const deps = yield fetchDepartures(id, { + direction: directionIds[0], + when + }) + validate(t, deps, 'departures', 'departures') + t.ok(deps.length > 0, 'must be >0 departures') + + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + const name = `deps[${i}]` + + const line = dep.line && dep.line.name + const trip = yield fetchTrip(dep.tripId, line, { + when, stopovers: true + }) + t.ok(trip.stopovers.some(st => { + return st.stop && directionIds.includes(st.stop.id) + }), `trip ${dep.tripId} of ${name} has no stopover at ${directionIds}`) + } +}) + +module.exports = testDeparturesInDirection diff --git a/test/nahsh.js b/test/nahsh.js index 6c29000f..cbdd2c84 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -20,6 +20,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testDeparturesInDirection = require('./lib/departures-in-direction') const testArrivals = require('./lib/arrivals') const when = createWhen('Europe/Berlin', 'de-DE') @@ -65,6 +66,8 @@ const flensburg = '8000103' const luebeckHbf = '8000237' const husum = '8000181' const schleswig = '8005362' +const ellerbekerMarkt = '9049027' +const seefischmarkt = '9049245' test('journeys – Kiel Hbf to Flensburg', co(function* (t) { const journeys = yield client.journeys(kielHbf, flensburg, { @@ -230,6 +233,19 @@ test('departures with station object', co(function* (t) { t.end() })) +test('departures at Berlin Hbf in direction of Berlin Ostbahnhof', co(function* (t) { + yield testDeparturesInDirection({ + test: t, + fetchDepartures: client.departures, + fetchTrip: client.trip, + id: ellerbekerMarkt, + directionIds: [seefischmarkt, '710102'], + when, + validate + }) + t.end() +})) + test('arrivals at Kiel Räucherei', co(function* (t) { const kielRaeucherei = '3440091' diff --git a/test/oebb.js b/test/oebb.js index 3c2f045c..d2c665d3 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -20,6 +20,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testJourneysWithDetour = require('./lib/journeys-with-detour') +const testDeparturesInDirection = require('./lib/departures-in-direction') const when = createWhen('Europe/Vienna', 'de-AT') @@ -57,6 +58,8 @@ const wienWestbahnhof = '1291501' const klagenfurtHbf = '8100085' const muenchenHbf = '8000261' const wienRenngasse = '1390186' +const wienKarlsplatz = '1390461' +const wienPilgramgasse = '1390562' test.skip('journeys – Salzburg Hbf to Wien Westbahnhof', co(function* (t) { const journeys = yield client.journeys(salzburgHbf, wienFickeystr, { @@ -265,6 +268,19 @@ test('departures with station object', co(function* (t) { t.end() })) +test('departures at Karlsplatz in direction of Pilgramgasse', co(function* (t) { + yield testDeparturesInDirection({ + test: t, + fetchDepartures: client.departures, + fetchTrip: client.trip, + id: wienKarlsplatz, + directionIds: [wienPilgramgasse, '905002'], + when, + validate + }) + t.end() +})) + // todo: arrivals test('nearby Salzburg Hbf', co(function* (t) { diff --git a/test/vbb.js b/test/vbb.js index 3ce8c9bb..6ec9817f 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -26,6 +26,7 @@ const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') +const testDeparturesInDirection = require('./lib/departures-in-direction') const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') @@ -297,6 +298,19 @@ test('departures with station object', co(function* (t) { t.end() })) +test('departures at Spichernstr. in direction of Westhafen', co(function* (t) { + yield testDeparturesInDirection({ + test: t, + fetchDepartures: client.departures, + fetchTrip: client.trip, + id: spichernstr, + directionIds: [westhafen], + when, + validate + }) + t.end() +})) + test('departures at 7-digit station', co(function* (t) { const eisenach = '8010097' // see derhuerst/vbb-hafas#22 yield client.departures(eisenach, {when}) From 0199749a31c61c66a50559ff9540d8a2a2d763d2 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jul 2018 12:40:38 +0200 Subject: [PATCH 139/176] language option with default 'en' --- docs/departures.md | 3 ++- docs/journeys.md | 3 ++- docs/locations.md | 1 + docs/nearby.md | 3 ++- docs/radar.md | 3 ++- docs/station.md | 3 ++- docs/trip.md | 3 ++- lib/request.js | 5 ++++- 8 files changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/departures.md b/docs/departures.md index 9930b5ad..6008b524 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -28,7 +28,8 @@ With `opt`, you can override the default options, which look like this: direction: null, // only show departures heading to this station duration: 10, // show departures for the next n minutes stationLines: false, // parse & expose lines of the station? - remarks: true // parse & expose hints & warnings? + remarks: true, // parse & expose hints & warnings? + language: 'en' // language to get results in } ``` diff --git a/docs/journeys.md b/docs/journeys.md index 1e7868a2..9cabaeda 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -67,7 +67,8 @@ With `opt`, you can override the default options, which look like this: polylines: false, // return a shape for each leg? remarks: true, // parse & expose hints & warnings? // Consider walking to nearby stations at the beginning of a journey? - startWithWalking: true + startWithWalking: true, + language: 'en' // language to get results in } ``` diff --git a/docs/locations.md b/docs/locations.md index 8337823d..3d961b0b 100644 --- a/docs/locations.md +++ b/docs/locations.md @@ -12,6 +12,7 @@ With `opt`, you can override the default options, which look like this: , addresses: true , poi: true // points of interest , stationLines: false // parse & expose lines of the station? + , language: 'en' // language to get results in } ``` diff --git a/docs/nearby.md b/docs/nearby.md index c75d0f03..21eac15e 100644 --- a/docs/nearby.md +++ b/docs/nearby.md @@ -11,7 +11,8 @@ With `opt`, you can override the default options, which look like this: distance: null, // maximum walking distance in meters poi: false, // return points of interest? stations: true, // return stations? - stationLines: false // parse & expose lines of the station? + stationLines: false, // parse & expose lines of the station? + language: 'en' // language to get results in } ``` diff --git a/docs/radar.md b/docs/radar.md index d10077ac..8dea87e4 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -11,7 +11,8 @@ With `opt`, you can override the default options, which look like this: results: 256, // maximum number of vehicles duration: 30, // compute frames for the next n seconds frames: 3, // nr of frames to compute - polylines: false // return a track shape for each vehicle? + polylines: false, // return a track shape for each vehicle? + language: 'en' // language to get results in } ``` diff --git a/docs/station.md b/docs/station.md index db780f5b..f8d46f47 100644 --- a/docs/station.md +++ b/docs/station.md @@ -23,7 +23,8 @@ With `opt`, you can override the default options, which look like this: ```js { - stationLines: false // parse & expose lines of the station? + stationLines: false, // parse & expose lines of the station? + language: 'en' // language to get results in } ## Response diff --git a/docs/trip.md b/docs/trip.md index 3122dd81..000359d4 100644 --- a/docs/trip.md +++ b/docs/trip.md @@ -29,7 +29,8 @@ With `opt`, you can override the default options, which look like this: when: new Date(), stopovers: true, // return stations on the way? polyline: false, // return a shape for the trip? - remarks: true // parse & expose hints & warnings? + remarks: true, // parse & expose hints & warnings? + language: 'en' // language to get results in } ``` diff --git a/lib/request.js b/lib/request.js index 6cedd1ca..1261a180 100644 --- a/lib/request.js +++ b/lib/request.js @@ -12,7 +12,10 @@ const {fetch} = require('fetch-ponyfill')({Promise}) const md5 = input => createHash('md5').update(input).digest() const request = (profile, opt, data) => { - const body = profile.transformReqBody({lang: 'en', svcReqL: [data]}) + const body = profile.transformReqBody({ + lang: opt.language || 'en', + svcReqL: [data] + }) const req = profile.transformReq({ method: 'post', // todo: CORS? referrer policy? From 0d5a8fab1b135a645981153874df125af8824584 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jul 2018 13:12:26 +0200 Subject: [PATCH 140/176] departures, arrivals, stopovers: former scheduled platform(s) --- parse/arrival-or-departure.js | 2 +- parse/stopover.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js index 6aaa5e4e..7d731b95 100644 --- a/parse/arrival-or-departure.js +++ b/parse/arrival-or-departure.js @@ -39,7 +39,7 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { const pR = d.stbStop[prefix + 'PlatfR'] const pP = d.stbStop[prefix + 'PlatfS'] res.platform = pR || pP || null - // todo: `formerScheduledPlatform` + if (pR && pP && pR !== pP) res.formerScheduledPlatform = pP // todo: DRY with parseStopover // todo: DRY with parseJourneyLeg diff --git a/parse/stopover.js b/parse/stopover.js index 491cef21..25fc2b46 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -39,6 +39,13 @@ const createParseStopover = (profile, opt, data, date) => { res.departureDelay = Math.round((realtime - planned) / 1000) } + if (st.aPlatfR && st.aPlatfS && st.aPlatfR !== st.aPlatfS) { + res.formerScheduledArrivalPlatform = st.aPlatfS + } + if (st.dPlatfR && st.dPlatfS && st.dPlatfR !== st.dPlatfS) { + res.formerScheduledDeparturePlatform = st.dPlatfS + } + // mark stations the train passes without stopping if(st.dInS === false && st.aOutS === false) res.passBy = true From 4529a93175db240dfcb19758166257f28b9faa71 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jul 2018 19:26:15 +0200 Subject: [PATCH 141/176] parse DB-specific hints inside DB profile --- p/db/index.js | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++ parse/hint.js | 40 +-------- 2 files changed, 222 insertions(+), 36 deletions(-) diff --git a/p/db/index.js b/p/db/index.js index 345f94f6..3d4dbb72 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -1,6 +1,9 @@ 'use strict' +const trim = require('lodash/trim') + const _createParseJourney = require('../../parse/journey') +const _parseHint = require('../../parse/hint') const _formatStation = require('../../format/station') const {bike} = require('../../format/filters') @@ -76,6 +79,220 @@ const createParseJourney = (profile, opt, data) => { return parseJourneyWithPrice } +const hintsByCode = Object.assign(Object.create(null), { + fb: { + type: 'hint', + code: 'bicycle-conveyance', + summary: 'bicycles conveyed' + }, + fr: { + type: 'hint', + code: 'bicycle-conveyance-reservation', + summary: 'bicycles conveyed, subject to reservation' + }, + nf: { + type: 'hint', + code: 'no-bicycle-conveyance', + summary: 'bicycles not conveyed' + }, + k2: { + type: 'hint', + code: '2nd-class-only', + summary: '2. class only' + }, + eh: { + type: 'hint', + code: 'boarding-ramp', + summary: 'vehicle-mounted boarding ramp available' + }, + ro: { + type: 'hint', + code: 'wheelchairs-space', + summary: 'space for wheelchairs' + }, + oa: { + type: 'hint', + code: 'wheelchairs-space-reservation', + summary: 'space for wheelchairs, subject to reservation' + }, + wv: { + type: 'hint', + code: 'wifi', + summary: 'WiFi available' + }, + wi: { + type: 'hint', + code: 'wifi', + summary: 'WiFi available' + }, + sn: { + type: 'hint', + code: 'snacks', + summary: 'snacks available for purchase' + }, + mb: { + type: 'hint', + code: 'snacks', + summary: 'snacks available for purchase' + }, + mp: { + type: 'hint', + code: 'snacks', + summary: 'snacks available for purchase at the seat' + }, + bf: { + type: 'hint', + code: 'barrier-free', + summary: 'barrier-free' + }, + rg: { + type: 'hint', + code: 'barrier-free-vehicle', + summary: 'barrier-free vehicle' + }, + bt: { + type: 'hint', + code: 'on-board-bistro', + summary: 'Bordbistro available' + }, + br: { + type: 'hint', + code: 'on-board-restaurant', + summary: 'Bordrestaurant available' + }, + ki: { + type: 'hint', + code: 'childrens-area', + summary: `children's area available` + }, + kk: { + type: 'hint', + code: 'parents-childrens-compartment', + summary: `parent-and-children compartment available` + }, + kr: { + type: 'hint', + code: 'kids-service', + summary: 'DB Kids Service available' + }, + ls: { + type: 'hint', + code: 'power-sockets', + summary: 'power sockets available' + }, + ev: { + type: 'hint', + code: 'replacement-service', + summary: 'replacement service' + }, + kl: { + type: 'hint', + code: 'air-conditioned', + summary: 'air-conditioned vehicle' + }, + r0: { + type: 'hint', + code: 'upward-escalator', + summary: 'upward escalator' + }, + au: { + type: 'hint', + code: 'elevator', + summary: 'elevator available' + }, + ck: { + type: 'hint', + code: 'komfort-checkin', + summary: 'Komfort-Checkin available' + }, + it: { + type: 'hint', + code: 'ice-sprinter', + summary: 'ICE Sprinter service' + }, + rp: { + type: 'hint', + code: 'compulsory-reservation', + summary: 'compulsory seat reservation' + }, + rm: { + type: 'hint', + code: 'optional-reservation', + summary: 'optional seat reservation' + }, + scl: { + type: 'hint', + code: 'all-2nd-class-seats-reserved', + summary: 'all 2nd class seats reserved' + }, + acl: { + type: 'hint', + code: 'all-seats-reserved', + summary: 'all seats reserved' + }, + sk: { + type: 'hint', + code: 'oversize-luggage-forbidden', + summary: 'oversize luggage not allowed' + }, + hu: { + type: 'hint', + code: 'animals-forbidden', + summary: 'animals not allowed, except guide dogs' + }, + ik: { + type: 'hint', + code: 'baby-cot-required', + summary: 'baby cot/child seat required' + }, + ee: { + type: 'hint', + code: 'on-board-entertainment', + summary: 'on-board entertainment available' + }, + toilet: { + type: 'hint', + code: 'toilet', + summary: 'toilet available' + }, + oc: { + type: 'hint', + code: 'wheelchair-accessible-toilet', + summary: 'wheelchair-accessible toilet available' + }, + iz: { + type: 'hint', + code: 'intercity-2', + summary: 'Intercity 2' + } +}) + +const codesByText = Object.assign(Object.create(null), { + 'journey cancelled': 'journey-cancelled', // todo: German variant + 'stop cancelled': 'stop-cancelled', // todo: change to `stopover-cancelled`, German variant + 'signal failure': 'signal-failure', + 'signalstörung': 'signal-failure', + 'additional stop': 'additional-stopover', // todo: German variant + 'platform change': 'changed platform', // todo: use dash, German variant +}) + +const parseHint = (profile, h, icons) => { + if (h.type === 'A') { + const hint = hintsByCode[h.code && h.code.trim().toLowerCase()] + if (hint) { + return Object.assign({text: h.txtN}, hint) + } + } + + const res = _parseHint(profile, h, icons) + if (h.txtN) { + console.error(h) + const text = trim(h.txtN.toLowerCase(), ' ()') + if (codesByText[text]) res.code = codesByText[text] + } + return res +} + const isIBNR = /^\d{6,}$/ const formatStation = (id) => { if (!isIBNR.test(id)) throw new Error('station ID must be an IBNR.') @@ -99,6 +316,7 @@ const dbProfile = { // todo: parseLocation parseJourney: createParseJourney, + parseHint, formatStation, diff --git a/parse/hint.js b/parse/hint.js index eebcad90..7dd6f045 100644 --- a/parse/hint.js +++ b/parse/hint.js @@ -1,7 +1,5 @@ 'use strict' -const trim = require('lodash/trim') - const hints = Object.assign(Object.create(null), { fb: { type: 'hint', @@ -218,36 +216,6 @@ const parseHint = (profile, h, icons) => { } } - // todo: find sth more reliable than this - if (h.type === 'P' && text.toLowerCase() === 'journey cancelled') { - return { - type: 'status', - code: 'journey-cancelled', - text - // todo: `h.sIdx` - } - } - if (h.type === 'U' && text.toLowerCase() === 'stop cancelled') { - return { - type: 'status', - code: 'stop-cancelled', // todo: change to stopover-cancelled - text - } - } - if (h.type === 'U' && trim(text.toLowerCase(), ' ()') === 'additional stop') { - return { - type: 'status', - code: 'additional-stopover', - text - } - } - if (h.type === 'G' && text.toLowerCase() === 'platform change') { - return { - type: 'status', - code: 'changed platform', - text - } - } if (h.type === 'L') { return { type: 'status', @@ -257,11 +225,11 @@ const parseHint = (profile, h, icons) => { } } if (h.type === 'A') { - const hint = hints[h.code && h.code.trim().toLowerCase()] - if (hint) { - return Object.assign({text: h.txtN}, hint) + return { + type: 'hint', + code: h.code || null, + text: h.txtN || null } - return null } if (h.type === 'D' || h.type === 'U' || h.type === 'R' || h.type === 'N') { From efc6467c9319f26798d53c42a4443bc0f7e3cc2e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jul 2018 19:46:13 +0200 Subject: [PATCH 142/176] tests: validate former scheduled platforms :white_check_mark: --- test/lib/validators.js | 12 +- test/vbn.js | 255 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 test/vbn.js diff --git a/test/lib/validators.js b/test/lib/validators.js index e6ff13e5..3760a29c 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -104,11 +104,21 @@ const createValidateStopover = (cfg) => { a.strictEqual(typeof s.arrivalPlatform, 'string', msg + 'be a string') a.ok(s.arrivalPlatform, msg + 'not be empty') } - if (is(s.departureDelay)) { + if (is(s.formerScheduledArrivalPlatform)) { + const msg = name + '.formerScheduledArrivalPlatform must ' + a.strictEqual(typeof s.formerScheduledArrivalPlatform, 'string', msg + 'be a string') + a.ok(s.formerScheduledArrivalPlatform, msg + 'not be empty') + } + if (is(s.departurePlatform)) { const msg = name + '.departurePlatform must ' a.strictEqual(typeof s.departurePlatform, 'string', msg + 'be a string') a.ok(s.departurePlatform, msg + 'not be empty') } + if (is(s.formerScheduledDeparturePlatform)) { + const msg = name + '.formerScheduledDeparturePlatform must ' + a.strictEqual(typeof s.formerScheduledDeparturePlatform, 'string', msg + 'be a string') + a.ok(s.formerScheduledDeparturePlatform, msg + 'not be empty') + } val.station(val, s.stop, name + '.stop') } diff --git a/test/vbn.js b/test/vbn.js new file mode 100644 index 00000000..93f6638e --- /dev/null +++ b/test/vbn.js @@ -0,0 +1,255 @@ +'use strict' + +const tapePromise = require('tape-promise').default +const tape = require('tape') +const isRoughlyEqual = require('is-roughly-equal') + +const {createWhen} = require('./lib/util') +const co = require('./lib/co') +const createClient = require('..') +const vbnProfile = require('../p/vbn') +const products = require('../p/vbn/products') +const createValidate = require('./lib/validate-fptf-with') +const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testJourneysStationToAddress = require('./lib/journeys-station-to-address') +const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') +const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') +const testDepartures = require('./lib/departures') +const testArrivals = require('./lib/arrivals') +const testJourneysWithDetour = require('./lib/journeys-with-detour') + +const when = createWhen('Europe/Berlin', 'de-DE') + +const cfg = { + when, + stationCoordsOptional: false, + products +} + +const validate = createValidate(cfg, {}) + +const test = tapePromise(tape) +const client = createClient(vbnProfile) + +const bremenHbf = '8000050' +const bremerhavenHbf = '8000051' + +test.only('journeys – Bremen Hbf to Bremerhaven Hbf', co(function* (t) { + const journeys = yield client.journeys(bremenHbf, bremerhavenHbf, { + results: 3, + departure: when, + stopovers: true + }) + + yield testJourneysStationToStation({ + test: t, + journeys, + validate, + fromId: bremenHbf, + toId: bremerhavenHbf + }) + t.end() +})) + +// todo: journeys, only one product + +test.skip('journeys – fails with no product', (t) => { + journeysFailsWithNoProduct({ + test: t, + fetchJourneys: client.journeys, + fromId: bremenHbf, + toId: bremerhavenHbf, + when, + products + }) + t.end() +}) + +test.skip('Magdeburg Hbf to 39104 Magdeburg, Sternstr. 10', co(function*(t) { + const sternStr = { + type: 'location', + address: 'Magdeburg - Altenstadt, Sternstraße 10', + latitude: 52.118414, + longitude: 11.422332 + } + + const journeys = yield client.journeys(bremenHbf, sternStr, { + results: 3, + departure: when + }) + + yield testJourneysStationToAddress({ + test: t, + journeys, + validate, + fromId: bremenHbf, + to: sternStr + }) + t.end() +})) + +test.skip('Magdeburg Hbf to Kloster Unser Lieben Frauen', co(function*(t) { + const kloster = { + type: 'location', + id: '970012223', + name: 'Magdeburg, Kloster Unser Lieben Frauen (Denkmal)', + latitude: 52.127601, + longitude: 11.636437 + } + const journeys = yield client.journeys(bremenHbf, kloster, { + results: 3, + departure: when + }) + + yield testJourneysStationToPoi({ + test: t, + journeys, + validate, + fromId: bremenHbf, + to: kloster + }) + t.end() +})) + +test.skip('journeys: via works – with detour', co(function* (t) { + // Going from Magdeburg, Hasselbachplatz (Sternstr.) (Tram/Bus) to Stendal + // via Dessau without detour is currently impossible. We check if the routing + // engine computes a detour. + const journeys = yield client.journeys(hasselbachplatzSternstrasse, stendal, { + via: dessau, + results: 1, + departure: when, + stopovers: true + }) + + yield testJourneysWithDetour({ + test: t, + journeys, + validate, + detourIds: ['8010077', dessau] // todo: trim IDs + }) + t.end() +})) + +// todo: without detour + +test.skip('earlier/later journeys', co(function* (t) { + yield testEarlierLaterJourneys({ + test: t, + fetchJourneys: client.journeys, + validate, + fromId: bremenHbf, + toId: bremerhavenHbf + }) + + t.end() +})) + +test.skip('trip details', co(function* (t) { + const journeys = yield client.journeys(bremenHbf, bremerhavenHbf, { + results: 1, departure: when + }) + + const p = journeys[0].legs[0] + t.ok(p.tripId, 'precondition failed') + t.ok(p.line.name, 'precondition failed') + const trip = yield client.trip(p.tripId, p.line.name, {when}) + + validate(t, trip, 'journeyLeg', 'trip') + t.end() +})) + +test.skip('departures at Magdeburg Leiterstr.', co(function*(t) { + const departures = yield client.departures(leiterstr, { + duration: 5, when + }) + + yield testDepartures({ + test: t, + departures, + validate, + id: leiterstr + }) + t.end() +})) + +test.skip('departures with station object', co(function* (t) { + const deps = yield client.departures({ + type: 'station', + id: bremenHbf, + name: 'Magdeburg Hbf', + location: { + type: 'location', + latitude: 1.23, + longitude: 2.34 + } + }, {when}) + + validate(t, deps, 'departures', 'departures') + t.end() +})) + +test.skip('arrivals at Magdeburg Leiterstr.', co(function*(t) { + const arrivals = yield client.arrivals(leiterstr, { + duration: 5, when + }) + + yield testArrivals({ + test: t, + arrivals, + validate, + id: leiterstr + }) + t.end() +})) + +// todo: nearby + +test.skip('locations named Magdeburg', co(function*(t) { + const locations = yield client.locations('Magdeburg', { + results: 20 + }) + + validate(t, locations, 'locations', 'locations') + t.ok(locations.length <= 20) + + t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.id && s.name)) // POIs + t.ok(locations.some((loc) => { + return ( + loc.id === '008010224' || // todo: trim IDs + loc.id === bremenHbf + ) + })) + + t.end() +})) + +test.skip('station Magdeburg-Buckau', co(function* (t) { + const s = yield client.station(bremerhavenHbf) + + validate(t, s, 'station', 'station') + t.equal(s.id, bremerhavenHbf) + + t.end() +})) + +test.skip('radar', co(function* (t) { + const vehicles = yield client.radar({ + north: 52.148364, + west: 11.600826, + south: 52.108486, + east: 11.651451 + }, { + duration: 5 * 60, when, results: 10 + }) + + const customCfg = Object.assign({}, cfg, { + stationCoordsOptional: true, // see #28 + }) + const validate = createValidate(customCfg, {}) + validate(t, vehicles, 'movements', 'vehicles') + + t.end() +})) From 90519cff6830f80e154872726b7dddbadf2e8f7c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 9 Jul 2018 19:48:12 +0200 Subject: [PATCH 143/176] remove accidental logging [ci skip] --- p/db/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/p/db/index.js b/p/db/index.js index 3d4dbb72..8bf9e98d 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -286,7 +286,6 @@ const parseHint = (profile, h, icons) => { const res = _parseHint(profile, h, icons) if (h.txtN) { - console.error(h) const text = trim(h.txtN.toLowerCase(), ' ()') if (codesByText[text]) res.code = codesByText[text] } From 1551943fdb4481dc1f22bb33e670d14ae07d0b7c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 10 Jul 2018 18:59:54 +0200 Subject: [PATCH 144/176] option: departures/arrivals at related stations With default `true` - because it seems to be the sensible default configuration - to stay backwards-compatible --- index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 752b2b6f..5175cdd0 100644 --- a/index.js +++ b/index.js @@ -35,7 +35,10 @@ const createClient = (profile, request = _request) => { direction: null, // only show departures heading to this station duration: 10, // show departures for the next n minutes stationLines: false, // parse & expose lines of the station? - remarks: true // parse & expose hints & warnings? + remarks: true, // parse & expose hints & warnings? + // departures at related stations + // e.g. those that belong together on the metro map. + includeRelatedStations: true }, opt) opt.when = new Date(opt.when || Date.now()) if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') @@ -52,7 +55,8 @@ const createClient = (profile, request = _request) => { dirLoc: dir, jnyFltrL: [products], dur: opt.duration, - getPasslist: false // todo: what is this? + getPasslist: false, // todo: what is this? + stbFltrEquiv: !opt.includeRelatedStations } }) .then((d) => { From a2cd5ba187717a6a53f86e92c67908bc89533a91 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 10 Jul 2018 19:01:40 +0200 Subject: [PATCH 145/176] docs & changelog for `includeRelatedStations` option :memo: see 1551943 --- docs/changelog.md | 1 + docs/departures.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index c42ce562..2fcbe9ea 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -13,6 +13,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 84bce0c `arrivals()`/`departures()`: parse & expose platforms - 85e0bdf `journeys()`: `startWithWalking` option with default `true` - f6ae29c journey legs with `type: 'walking'` now have a `distance` in meters +- 1551943 `arrivals()`/`departures()`: `includeRelatedStations` option with default `true` ### breaking changes 💥 diff --git a/docs/departures.md b/docs/departures.md index 6008b524..3fa9dac6 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -29,6 +29,9 @@ With `opt`, you can override the default options, which look like this: duration: 10, // show departures for the next n minutes stationLines: false, // parse & expose lines of the station? remarks: true, // parse & expose hints & warnings? + // departures at related stations + // e.g. those that belong together on the metro map. + includeRelatedStations: true, language: 'en' // language to get results in } ``` From c0a04fc74f2c903a75c64cd58029363dd4993e4d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 10 Jul 2018 22:33:52 +0200 Subject: [PATCH 146/176] tests for `includeRelatedStations` option :white_check_mark: see 1551943 --- test/db.js | 13 ++++++ .../departures-without-related-stations.js | 41 +++++++++++++++++++ test/vbb.js | 13 ++++++ 3 files changed, 67 insertions(+) create mode 100644 test/lib/departures-without-related-stations.js diff --git a/test/db.js b/test/db.js index f714b207..7b1fb76f 100644 --- a/test/db.js +++ b/test/db.js @@ -23,6 +23,7 @@ const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testDeparturesInDirection = require('./lib/departures-in-direction') +const testDeparturesWithoutRelatedStations = require('./lib/departures-without-related-stations') const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') @@ -256,6 +257,18 @@ test('departures at Berlin Hbf in direction of Berlin Ostbahnhof', co(function* t.end() })) +test('departures without related stations', co(function* (t) { + yield testDeparturesWithoutRelatedStations({ + test: t, + fetchDepartures: client.departures, + id: '8089051', // Berlin Yorckstr. (S1) + when, + products: {bus: false}, + linesOfRelatedStations: ['S 2', 'S 25', 'S 26', 'U 7'] + }) + t.end() +})) + test('arrivals at Berlin Schwedter Str.', co(function* (t) { const arrivals = yield client.arrivals(blnSchwedterStr, { duration: 5, when diff --git a/test/lib/departures-without-related-stations.js b/test/lib/departures-without-related-stations.js new file mode 100644 index 00000000..02c60f57 --- /dev/null +++ b/test/lib/departures-without-related-stations.js @@ -0,0 +1,41 @@ +'use strict' + +const co = require('./co') + +const testDeparturesWithoutUnrelatedStations = co(function* (cfg) { + const { + test: t, + fetchDepartures, + id, + when + // duration, products + } = cfg + + const relatedLines = cfg.linesOfRelatedStations + .map(lName => lName.toLowerCase().trim()) + + const isUnrelatedLine = (dep) => { + if (!dep.line || !dep.line.name) return false + return relatedLines.includes(dep.line.name.toLowerCase().trim()) + } + + const depsWith = yield fetchDepartures(id, { + when, + duration: cfg.duration || 20, + products: cfg.products || {} + }) + t.ok(depsWith.some(isUnrelatedLine), 'precondition failed: no line at related station found') + + const depsWithout = yield fetchDepartures(id, { + includeRelatedStations: false, + when, + duration: cfg.duration || 20, + products: cfg.products || {} + }) + + const unrelatedDep = depsWithout.find(isUnrelatedLine) + if (unrelatedDep) t.fail('line at related station: ' + unrelatedDep.line.name) + else t.pass('no lines from related stations') +}) + +module.exports = testDeparturesWithoutUnrelatedStations diff --git a/test/vbb.js b/test/vbb.js index 6ec9817f..0a657acf 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -27,6 +27,7 @@ const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testDeparturesInDirection = require('./lib/departures-in-direction') +const testDeparturesWithoutRelatedStations = require('./lib/departures-without-related-stations') const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') @@ -318,6 +319,18 @@ test('departures at 7-digit station', co(function* (t) { t.end() })) +test('departures without related stations', co(function* (t) { + yield testDeparturesWithoutRelatedStations({ + test: t, + fetchDepartures: client.departures, + id: '900000024101', // Charlottenburg + when, + products: {bus: false, suburban: false, regional: false}, + linesOfRelatedStations: ['U7'] + }) + t.end() +})) + test('arrivals', co(function* (t) { const arrivals = yield client.arrivals(spichernstr, { duration: 5, when From fe8e68e4a2544c7dbf9792b3882fb307c82f75e3 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 13 Jul 2018 12:17:15 +0200 Subject: [PATCH 147/176] fix DB hints parsing :bug: --- p/db/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/db/index.js b/p/db/index.js index 8bf9e98d..2e49a675 100644 --- a/p/db/index.js +++ b/p/db/index.js @@ -285,7 +285,7 @@ const parseHint = (profile, h, icons) => { } const res = _parseHint(profile, h, icons) - if (h.txtN) { + if (res && h.txtN) { const text = trim(h.txtN.toLowerCase(), ' ()') if (codesByText[text]) res.code = codesByText[text] } From a475f2048df447d323bd3c7c2f5124630b032d43 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 10 Jul 2018 23:32:34 +0200 Subject: [PATCH 148/176] parse stops and their stations --- lib/request.js | 12 +++++++++++- p/oebb/index.js | 2 +- p/vbb/index.js | 2 +- parse/location.js | 12 ++++++------ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/request.js b/lib/request.js index 1261a180..709fa483 100644 --- a/lib/request.js +++ b/lib/request.js @@ -104,8 +104,18 @@ const request = (profile, opt, data) => { d.lines = c.prodL.map(parse) } if (Array.isArray(c.locL)) { - const parse = loc => profile.parseLocation(profile, opt, {lines: d.lines}, loc) + const data = {lines: d.lines} + const parse = loc => profile.parseLocation(profile, opt, data, loc) + d.locations = c.locL.map(parse) + for (let i = 0; i < d.locations.length; i++) { + const raw = c.locL[i] + const loc = d.locations[i] + if ('number' === typeof raw.mMastLocX) { + loc.station = Object.assign({}, d.locations[raw.mMastLocX]) + loc.station.type = 'station' + } else if (raw.isMainMast) loc.type = 'station' + } } return d }) diff --git a/p/oebb/index.js b/p/oebb/index.js index c6fbb1e7..2dd24b49 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -30,7 +30,7 @@ const parseLocation = (profile, opt, data, l) => { // like station entrances, that are actually POIs. const res = _parseLocation(profile, opt, data, l) if ( - res.type === 'station' && + (res.type === 'station' || res.type === 'stop') && !res.products && res.name && res.id && res.id.length !== 7 diff --git a/p/vbb/index.js b/p/vbb/index.js index cfb14373..18909ded 100644 --- a/p/vbb/index.js +++ b/p/vbb/index.js @@ -45,7 +45,7 @@ const createParseLine = (profile, opt, data) => { const parseLocation = (profile, opt, data, l) => { const res = _parseLocation(profile, opt, data, l) - if (res.type === 'station') { + if (res.type === 'stop' || res.type === 'station') { res.name = shorten(res.name) res.id = to12Digit(res.id) if (!res.location.latitude || !res.location.longitude) { diff --git a/parse/location.js b/parse/location.js index 2e53f431..2283ef2b 100644 --- a/parse/location.js +++ b/parse/location.js @@ -13,28 +13,28 @@ const parseLocation = (profile, opt, {lines}, l) => { } if (l.type === STATION) { - const station = { - type: 'station', + const stop = { + type: l.isMainMast ? 'station' : 'stop', id: l.extId, name: l.name ? profile.parseStationName(l.name) : null, location: 'number' === typeof res.latitude ? res : null } - if ('pCls' in l) station.products = profile.parseProducts(l.pCls) + if ('pCls' in l) stop.products = profile.parseProducts(l.pCls) if ( opt.stationLines && Array.isArray(l.pRefL) && Array.isArray(lines) ) { - station.lines = [] + stop.lines = [] for (let pRef of l.pRefL) { const line = lines[pRef] - if (line) station.lines.push(line) + if (line) stop.lines.push(line) } } - return station + return stop } if (l.type === ADDRESS) res.address = l.name From 59fee112168f53980ed9ae25b12b1b04c1b116dd Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 11 Jul 2018 00:32:57 +0200 Subject: [PATCH 149/176] adapt docs to 98a051b :memo: --- docs/locations.md | 2 +- docs/nearby.md | 6 +++--- docs/radar.md | 6 +++--- docs/station.md | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/locations.md b/docs/locations.md index 3d961b0b..3abdc58e 100644 --- a/docs/locations.md +++ b/docs/locations.md @@ -35,7 +35,7 @@ The response may look like this: ```js [ { - type: 'station', + type: 'stop', id: '900000100003', name: 'S+U Alexanderplatz', location: { diff --git a/docs/nearby.md b/docs/nearby.md index 21eac15e..33352922 100644 --- a/docs/nearby.md +++ b/docs/nearby.md @@ -39,7 +39,7 @@ The response may look like this: ```js [ { - type: 'station', + type: 'stop', id: '900000120001', name: 'S+U Frankfurter Allee', location: { @@ -58,7 +58,7 @@ The response may look like this: }, distance: 56 }, { - type: 'station', + type: 'stop', id: '900000120540', name: 'Scharnweberstr./Weichselstr.', location: { @@ -69,7 +69,7 @@ The response may look like this: products: { /* … */ }, distance: 330 }, { - type: 'station', + type: 'stop', id: '900000160544', name: 'Rathaus Lichtenberg', location: { diff --git a/docs/radar.md b/docs/radar.md index 8dea87e4..9e8a21e3 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -69,7 +69,7 @@ The response may look like this: trip: 31463, nextStops: [ { station: { - type: 'station', + type: 'stop', id: '900000029101', name: 'S Spandau', location: { @@ -94,14 +94,14 @@ The response may look like this: } /* … */ ], frames: [ { origin: { - type: 'station', + type: 'stop', id: '900000100003', name: 'S+U Alexanderplatz', location: { /* … */ }, products: { /* … */ } }, destination: { - type: 'station', + type: 'stop', id: '900000100004', name: 'S+U Jannowitzbrücke', location: { /* … */ }, diff --git a/docs/station.md b/docs/station.md index f8d46f47..b329245f 100644 --- a/docs/station.md +++ b/docs/station.md @@ -46,7 +46,7 @@ The response may look like this: ```js { - type: 'station', + type: 'stop', id: '900000042101', name: 'U Spichernstr.', location: { From 951c21eecc1f451be2d10c23b82ba95bc2baf3f6 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 11 Jul 2018 13:25:22 +0200 Subject: [PATCH 150/176] adapt tests to 98a051b --- test/db.js | 5 ++++- test/insa.js | 15 +++++++------- test/lib/arrivals.js | 10 ++++++--- test/lib/departures-in-direction.js | 7 ++++--- test/lib/departures.js | 10 ++++++--- test/lib/journeys-station-to-poi.js | 27 ++++++++++++++++--------- test/lib/journeys-station-to-station.js | 10 +++++---- test/lib/journeys-with-detour.js | 7 ++++--- test/lib/validators.js | 22 +++++++++++++++----- test/nahsh.js | 9 +++++---- test/oebb.js | 19 +++++++++++++---- test/vbb.js | 4 ++-- test/vbn.js | 13 ++++++------ 13 files changed, 104 insertions(+), 54 deletions(-) diff --git a/test/db.js b/test/db.js index 7b1fb76f..5448ccfd 100644 --- a/test/db.js +++ b/test/db.js @@ -317,6 +317,9 @@ test('locations named Jungfernheide', co(function* (t) { t.ok(locations.length <= 10) t.ok(locations.some((l) => { // todo: trim IDs + if (l.station) { + if (l.station.id === '008011167' || l.station.id === jungfernheide) return true + } return l.id === '008011167' || l.id === jungfernheide }), 'Jungfernheide not found') @@ -326,7 +329,7 @@ test('locations named Jungfernheide', co(function* (t) { test('station', co(function* (t) { const s = yield client.station(regensburgHbf) - validate(t, s, 'station', 'station') + validate(t, s, ['stop', 'station'], 'station') t.equal(s.id, regensburgHbf) t.end() diff --git a/test/insa.js b/test/insa.js index 0192d544..2a6fe169 100644 --- a/test/insa.js +++ b/test/insa.js @@ -235,13 +235,14 @@ test('locations named Magdeburg', co(function*(t) { validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 20) - t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.type === 'stop' || s.type === 'station')) t.ok(locations.find(s => s.id && s.name)) // POIs - t.ok(locations.some((loc) => { - return ( - loc.id === '008010224' || // todo: trim IDs - loc.id === magdeburgHbf - ) + t.ok(locations.some((l) => { + // todo: trim IDs + if (l.station) { + if (l.station.id === '008010224' || l.station.id === magdeburgHbf) return true + } + return l.id === '008010224' || l.id === magdeburgHbf })) t.end() @@ -250,7 +251,7 @@ test('locations named Magdeburg', co(function*(t) { test('station Magdeburg-Buckau', co(function* (t) { const s = yield client.station(magdeburgBuckau) - validate(t, s, 'station', 'station') + validate(t, s, ['stop', 'station'], 'station') t.equal(s.id, magdeburgBuckau) t.end() diff --git a/test/lib/arrivals.js b/test/lib/arrivals.js index 9483514a..4a21771e 100644 --- a/test/lib/arrivals.js +++ b/test/lib/arrivals.js @@ -8,10 +8,14 @@ const testArrivals = co(function* (cfg) { validate(t, arrs, 'arrivals', 'arrivals') t.ok(arrs.length > 0, 'must be >0 arrivals') for (let i = 0; i < arrs.length; i++) { - const dep = arrs[i] - const name = `arrs[${i}]` + let station = arrs[i].station + let name = `arrs[${i}].station` + if (station.station) { + station = station.station + name += '.station' + } - t.equal(dep.station.id, id, name + '.station.id is invalid') + t.equal(station.id, id, name + '.id is invalid') } // todo: move into arrivals validator diff --git a/test/lib/departures-in-direction.js b/test/lib/departures-in-direction.js index e6c0b9ff..bc82a790 100644 --- a/test/lib/departures-in-direction.js +++ b/test/lib/departures-in-direction.js @@ -28,9 +28,10 @@ const testDeparturesInDirection = co(function* (cfg) { const trip = yield fetchTrip(dep.tripId, line, { when, stopovers: true }) - t.ok(trip.stopovers.some(st => { - return st.stop && directionIds.includes(st.stop.id) - }), `trip ${dep.tripId} of ${name} has no stopover at ${directionIds}`) + t.ok(trip.stopovers.some(st => ( + st.stop.station && directionIds.includes(st.stop.station.id) || + directionIds.includes(st.stop.id) + )), `trip ${dep.tripId} of ${name} has no stopover at ${directionIds}`) } }) diff --git a/test/lib/departures.js b/test/lib/departures.js index 1da624a6..f73015fc 100644 --- a/test/lib/departures.js +++ b/test/lib/departures.js @@ -8,10 +8,14 @@ const testDepartures = co(function* (cfg) { validate(t, deps, 'departures', 'departures') t.ok(deps.length > 0, 'must be >0 departures') for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - const name = `deps[${i}]` + let station = deps[i].station + let name = `deps[${i}].station` + if (station.station) { + station = station.station + name += '.station' + } - t.equal(dep.station.id, id, name + '.station.id is invalid') + t.equal(station.id, id, name + '.id is invalid') } // todo: move into deps validator diff --git a/test/lib/journeys-station-to-poi.js b/test/lib/journeys-station-to-poi.js index e996dc37..ca7fe1da 100644 --- a/test/lib/journeys-station-to-poi.js +++ b/test/lib/journeys-station-to-poi.js @@ -13,17 +13,26 @@ const testJourneysStationToPoi = co(function* (cfg) { for (let i = 0; i < journeys.length; i++) { const j = journeys[i] - const firstLeg = j.legs[0] - t.strictEqual(firstLeg.origin.id, fromId) + let o = j.legs[0].origin + let oN = `journeys[0].legs[0].destination` + if (o.station) { + o = o.station + oN += '.station' + } + t.strictEqual(o.id, fromId) - const d = j.legs[j.legs.length - 1].destination - const n = `journeys[0].legs[${i}].destination` + let d = j.legs[j.legs.length - 1].destination + let dN = `journeys[${i}].legs[${j.legs.length - 1}].destination` + if (d.station) { + d = d.station + dN += '.station' + } - t.strictEqual(d.type, 'location', n + '.type is invalid') - t.strictEqual(d.id, id, n + '.id is invalid') - t.strictEqual(d.name, name, n + '.name is invalid') - t.ok(isRoughlyEqual(.0001, d.latitude, latitude), n + '.latitude is invalid') - t.ok(isRoughlyEqual(.0001, d.longitude, longitude), n + '.longitude is invalid') + t.strictEqual(d.type, 'location', dN + '.type is invalid') + t.strictEqual(d.id, id, dN + '.id is invalid') + t.strictEqual(d.name, name, dN + '.name is invalid') + t.ok(isRoughlyEqual(.0001, d.latitude, latitude), dN + '.latitude is invalid') + t.ok(isRoughlyEqual(.0001, d.longitude, longitude), dN + '.longitude is invalid') } }) diff --git a/test/lib/journeys-station-to-station.js b/test/lib/journeys-station-to-station.js index 8d44fce4..5cc2ef58 100644 --- a/test/lib/journeys-station-to-station.js +++ b/test/lib/journeys-station-to-station.js @@ -10,10 +10,12 @@ const testJourneysStationToStation = co(function* (cfg) { for (let i = 0; i < journeys.length; i++) { const j = journeys[i] - const firstLeg = j.legs[0] - const lastLeg = j.legs[j.legs.length - 1] - t.strictEqual(firstLeg.origin.id, fromId) - t.strictEqual(lastLeg.destination.id, toId) + let origin = j.legs[0].origin + if (origin.station) origin = origin.station + let dest = j.legs[j.legs.length - 1].destination + if (dest.station) dest = dest.station + t.strictEqual(origin.id, fromId) + t.strictEqual(dest.id, toId) } }) diff --git a/test/lib/journeys-with-detour.js b/test/lib/journeys-with-detour.js index 9b725a9b..049f103c 100644 --- a/test/lib/journeys-with-detour.js +++ b/test/lib/journeys-with-detour.js @@ -11,9 +11,10 @@ const testJourneysWithDetour = co(function* (cfg) { validate(t, journeys, 'journeys', 'journeys') const leg = journeys[0].legs.some((leg) => { - return leg.stopovers && leg.stopovers.some((stopover) => { - return detourIds.includes(stopover.stop.id) - }) + return leg.stopovers && leg.stopovers.some((st) => ( + st.stop.station && detourIds.includes(st.stop.station.id) || + detourIds.includes(st.stop.id) + )) }) t.ok(leg, detourIds.join('/') + ' is not being passed') }) diff --git a/test/lib/validators.js b/test/lib/validators.js index 3760a29c..3a91d28f 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -32,6 +32,16 @@ const createValidateStation = (cfg) => { return validateStation } + +const validateStop = (val, s, name = 'stop') => { + // HAFAS doesn't always return the station of a stop. We mock it here + // to silence `validate-fptf`. + const station = Object.assign({}, s) + station.type = 'station' + s = Object.assign({station}, s) + defaultValidators.stop(val, s, name) +} + const validatePoi = (val, poi, name = 'location') => { defaultValidators.location(val, poi, name) val.ref(val, poi.id, name + '.id') @@ -46,7 +56,8 @@ const validateAddress = (val, addr, name = 'location') => { const validateLocation = (val, loc, name = 'location') => { a.ok(isObj(loc), name + ' must be an object') - if (loc.type === 'station') val.station(val, loc, name) + if (loc.type === 'stop') val.stop(val, loc, name) + else if (loc.type === 'station') val.station(val, loc, name) else if ('id' in loc) validatePoi(val, loc, name) else if (!('name' in loc) && ('address' in loc)) { validateAddress(val, loc, name) @@ -120,7 +131,7 @@ const createValidateStopover = (cfg) => { a.ok(s.formerScheduledDeparturePlatform, msg + 'not be empty') } - val.station(val, s.stop, name + '.stop') + anyOf(['stop', 'station'], val, s.stop, name + '.stop') } return validateStopover } @@ -251,7 +262,7 @@ const createValidateArrivalOrDeparture = (cfg) => { a.ok(dep.tripId, name + '.tripId must not be empty') a.strictEqual(typeof dep.trip, 'number', name + '.trip must be a number') - val.station(val, dep.station, name + '.station') + anyOf(['stop', 'station'], val, dep.station, name + '.station') assertValidWhen(dep.when, cfg.when, name) if (dep.delay !== null) { @@ -315,8 +326,8 @@ const validateMovement = (val, m, name = 'movement') => { const fName = name + `.frames[${i}]` a.ok(isObj(f), fName + ' must be an object') - anyOf(['location', 'station'], val, f.origin, fName + '.origin') - anyOf(['location', 'station'], val, f.destination, fName + '.destination') + anyOf(['location', 'stop', 'station'], val, f.origin, fName + '.origin') + anyOf(['location', 'stop', 'station'], val, f.destination, fName + '.destination') a.strictEqual(typeof f.t, 'number', fName + '.frames must be a number') } @@ -333,6 +344,7 @@ const validateMovements = (val, ms, name = 'movements') => { module.exports = { station: createValidateStation, + stop: () => validateStop, location: () => validateLocation, locations: () => validateLocations, poi: () => validatePoi, diff --git a/test/nahsh.js b/test/nahsh.js index cbdd2c84..2dc02879 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -164,7 +164,8 @@ test('Husum to Lübeck Hbf with stopover at Kiel Hbf', co(function* (t) { const leg = journeys[0].legs.some((leg) => { return leg.stopovers && leg.stopovers.some((stopover) => { - return stopover.stop.id === kielHbf + const s = stopover.stop + return s.station && s.station.id === kielHbf || s.id === kielHbf }) }) t.ok(leg, 'Kiel Hbf is not being passed') @@ -293,9 +294,9 @@ test('locations named Kiel', co(function* (t) { validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 20) - t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.type === 'stop' || s.type === 'station')) t.ok(locations.find(s => s.id && s.name)) // POIs - t.ok(locations.some(l => l.id === kielHbf)) + t.ok(locations.some(l => l.station && s.station.id === kielHbf || l.id === kielHbf)) t.end() })) @@ -303,7 +304,7 @@ test('locations named Kiel', co(function* (t) { test('station', co(function* (t) { const s = yield client.station(kielHbf) - validate(t, s, 'station', 'station') + validate(t, s, ['stop', 'station'], 'station') t.equal(s.id, kielHbf) t.end() diff --git a/test/oebb.js b/test/oebb.js index d2c665d3..dc1c96ae 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -11,7 +11,8 @@ const createClient = require('..') const oebbProfile = require('../p/oebb') const products = require('../p/oebb/products') const { - station: createValidateStation + station: createValidateStation, + stop: validateStop } = require('./lib/validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') @@ -314,9 +315,15 @@ test('locations named Salzburg', co(function* (t) { validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 20) - t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.type === 'stop' || s.type === 'station')) t.ok(locations.find(s => s.id && s.name)) // POIs - t.ok(locations.some(s => s.id === '008100002' || s.id === '8100002')) + t.ok(locations.some((s) => { + // todo: trim IDs + if (s.station) { + if (s.station.id === '008100002' || s.station.id === '8100002') return true + } + return s.id === '008100002' || s.id === '8100002' + })) t.end() })) @@ -329,12 +336,16 @@ test('station', co(function* (t) { const allProducts = products.reduce((acc, p) => (acc[p.id] = true, acc), {}) const validateStation = createValidateStation(cfg) const validate = createValidate(cfg, { + stop: (validate, s, name) => { + const withFakeProducts = Object.assign({products: allProducts}, s) + validateStop(validate, withFakeProducts, name) + }, station: (validate, s, name) => { const withFakeProducts = Object.assign({products: allProducts}, s) validateStation(validate, withFakeProducts, name) } }) - validate(t, loc, 'station', 'station') + validate(t, loc, ['stop', 'station'], 'station') t.equal(loc.id, wienRenngasse) diff --git a/test/vbb.js b/test/vbb.js index 0a657acf..cbda4065 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -377,7 +377,7 @@ test('locations', co(function* (t) { validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 20) - t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.type === 'stop' || s.type === 'station')) t.ok(locations.find(s => s.id && s.name)) // POIs t.ok(locations.find(s => !s.name && s.address)) // addresses @@ -387,7 +387,7 @@ test('locations', co(function* (t) { test('station', co(function* (t) { const s = yield client.station(spichernstr) - validate(t, s, 'station', 'station') + validate(t, s, ['stop', 'station'], 'station') t.equal(s.id, spichernstr) t.end() diff --git a/test/vbn.js b/test/vbn.js index 93f6638e..f4c65e2f 100644 --- a/test/vbn.js +++ b/test/vbn.js @@ -214,13 +214,14 @@ test.skip('locations named Magdeburg', co(function*(t) { validate(t, locations, 'locations', 'locations') t.ok(locations.length <= 20) - t.ok(locations.find(s => s.type === 'station')) + t.ok(locations.find(s => s.type === 'stop' || s.type === 'station')) t.ok(locations.find(s => s.id && s.name)) // POIs t.ok(locations.some((loc) => { - return ( - loc.id === '008010224' || // todo: trim IDs - loc.id === bremenHbf - ) + // todo: trim IDs + if (l.station) { + if (l.station.id === '008010224' || l.station.id === bremenHbf) return true + } + return l.id === '008010224' || l.id === bremenHbf })) t.end() @@ -229,7 +230,7 @@ test.skip('locations named Magdeburg', co(function*(t) { test.skip('station Magdeburg-Buckau', co(function* (t) { const s = yield client.station(bremerhavenHbf) - validate(t, s, 'station', 'station') + validate(t, s, ['stop', 'station'], 'station') t.equal(s.id, bremerhavenHbf) t.end() From 0c1cec01c26bb16a795d279de86c4a28028b9456 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 11 Jul 2018 13:43:36 +0200 Subject: [PATCH 151/176] adapt Nah.sh tests to 98a051b :green_heart: --- test/nahsh.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/test/nahsh.js b/test/nahsh.js index 2dc02879..83fbcba7 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -61,13 +61,14 @@ const assertValidPrice = (t, p) => { const test = tapePromise(tape) const client = createClient(nahshProfile) -const kielHbf = '8000199' -const flensburg = '8000103' -const luebeckHbf = '8000237' -const husum = '8000181' -const schleswig = '8005362' +const kielHbf = '9049079' +const flensburg = '9027253' +const luebeckHbf = '9057819' +const husum = '9044660' +const schleswig = '9081683' const ellerbekerMarkt = '9049027' const seefischmarkt = '9049245' +const kielRaeucherei = '9049217' test('journeys – Kiel Hbf to Flensburg', co(function* (t) { const journeys = yield client.journeys(kielHbf, flensburg, { @@ -132,7 +133,7 @@ test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { test('Kiel Hbf to Holstentor', co(function* (t) { const holstentor = { type: 'location', - id: '970003547', + id: '970004303', name: 'Hansestadt Lübeck, Holstentor (Denkmal)', latitude: 53.866321, longitude: 10.679976 @@ -203,8 +204,6 @@ test('trip details', co(function* (t) { })) test('departures at Kiel Räucherei', co(function* (t) { - const kielRaeucherei = '3440091' - const departures = yield client.departures(kielRaeucherei, { duration: 30, when }) @@ -248,8 +247,6 @@ test('departures at Berlin Hbf in direction of Berlin Ostbahnhof', co(function* })) test('arrivals at Kiel Räucherei', co(function* (t) { - const kielRaeucherei = '3440091' - const arrivals = yield client.arrivals(kielRaeucherei, { duration: 30, when }) @@ -278,7 +275,7 @@ test('nearby Kiel Hbf', co(function* (t) { t.ok(Array.isArray(nearby)) t.equal(nearby.length, 2) - t.equal(nearby[0].id, kielHbf) + t.ok(nearby[0].id === kielHbf || nearby[0].id === '8000199') t.equal(nearby[0].name, 'Kiel Hbf') t.ok(nearby[0].distance >= 0) t.ok(nearby[0].distance <= 100) From 12e61bea0a6ec139e9eab759b76854ff9ad2828c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 13 Jul 2018 11:34:54 +0200 Subject: [PATCH 152/176] {arrival,departure}.station -> .stop :boom:, adapt docs :memo: --- docs/departures.md | 22 ++-------------------- docs/radar.md | 6 +++--- parse/arrival-or-departure.js | 2 +- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/docs/departures.md b/docs/departures.md index 3fa9dac6..02fe5b37 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -62,7 +62,7 @@ The response may look like this: [ { tripId: '1|31431|28|86|17122017', trip: 31431, - station: { + stop: { type: 'station', id: '900000024101', name: 'S Charlottenburg', @@ -129,25 +129,7 @@ The response may look like this: }, { tripId: '1|28671|4|86|17122017', trip: 28671, - station: { - type: 'station', - id: '900000024202', - name: 'U Wilmersdorfer Str.', - location: { - type: 'location', - latitude: 52.506415, - longitude: 13.306777 - }, - products: { - suburban: false, - subway: true, - tram: false, - bus: false, - ferry: false, - express: false, - regional: false - } - }, + stop: { /* … */ }, when: '2017-12-17T19:35:00.000+01:00', delay: 0, line: { diff --git a/docs/radar.md b/docs/radar.md index 9e8a21e3..6d0bd2a5 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -68,7 +68,7 @@ The response may look like this: direction: 'S Flughafen Berlin-Schönefeld', trip: 31463, nextStops: [ { - station: { + stop: { type: 'stop', id: '900000029101', name: 'S Spandau', @@ -140,13 +140,13 @@ The response may look like this: direction: 'Heinersdorf', trip: 26321, nextStops: [ { - station: { /* S+U Alexanderplatz/Dircksenstr. */ }, + stop: { /* S+U Alexanderplatz/Dircksenstr. */ }, arrival: null, arrivalDelay: null, departure: '2017-12-17T19:52:00.000+01:00', departureDelay: null }, { - station: { /* Memhardstr. */ }, + stop: { /* Memhardstr. */ }, arrival: '2017-12-17T19:54:00.000+01:00', arrivalDelay: null, departure: '2017-12-17T19:54:00.000+01:00', diff --git a/parse/arrival-or-departure.js b/parse/arrival-or-departure.js index 7d731b95..cfcfce28 100644 --- a/parse/arrival-or-departure.js +++ b/parse/arrival-or-departure.js @@ -15,7 +15,7 @@ const createParseArrOrDep = (profile, opt, data, prefix) => { const res = { tripId: d.jid, - station: locations[parseInt(d.stbStop.locX)] || null, + stop: locations[parseInt(d.stbStop.locX)] || null, when: when.toISO(), direction: profile.parseStationName(d.dirTxt), line: lines[parseInt(d.prodX)] || null, From e6130d7c2350270b4a6e5e74e5d1fb77bebe556d Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 16 Jul 2018 11:35:47 +0200 Subject: [PATCH 153/176] changelog, related libs :memo:, clean up code --- docs/changelog.md | 2 ++ docs/journeys.md | 2 +- index.js | 2 +- parse/movement.js | 2 +- parse/stopover.js | 1 - readme.md | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 2fcbe9ea..3eee4338 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -13,6 +13,8 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 84bce0c `arrivals()`/`departures()`: parse & expose platforms - 85e0bdf `journeys()`: `startWithWalking` option with default `true` - f6ae29c journey legs with `type: 'walking'` now have a `distance` in meters +- 0d5a8fa departures, arrivals, stopovers: former scheduled platform(s) +- 0199749 `language` option with default `en` - 1551943 `arrivals()`/`departures()`: `includeRelatedStations` option with default `true` ### breaking changes 💥 diff --git a/docs/journeys.md b/docs/journeys.md index 9cabaeda..ccddad55 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -262,7 +262,7 @@ client.journeys(hbf, heinrichHeineStr) }) }) .then((laterJourneys) => { - const firstJourney = laterourneys[laterJourneys.length - 1] + const firstJourney = laterJourneys[laterJourneys.length - 1] console.log('departure of first (later) journey', firstJourney.legs[0].departure) }) .catch(console.error) diff --git a/index.js b/index.js index 5175cdd0..62aa7b6a 100644 --- a/index.js +++ b/index.js @@ -328,7 +328,7 @@ const createClient = (profile, request = _request) => { } opt = Object.assign({ stopovers: true, // return stations on the way? - polyline: false, + polyline: false, // return a track shape? remarks: true // parse & expose hints & warnings? }, opt) opt.when = new Date(opt.when || Date.now()) diff --git a/parse/movement.js b/parse/movement.js index 81166d1d..c51513a3 100644 --- a/parse/movement.js +++ b/parse/movement.js @@ -1,7 +1,7 @@ 'use strict' const createParseMovement = (profile, opt, data) => { - const {locations, lines, hints, warnings, polylines} = data + const {locations, lines, polylines} = data // todo: what is m.dirGeo? maybe the speed? // todo: what is m.stopL? diff --git a/parse/stopover.js b/parse/stopover.js index 25fc2b46..8c97acdd 100644 --- a/parse/stopover.js +++ b/parse/stopover.js @@ -2,7 +2,6 @@ const findRemark = require('./find-remark') -// todo: arrivalDelay, departureDelay or only delay ? const createParseStopover = (profile, opt, data, date) => { const {locations, lines, hints, warnings} = data diff --git a/readme.md b/readme.md index c7129099..a74d03ab 100644 --- a/readme.md +++ b/readme.md @@ -178,10 +178,10 @@ The returned [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript - [`hafas-departures-in-direction`](https://github.com/derhuerst/hafas-departures-in-direction#hafas-departures-in-direction) – Pass in a HAFAS client, get departures in a certain direction. - [`hafas-collect-departures-at`](https://github.com/derhuerst/hafas-collect-departures-at#hafas-collect-departures-at) – Utility to collect departures, using any HAFAS client. - [`hafas-monitor-departures`](https://github.com/derhuerst/hafas-monitor-departures#hafas-monitor-departures) – Pass in a HAFAS client, fetch all departures at any set of stations. -- [`hafas-departures-in-direction`](https://github.com/derhuerst/hafas-departures-in-direction#hafas-departures-in-direction) – Pass in a HAFAS client, get departures in a certain direction. - [`hafas-discover-stations`](https://github.com/derhuerst/hafas-discover-stations#hafas-discover-stations) – Pass in a HAFAS client, discover stations by querying departures. - [`hafas-record-delays`](https://github.com/derhuerst/hafas-record-delays#hafas-record-delays) – Record delays from hafas-monitor-departures into a LevelDB. - [`hafas-estimate-station-weight`](https://github.com/derhuerst/hafas-estimate-station-weight#hafas-estimate-station-weight) – Pass in a HAFAS client, estimate the importance of a station. +- [`hafas-client-rpc`](https://github.com/derhuerst/hafas-client-rpc) – Make JSON-RPC calls to `hafas-client` via WebSockets. - [`hafas-rest-api`](https://github.com/derhuerst/hafas-rest-api#hafas-rest-api) – Expose a HAFAS client via an HTTP REST API. - [List of european long-distance transport operators, available API endpoints, GTFS feeds and client modules.](https://github.com/public-transport/european-transport-operators) - [Collection of european transport JavaScript modules.](https://github.com/public-transport/european-transport-modules) From c4511c949c2d57f01c3721e8e2818e4bf164a525 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 16 Jul 2018 11:56:47 +0200 Subject: [PATCH 154/176] adapt tests & Nah.sh profile to 12e61be --- p/nahsh/index.js | 2 +- test/lib/arrivals.js | 10 +++++----- test/lib/departures.js | 10 +++++----- test/lib/journeys-station-to-address.js | 3 ++- test/lib/validators.js | 2 +- test/oebb.js | 4 ++-- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/p/nahsh/index.js b/p/nahsh/index.js index 9a992266..fd5ea3ae 100644 --- a/p/nahsh/index.js +++ b/p/nahsh/index.js @@ -84,7 +84,7 @@ const createParseMovement = (profile, opt, data) => { const res = _parseMovement(m) // filter out empty nextStops entries res.nextStops = res.nextStops.filter((f) => { - return f.station !== null || f.arrival !== null || f.departure !== null + return f.stop !== null || f.arrival !== null || f.departure !== null }) return res } diff --git a/test/lib/arrivals.js b/test/lib/arrivals.js index 4a21771e..4c486f49 100644 --- a/test/lib/arrivals.js +++ b/test/lib/arrivals.js @@ -8,14 +8,14 @@ const testArrivals = co(function* (cfg) { validate(t, arrs, 'arrivals', 'arrivals') t.ok(arrs.length > 0, 'must be >0 arrivals') for (let i = 0; i < arrs.length; i++) { - let station = arrs[i].station - let name = `arrs[${i}].station` - if (station.station) { - station = station.station + let stop = arrs[i].stop + let name = `arrs[${i}].stop` + if (stop.station) { + stop = stop.station name += '.station' } - t.equal(station.id, id, name + '.id is invalid') + t.equal(stop.id, id, name + '.id is invalid') } // todo: move into arrivals validator diff --git a/test/lib/departures.js b/test/lib/departures.js index f73015fc..e0853fe1 100644 --- a/test/lib/departures.js +++ b/test/lib/departures.js @@ -8,14 +8,14 @@ const testDepartures = co(function* (cfg) { validate(t, deps, 'departures', 'departures') t.ok(deps.length > 0, 'must be >0 departures') for (let i = 0; i < deps.length; i++) { - let station = deps[i].station - let name = `deps[${i}].station` - if (station.station) { - station = station.station + let stop = deps[i].stop + let name = `deps[${i}].stop` + if (stop.station) { + stop = stop.station name += '.station' } - t.equal(station.id, id, name + '.id is invalid') + t.equal(stop.id, id, name + '.id is invalid') } // todo: move into deps validator diff --git a/test/lib/journeys-station-to-address.js b/test/lib/journeys-station-to-address.js index f7e9f9fb..1184aeda 100644 --- a/test/lib/journeys-station-to-address.js +++ b/test/lib/journeys-station-to-address.js @@ -14,7 +14,8 @@ const testJourneysStationToAddress = co(function* (cfg) { const j = journeys[i] const firstLeg = j.legs[0] - t.strictEqual(firstLeg.origin.id, fromId) + const orig = firstLeg.origin.station || firstLeg.origin + t.ok(orig.id, fromId) const d = j.legs[j.legs.length - 1].destination const n = `journeys[0].legs[${i}].destination` diff --git a/test/lib/validators.js b/test/lib/validators.js index 3a91d28f..985598fd 100644 --- a/test/lib/validators.js +++ b/test/lib/validators.js @@ -262,7 +262,7 @@ const createValidateArrivalOrDeparture = (cfg) => { a.ok(dep.tripId, name + '.tripId must not be empty') a.strictEqual(typeof dep.trip, 'number', name + '.trip must be a number') - anyOf(['stop', 'station'], val, dep.station, name + '.station') + anyOf(['stop', 'station'], val, dep.stop, name + '.stop') assertValidWhen(dep.when, cfg.when, name) if (dep.delay !== null) { diff --git a/test/oebb.js b/test/oebb.js index dc1c96ae..3c8474a8 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -246,8 +246,8 @@ test('departures at Wien Leibenfrostgasse', co(function* (t) { for (let i = 0; i < deps.length; i++) { const dep = deps[i] - const msg = `deps[${i}].station.id is invalid` - t.ok(ids.includes(dep.station.id, msg)) + const msg = `deps[${i}].stop.id is invalid` + t.ok(ids.includes(dep.stop.id, msg)) } t.end() From 36b24e27e4be8c1f96826ce6602486142e6bfd3e Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 16 Jul 2018 12:15:52 +0200 Subject: [PATCH 155/176] fix Nah.sh station -> address tests :green_heart: --- test/nahsh.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/nahsh.js b/test/nahsh.js index 83fbcba7..7ee8c050 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -108,14 +108,14 @@ test('journeys – fails with no product', (t) => { t.end() }) -test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { - const zingel = { +test('Kiel Hbf to Berliner Str. 80, Husum', co(function* (t) { + const berlinerStr = { type: 'location', - address: 'Husum, Zingel 10', - latitude: 54.475359, - longitude: 9.050798 + address: 'Husum, Berliner Straße 80', + latitude: 54.488995, + longitude: 9.056263 } - const journeys = yield client.journeys(kielHbf, zingel, { + const journeys = yield client.journeys(kielHbf, berlinerStr, { results: 3, departure: when }) @@ -125,7 +125,7 @@ test('Kiel Hbf to Husum, Zingel 10', co(function* (t) { journeys, validate, fromId: kielHbf, - to: zingel + to: berlinerStr }) t.end() })) From 9c1c2f51e5310216a6125b055cb816430b9acba5 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 16 Jul 2018 12:18:23 +0200 Subject: [PATCH 156/176] parse leg remarks even if it is a walking leg :bug: --- parse/journey-leg.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index 95f02324..e6f2a2e4 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -81,6 +81,10 @@ const createParseJourneyLeg = (profile, opt, data) => { res.public = true res.distance = pt.gis && pt.gis.dist || null if (pt.type === 'TRSF') res.transfer = true + + if (opt.remarks && Array.isArray(pt.gis.msgL)) { + applyRemarks(res, hints, warnings, pt.gis.msgL) + } } else if (pt.type === 'JNY') { // todo: pull `public` value from `profile.products` res.id = pt.jny.jid From d44ec05a1d94aca279b2bea8c210efd87047ae72 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 16 Jul 2018 12:39:13 +0200 Subject: [PATCH 157/176] adapt markup in docs to current code :memo: --- docs/departures.md | 22 +++++----- docs/journeys.md | 102 ++++++++++++++++++++++++++++----------------- docs/locations.md | 4 +- docs/station.md | 24 ++++++----- 4 files changed, 89 insertions(+), 63 deletions(-) diff --git a/docs/departures.md b/docs/departures.md index 02fe5b37..07212022 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -61,7 +61,6 @@ The response may look like this: ```js [ { tripId: '1|31431|28|86|17122017', - trip: 31431, stop: { type: 'station', id: '900000024101', @@ -86,10 +85,10 @@ The response may look like this: line: { type: 'line', id: '18299', - name: 'S9', - public: true, mode: 'train', product: 'suburban', + public: true, + name: 'S9', symbol: 'S', nr: 9, metro: false, @@ -102,21 +101,21 @@ The response may look like this: name: 'S-Bahn Berlin GmbH' } }, - direction: 'S Spandau' + direction: 'S Spandau', + trip: 31431 }, { tripId: '1|30977|8|86|17122017', - trip: 30977, - station: { /* … */ }, + stop: { /* … */ }, when: null, delay: null, cancelled: true, line: { type: 'line', id: '16441', - name: 'S5', - public: true, mode: 'train', product: 'suburban', + public: true, + name: 'S5', symbol: 'S', nr: 5, metro: false, @@ -125,7 +124,8 @@ The response may look like this: productCode: 0, operator: { /* … */ } }, - direction: 'S Westkreuz' + direction: 'S Westkreuz', + trip: 30977 }, { tripId: '1|28671|4|86|17122017', trip: 28671, @@ -135,10 +135,10 @@ The response may look like this: line: { type: 'line', id: '19494', - name: 'U7', - public: true, mode: 'train', product: 'subway', + public: true, + name: 'U7', symbol: 'U', nr: 7, metro: false, diff --git a/docs/journeys.md b/docs/journeys.md index ccddad55..2fa65400 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -99,7 +99,7 @@ The response may look like this: [ { legs: [ { - id: '1|31041|35|86|17122017', + id: '1|32615|6|86|10072018', origin: { type: 'station', id: '900000003201', @@ -119,30 +119,23 @@ The response may look like this: regional: true } }, - departure: '2017-12-17T19:07:00.000+01:00', - departurePlatform: '16', destination: { type: 'station', - id: '900000024101', - name: 'S Charlottenburg', + id: '900000100004', + name: 'S+U Jannowitzbrücke', location: { type: 'location', latitude: 52.504806, longitude: 13.303846 }, - products: { - suburban: true, - subway: false, - tram: false, - bus: true, - ferry: false, - express: false, - regional: true - } + products: { /* … */ } }, - arrival: '2017-12-17T19:47:00.000+01:00', - arrivalPlatform: '8', - arrivalDelay: 30, + departure: '2018-07-10T23:54:00.000+02:00', + departureDelay: 60, + departurePlatform: '15', + arrival: '2018-07-11T00:02:00.000+02:00', + arrivalDelay: 60, + arrivalPlatform: '3', line: { type: 'line', id: '16845', @@ -150,19 +143,19 @@ The response may look like this: public: true, mode: 'train', product: 'suburban', + operator: { + type: 'operator', + id: 's-bahn-berlin-gmbh', + name: 'S-Bahn Berlin GmbH' + }, symbol: 'S', nr: 7, metro: false, express: false, night: false, - productCode: 0, - operator: { - type: 'operator', - id: 's-bahn-berlin-gmbh', - name: 'S-Bahn Berlin GmbH' - } + productCode: 0 }, - direction: 'S Potsdam Hauptbahnhof', + direction: 'S Ahrensfelde', stopovers: [ { stop: { type: 'station', @@ -173,32 +166,63 @@ The response may look like this: }, arrival: null, departure: null, - cancelled: true + cancelled: true, + remarks: [ + {type: 'hint', code: 'bf', text: 'barrier-free'}, + {type: 'hint', code: 'FB', text: 'Bicycle conveyance'} + ] }, { stop: { type: 'station', - id: '900000003102', - name: 'S Bellevue', + id: '900000100001', + name: 'S+U Friedrichstr.', location: { /* … */ }, products: { /* … */ } }, - arrival: '2017-12-17T19:09:00.000+01:00', - arrivalPlatform: '1', - departure: '2017-12-17T19:09:00.000+01:00', - departurePlatform: '1' - }, /* … */ { - stop: { + arrival: '2018-07-10T23:56:00.000+02:00', + arrivalDelay: 60, + arrivalPlatform: null, + departure: '2018-07-10T23:57:00.000+02:00', + departureDelay: 60, + departurePlatform: null, + remarks: [ /* … */ ] + }, + /* … */ + { type: 'station', - id: '900000024101', - name: 'S Charlottenburg', + id: '900000100004', + name: 'S+U Jannowitzbrücke', location: { /* … */ }, products: { /* … */ } }, - arrival: '2017-12-17T19:17:00.000+01:00', - arrivalPlatform: '5', - departure: '2017-12-17T19:17:00.000+01:00', - departurePlatform: '5' + arrival: '2018-07-11T00:02:00.000+02:00', + arrivalDelay: 60, + arrivalPlatform: null, + departure: '2018-07-11T00:02:00.000+02:00', + departureDelay: null, + departurePlatform: null, + remarks: [ /* … */ ] } ] + }, { + origin: { + type: 'station', + id: '900000100004', + name: 'S+U Jannowitzbrücke', + location: { /* … */ }, + products: { /* … */ } + }, + destination: { + type: 'station', + id: '900000100008', + name: 'U Heinrich-Heine-Str.', + location: { /* … */ }, + products: { /* … */ } + }, + departure: '2018-07-11T00:01:00.000+02:00', + arrival: '2018-07-11T00:10:00.000+02:00', + mode: 'walking', + public: true, + distance: 558 } ] }, earlierRef: '…', // use with the `earlierThan` option diff --git a/docs/locations.md b/docs/locations.md index 3abdc58e..9d8cfd65 100644 --- a/docs/locations.md +++ b/docs/locations.md @@ -54,14 +54,14 @@ The response may look like this: } }, { // point of interest type: 'location', - name: 'Berlin, Holiday Inn Centre Alexanderplatz****', id: '900980709', + name: 'Berlin, Holiday Inn Centre Alexanderplatz****', latitude: 52.523549, longitude: 13.418441 }, { // point of interest type: 'location', - name: 'Berlin, Hotel Agon am Alexanderplatz', id: '900980176', + name: 'Berlin, Hotel Agon am Alexanderplatz', latitude: 52.524556, longitude: 13.420266 } ] diff --git a/docs/station.md b/docs/station.md index b329245f..d31feb6f 100644 --- a/docs/station.md +++ b/docs/station.md @@ -66,24 +66,26 @@ The response may look like this: lines: [ { type: 'line', id: 'u1', - name: 'U1', - public: true, - class: 2, - product: 'subway', mode: 'train', + product: 'subway', + public: true, + name: 'U1', + class: 2, symbol: 'U', nr: 1, metro: false, express: false, - night: false }, - // … - { type: 'line', + night: false + }, + // … + { + type: 'line', id: 'n9', - name: 'N9', - public: true, - class: 8, - product: 'bus', mode: 'bus', + product: 'bus', + public: true, + name: 'N9', + class: 8, symbol: 'N', nr: 9, metro: false, From b5b2cfb38f852a1a824686155031a0806605aa53 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 16 Jul 2018 14:41:41 +0200 Subject: [PATCH 158/176] put leg-wide remarks into leg, not stopovers [ci skip] --- parse/journey-leg.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/parse/journey-leg.js b/parse/journey-leg.js index e6f2a2e4..0107a85f 100644 --- a/parse/journey-leg.js +++ b/parse/journey-leg.js @@ -17,19 +17,24 @@ const applyRemarks = (leg, hints, warnings, refs) => { const toI = leg.stopovers.findIndex(s => s[locX] === ref.tLocX) if (fromI < 0 || toI < 0) continue - for (let i = fromI; i <= toI; i++) { - const stopover = leg.stopovers[i] - if (!stopover) continue - if (Array.isArray(stopover.remarks)) { - stopover.remarks.push(remark) - } else { - stopover.remarks = [remark] + const wholeLeg = fromI === 0 && toI === (leg.stopovers.length - 1) + if (!wholeLeg) { + for (let i = fromI; i <= toI; i++) { + const stopover = leg.stopovers[i] + if (!stopover) continue + if (Array.isArray(stopover.remarks)) { + stopover.remarks.push(remark) + } else { + stopover.remarks = [remark] + } } + + continue } - } else { - if (Array.isArray(leg.remarks)) leg.remarks.push(remark) - else leg.remarks = [remark] } + + if (Array.isArray(leg.remarks)) leg.remarks.push(remark) + else leg.remarks = [remark] // todo: `ref.tagL` } } From 5d9d738152e60855388e22e9d1b88b5ca21d4c37 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Jul 2018 21:50:20 +0200 Subject: [PATCH 159/176] mandatory User-Agent param :boom: --- index.js | 20 ++++++++++++-------- lib/request.js | 4 ++-- throttle.js | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 62aa7b6a..671290a5 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ const _request = require('./lib/request') const isNonEmptyString = str => 'string' === typeof str && str.length > 0 -const createClient = (profile, request = _request) => { +const createClient = (profile, userAgent, request = _request) => { profile = Object.assign({}, defaultProfile, profile) if (!profile.parseProducts) { profile.parseProducts = createParseBitmask(profile) @@ -22,6 +22,10 @@ const createClient = (profile, request = _request) => { } validateProfile(profile) + if ('string' !== typeof userAgent) { + throw new Error('userAgent must be a string'); + } + const _stationBoard = (station, type, parser, opt = {}) => { if (isObj(station)) station = profile.formatStation(station.id) else if ('string' === typeof station) station = profile.formatStation(station) @@ -45,7 +49,7 @@ const createClient = (profile, request = _request) => { const products = profile.formatProductsFilter(opt.products || {}) const dir = opt.direction ? profile.formatStation(opt.direction) : null - return request(profile, opt, { + return request(profile, userAgent, opt, { meth: 'StationBoard', req: { type, @@ -181,7 +185,7 @@ const createClient = (profile, request = _request) => { } if (profile.journeysNumF) query.numF = opt.results - return request(profile, opt, { + return request(profile, userAgent, opt, { cfg: {polyEnc: 'GPA'}, meth: 'TripSearch', req: profile.transformJourneysQuery(query, opt) @@ -234,7 +238,7 @@ const createClient = (profile, request = _request) => { }, opt) const f = profile.formatLocationFilter(opt.stations, opt.addresses, opt.poi) - return request(profile, opt, { + return request(profile, userAgent, opt, { cfg: {polyEnc: 'GPA'}, meth: 'LocMatch', req: {input: { @@ -261,7 +265,7 @@ const createClient = (profile, request = _request) => { opt = Object.assign({ stationLines: false // parse & expose lines of the station? }, opt) - return request(profile, opt, { + return request(profile, userAgent, opt, { meth: 'LocDetails', req: { locL: [station] @@ -295,7 +299,7 @@ const createClient = (profile, request = _request) => { stationLines: false // parse & expose lines of the station? }, opt) - return request(profile, opt, { + return request(profile, userAgent, opt, { cfg: {polyEnc: 'GPA'}, meth: 'LocGeoPos', req: { @@ -334,7 +338,7 @@ const createClient = (profile, request = _request) => { opt.when = new Date(opt.when || Date.now()) if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') - return request(profile, opt, { + return request(profile, userAgent, opt, { cfg: {polyEnc: 'GPA'}, meth: 'JourneyDetails', req: { @@ -384,7 +388,7 @@ const createClient = (profile, request = _request) => { if (Number.isNaN(+opt.when)) throw new Error('opt.when is invalid') const durationPerStep = opt.duration / Math.max(opt.frames, 1) * 1000 - return request(profile, opt, { + return request(profile, userAgent, opt, { meth: 'JourneyGeoPos', req: { maxJny: opt.results, diff --git a/lib/request.js b/lib/request.js index 709fa483..00396906 100644 --- a/lib/request.js +++ b/lib/request.js @@ -11,7 +11,7 @@ const {fetch} = require('fetch-ponyfill')({Promise}) const md5 = input => createHash('md5').update(input).digest() -const request = (profile, opt, data) => { +const request = (profile, userAgent, opt, data) => { const body = profile.transformReqBody({ lang: opt.language || 'en', svcReqL: [data] @@ -24,7 +24,7 @@ const request = (profile, opt, data) => { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'Accept': 'application/json', - 'user-agent': 'https://github.com/public-transport/hafas-client' + 'user-agent': userAgent }, query: {} }) diff --git a/throttle.js b/throttle.js index 99fb3cc0..995e855e 100644 --- a/throttle.js +++ b/throttle.js @@ -5,9 +5,9 @@ const throttle = require('p-throttle') const request = require('./lib/request') const createClient = require('.') -const createThrottledClient = (profile, limit = 5, interval = 1000) => { +const createThrottledClient = (profile, userAgent, limit = 5, interval = 1000) => { const throttledRequest = throttle(request, limit, interval) - return createClient(profile, throttledRequest) + return createClient(profile, userAgent, throttledRequest) } module.exports = createThrottledClient From f20931b6ded6db5bef27b591747a96997a6e457c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Jul 2018 21:55:03 +0200 Subject: [PATCH 160/176] adapt docs & examples to 0a0cddc :memo: --- docs/changelog.md | 1 + docs/departures.md | 2 +- docs/journeys.md | 2 +- docs/locations.md | 2 +- docs/migrating-to-3.md | 13 +++++++++++++ docs/nearby.md | 2 +- docs/radar.md | 2 +- docs/station.md | 2 +- docs/trip.md | 2 +- p/db/example.js | 2 +- p/insa/example.js | 2 +- p/nahsh/example.js | 2 +- p/oebb/example.js | 2 +- p/vbb/example.js | 2 +- readme.md | 2 +- 15 files changed, 27 insertions(+), 13 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 3eee4338..34450707 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -19,6 +19,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( ### breaking changes 💥 +- c4935bc new mandatory `User-Agent` parameter - b7c1ee3 profiles: new products markup ([guide](https://github.com/public-transport/hafas-client/blob/ebe4fa64d871f711ced99d528c0171b180edc135/docs/writing-a-profile.md#3-products)) - 40b559f change `radar(n, w, s, e)` signature to `radar({north, west, south, east})` - 005f3f8 remove `journey.departure`, `journey.arrival`, … diff --git a/docs/departures.md b/docs/departures.md index 07212022..33651a1d 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -48,7 +48,7 @@ As an example, we're going to use the [VBB profile](../p/vbb): const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') // S Charlottenburg client.departures('900000024101', {duration: 3}) diff --git a/docs/journeys.md b/docs/journeys.md index 2fa65400..f7be35c1 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -82,7 +82,7 @@ As an example, we're going to use the [VBB profile](../p/vbb): const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') // Hauptbahnhof to Heinrich-Heine-Str. client.journeys('900000003201', '900000100008', { diff --git a/docs/locations.md b/docs/locations.md index 9d8cfd65..169dd342 100644 --- a/docs/locations.md +++ b/docs/locations.md @@ -24,7 +24,7 @@ As an example, we're going to use the [VBB profile](../p/vbb): const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') client.locations('Alexanderplatz', {results: 3}) .then(console.log) diff --git a/docs/migrating-to-3.md b/docs/migrating-to-3.md index eb2c7ba2..0855dd02 100644 --- a/docs/migrating-to-3.md +++ b/docs/migrating-to-3.md @@ -1,5 +1,18 @@ # Migrating to `hafas-client@3` +## New `User-Agent` parameter + +Pass an additional `User-Agent` string into `createClient`: + +```js +const createClient = require('hafas-client') +const dbProfile = require('hafas-client/p/db') + +const client = createClient(dbProfile, 'my-awesome-program') +``` + +Pick a name that describes your program and – if possible – the website/repo of it. + ## If you use the `journeyLeg()` method… …change the `journeyLeg(id, lineName)` call to `trip(id, lineName)`. c8ff217 diff --git a/docs/nearby.md b/docs/nearby.md index 33352922..20186c46 100644 --- a/docs/nearby.md +++ b/docs/nearby.md @@ -24,7 +24,7 @@ As an example, we're going to use the [VBB profile](../p/vbb): const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') client.nearby({ type: 'location', diff --git a/docs/radar.md b/docs/radar.md index 6d0bd2a5..f5502308 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -26,7 +26,7 @@ As an example, we're going to use the [VBB profile](../p/vbb): const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') client.radar({ north: 52.52411, diff --git a/docs/station.md b/docs/station.md index d31feb6f..615dad93 100644 --- a/docs/station.md +++ b/docs/station.md @@ -35,7 +35,7 @@ As an example, we're going to use the [VBB profile](../p/vbb): const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') client.station('900000042101') // U Spichernstr. .then(console.log) diff --git a/docs/trip.md b/docs/trip.md index 000359d4..9d1b34fe 100644 --- a/docs/trip.md +++ b/docs/trip.md @@ -10,7 +10,7 @@ Let's say you used [`journeys`](journeys.md) and now want to get more up-to-date const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') // Hauptbahnhof to Heinrich-Heine-Str. client.journeys('900000003201', '900000100008', {results: 1}) diff --git a/p/db/example.js b/p/db/example.js index 9d7ef481..1ca575b9 100644 --- a/p/db/example.js +++ b/p/db/example.js @@ -3,7 +3,7 @@ const createClient = require('../../') const dbProfile = require('.') -const client = createClient(dbProfile) +const client = createClient(dbProfile, 'hafas-client-example') // Berlin Jungfernheide to München Hbf client.journeys('8011167', '8000261', {results: 1, tickets: true}) diff --git a/p/insa/example.js b/p/insa/example.js index 01ebdaf0..a697f110 100644 --- a/p/insa/example.js +++ b/p/insa/example.js @@ -3,7 +3,7 @@ const createClient = require('../..') const insaProfile = require('.') -const client = createClient(insaProfile) +const client = createClient(insaProfile, 'hafas-client-example') // from Magdeburg-Neustadt to Magdeburg-Buckau client.journeys('008010226', '008013456', {results: 1}) diff --git a/p/nahsh/example.js b/p/nahsh/example.js index fad4fbf4..57a9aec5 100644 --- a/p/nahsh/example.js +++ b/p/nahsh/example.js @@ -3,7 +3,7 @@ const createClient = require('../..') const nahshProfile = require('.') -const client = createClient(nahshProfile) +const client = createClient(nahshProfile, 'hafas-client-example') // Flensburg Hbf to Kiel Hbf client.journeys('8000103', '8000199', {results: 10, tickets: true}) diff --git a/p/oebb/example.js b/p/oebb/example.js index ff6d5b40..f2c3ccdb 100644 --- a/p/oebb/example.js +++ b/p/oebb/example.js @@ -3,7 +3,7 @@ const createClient = require('../..') const oebbProfile = require('.') -const client = createClient(oebbProfile) +const client = createClient(oebbProfile, 'hafas-client-example') // Wien Westbahnhof to Salzburg Hbf client.journeys('1291501', '8100002', {results: 1}) diff --git a/p/vbb/example.js b/p/vbb/example.js index 270379ba..6a1968c2 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -3,7 +3,7 @@ const createClient = require('../..') const vbbProfile = require('.') -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'hafas-client-example') // Hauptbahnhof to Charlottenburg client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) diff --git a/readme.md b/readme.md index a74d03ab..bad03961 100644 --- a/readme.md +++ b/readme.md @@ -47,7 +47,7 @@ const createClient = require('hafas-client') const dbProfile = require('hafas-client/p/db') // create a client with Deutsche Bahn profile -const client = createClient(dbProfile) +const client = createClient(dbProfile, 'my-awesome-program') // Berlin Jungfernheide to München Hbf client.journeys('8011167', '8000261', {results: 1}) From 5f39d2875cd51320ef5107f7d950b7407498301b Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 19 Jul 2018 21:55:54 +0200 Subject: [PATCH 161/176] adapt tests to 0a0cddc --- test/db.js | 2 +- test/insa.js | 2 +- test/nahsh.js | 2 +- test/oebb.js | 2 +- test/throttle.js | 3 ++- test/vbb.js | 2 +- test/vbn.js | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/test/db.js b/test/db.js index 5448ccfd..2486a412 100644 --- a/test/db.js +++ b/test/db.js @@ -66,7 +66,7 @@ const assertValidPrice = (t, p) => { } const test = tapePromise(tape) -const client = createClient(dbProfile) +const client = createClient(dbProfile, 'public-transport/hafas-client:test') const berlinHbf = '8011160' const münchenHbf = '8000261' diff --git a/test/insa.js b/test/insa.js index 2a6fe169..90aee588 100644 --- a/test/insa.js +++ b/test/insa.js @@ -33,7 +33,7 @@ const cfg = { const validate = createValidate(cfg, {}) const test = tapePromise(tape) -const client = createClient(insaProfile) +const client = createClient(insaProfile, 'public-transport/hafas-client:test') const magdeburgHbf = '8010224' const magdeburgBuckau = '8013456' diff --git a/test/nahsh.js b/test/nahsh.js index 7ee8c050..f17c1c62 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -59,7 +59,7 @@ const assertValidPrice = (t, p) => { } const test = tapePromise(tape) -const client = createClient(nahshProfile) +const client = createClient(nahshProfile, 'public-transport/hafas-client:test') const kielHbf = '9049079' const flensburg = '9027253' diff --git a/test/oebb.js b/test/oebb.js index 3c8474a8..f383e6ba 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -50,7 +50,7 @@ const assertValidPrice = (t, p) => { } const test = tapePromise(tape) -const client = createClient(oebbProfile) +const client = createClient(oebbProfile, 'public-transport/hafas-client:test') const salzburgHbf = '8100002' const wienFickeystr = '911014' diff --git a/test/throttle.js b/test/throttle.js index cc849218..bbbdea87 100644 --- a/test/throttle.js +++ b/test/throttle.js @@ -5,6 +5,7 @@ const test = require('tape') const createThrottledClient = require('../throttle') const vbbProfile = require('../p/vbb') +const userAgent = 'public-transport/hafas-client:test' const spichernstr = '900000042101' test('throttle works', (t) => { @@ -15,7 +16,7 @@ test('throttle works', (t) => { } const mockProfile = Object.assign({}, vbbProfile, {transformReqBody}) - const client = createThrottledClient(mockProfile, 2, 1000) + const client = createThrottledClient(mockProfile, userAgent, 2, 1000) for (let i = 0; i < 10; i++) client.departures(spichernstr, {duration: 1}) t.plan(3) diff --git a/test/vbb.js b/test/vbb.js index cbda4065..9968fcd4 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -103,7 +103,7 @@ const validate = createValidate(cfg, { }) const test = tapePromise(tape) -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'public-transport/hafas-client:test') const amrumerStr = '900000009101' const spichernstr = '900000042101' diff --git a/test/vbn.js b/test/vbn.js index f4c65e2f..241e442a 100644 --- a/test/vbn.js +++ b/test/vbn.js @@ -30,7 +30,7 @@ const cfg = { const validate = createValidate(cfg, {}) const test = tapePromise(tape) -const client = createClient(vbnProfile) +const client = createClient(vbnProfile, 'public-transport/hafas-client:test') const bremenHbf = '8000050' const bremerhavenHbf = '8000051' From 3dc492b6861b1f029a9940fb1133f7307755f574 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 21 Jul 2018 14:18:26 +0200 Subject: [PATCH 162/176] docs/station: fix formatting --- docs/station.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/station.md b/docs/station.md index 615dad93..831d6ff9 100644 --- a/docs/station.md +++ b/docs/station.md @@ -26,6 +26,7 @@ With `opt`, you can override the default options, which look like this: stationLines: false, // parse & expose lines of the station? language: 'en' // language to get results in } +``` ## Response From 5b754aaa5506dbda781cbae0a6992c7a14405ac9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Jul 2018 17:37:36 +0200 Subject: [PATCH 163/176] refreshJourney method --- index.js | 42 ++++++++++++++++++++++++++++++++++++++++-- parse/journey.js | 3 ++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 671290a5..54235449 100644 --- a/index.js +++ b/index.js @@ -59,7 +59,7 @@ const createClient = (profile, userAgent, request = _request) => { dirLoc: dir, jnyFltrL: [products], dur: opt.duration, - getPasslist: false, // todo: what is this? + getPasslist: false, // todo stbFltrEquiv: !opt.includeRelatedStations } }) @@ -224,6 +224,44 @@ const createClient = (profile, userAgent, request = _request) => { return more(when, journeysRef) } + const refreshJourney = (refreshToken, opt = {}) => { + if ('string' !== typeof refreshToken || !refreshToken) { + new Error('refreshToken must be a non-empty string.') + } + + opt = Object.assign({ + stopovers: false, // return stations on the way? + tickets: false, // return tickets? + polylines: false, // return leg shapes? + remarks: true // parse & expose hints & warnings? + }, opt) + + return request(profile, opt, { + meth: 'Reconstruction', + req: { + ctxRecon: refreshToken, + getIST: true, // todo: make an option + getPasslist: !!opt.stopovers, + getPolyline: !!opt.polylines, + getTariff: !!opt.tickets + } + }) + .then((d) => { + if (!Array.isArray(d.outConL) || !d.outConL[0]) { + throw new Error('invalid response') + } + + const parse = profile.parseJourney(profile, opt, { + locations: d.locations, + lines: d.lines, + hints: d.hints, + warnings: d.warnings, + polylines: opt.polylines && d.common.polyL || [] + }) + return parse(d.outConL[0]) + }) + } + const locations = (query, opt = {}) => { if (!isNonEmptyString(query)) { throw new Error('query must be a non-empty string.') @@ -420,7 +458,7 @@ const createClient = (profile, userAgent, request = _request) => { }) } - const client = {departures, arrivals, journeys, locations, station, nearby} + const client = {departures, arrivals, journeys, refreshJourney, locations, station, nearby} if (profile.trip) client.trip = trip if (profile.radar) client.radar = radar Object.defineProperty(client, 'profile', {value: profile}) diff --git a/parse/journey.js b/parse/journey.js index a852469f..0ebe1001 100644 --- a/parse/journey.js +++ b/parse/journey.js @@ -20,7 +20,8 @@ const createParseJourney = (profile, opt, data) => { const legs = j.secL.map(leg => parseLeg(j, leg)) const res = { type: 'journey', - legs + legs, + refreshToken: j.ctxRecon || null } if (opt.remarks && Array.isArray(j.msgL)) { From 8a45d26fda79ad35bf804e6dba1bf8960c067911 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Jul 2018 18:16:14 +0200 Subject: [PATCH 164/176] add docs for refreshJourneys (c88071f) :memo: --- docs/readme.md | 1 + docs/refresh-journey.md | 38 ++++++++++++++++++++++++++++++++++++++ p/insa/example.js | 4 ++++ p/vbb/example.js | 4 ++++ 4 files changed, 47 insertions(+) create mode 100644 docs/refresh-journey.md diff --git a/docs/readme.md b/docs/readme.md index f7d3a42b..9c85bce2 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,6 +1,7 @@ # API documentation - [`journeys(from, to, [opt])`](journeys.md) – get journeys between locations +- [`refreshJourney(refreshToken, [opt])`](refresh-journey.md) – fetch up-to-date/more details of a `journey` - [`trip(id, lineName, [opt])`](trip.md) – get details for a trip - [`departures(station, [opt])`](departures.md) – query the next departures at a station - [`arrivals(station, [opt])`](arrivals.md) – query the next arrivals at a station diff --git a/docs/refresh-journey.md b/docs/refresh-journey.md new file mode 100644 index 00000000..94e6025c --- /dev/null +++ b/docs/refresh-journey.md @@ -0,0 +1,38 @@ +# `refreshJourney(refreshToken, [opt])` + +`refreshToken` must be a string, taken from `journey.refreshToken`. + +With `opt`, you can override the default options, which look like this: + +```js +{ + stopovers: false, // return stations on the way? + polylines: false, // return a shape for each leg? + tickets: false, // return tickets? only available with some profiles + remarks: true, // parse & expose hints & warnings? + language: 'en' // language to get results in +} +``` + +## Response + +As an example, we're going to use the [VBB profile](../p/vbb): + +```js +const createClient = require('hafas-client') +const vbbProfile = require('hafas-client/p/vbb') + +const client = createClient(vbbProfile) + +// Hauptbahnhof to Heinrich-Heine-Str. +client.journeys('900000003201', '900000100008', {results: 1}) +.then(([journey]) => { + // later, fetch up-to-date info on the journey + client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true}) + .then(console.log) + .catch(console.error) +}) +.catch(console.error) +``` + +`refreshJourney()` will return a *single* [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1) `journey`, in the same format as with `journeys()`. diff --git a/p/insa/example.js b/p/insa/example.js index a697f110..8d92c4fe 100644 --- a/p/insa/example.js +++ b/p/insa/example.js @@ -29,6 +29,10 @@ client.journeys('008010226', '008013456', {results: 1}) // return client.trip(leg.id, leg.line.name) // }) +// .then(([journey]) => { +// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true}) +// }) + .then(data => { console.log(require('util').inspect(data, { depth: null })) }) diff --git a/p/vbb/example.js b/p/vbb/example.js index 6a1968c2..5a79930f 100644 --- a/p/vbb/example.js +++ b/p/vbb/example.js @@ -27,6 +27,10 @@ client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) // const leg = journey.legs[0] // return client.trip(leg.id, leg.line.name, {polyline: true}) // }) + +// .then(([journey]) => { +// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true}) +// }) .then((data) => { console.log(require('util').inspect(data, {depth: null})) }) From da10988b2915555d00786a54449218b977f1b59c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Jul 2018 18:16:46 +0200 Subject: [PATCH 165/176] add tests for refreshJourneys (c88071f) :white_check_mark: --- test/db.js | 14 ++++++++++ test/insa.js | 14 ++++++++++ test/lib/refresh-journey.js | 51 +++++++++++++++++++++++++++++++++++++ test/nahsh.js | 14 ++++++++++ test/oebb.js | 14 ++++++++++ test/vbb.js | 14 ++++++++++ test/vbn.js | 14 ++++++++++ 7 files changed, 135 insertions(+) create mode 100644 test/lib/refresh-journey.js diff --git a/test/db.js b/test/db.js index 2486a412..e4d0d013 100644 --- a/test/db.js +++ b/test/db.js @@ -20,6 +20,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testRefreshJourney = require('./lib/refresh-journey') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testDeparturesInDirection = require('./lib/departures-in-direction') @@ -192,6 +193,19 @@ test('earlier/later journeys, Jungfernheide -> München Hbf', co(function* (t) { t.end() })) +test('refreshJourney', co(function* (t) { + yield testRefreshJourney({ + test: t, + fetchJourneys: client.journeys, + refreshJourney: client.refreshJourney, + validate, + fromId: jungfernheide, + toId: münchenHbf, + when + }) + t.end() +})) + test('trip details', co(function* (t) { const journeys = yield client.journeys(berlinHbf, münchenHbf, { results: 1, departure: when diff --git a/test/insa.js b/test/insa.js index 90aee588..cca2762b 100644 --- a/test/insa.js +++ b/test/insa.js @@ -14,6 +14,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testRefreshJourney = require('./lib/refresh-journey') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testDeparturesInDirection = require('./lib/departures-in-direction') @@ -154,6 +155,19 @@ test('earlier/later journeys', co(function* (t) { t.end() })) +test('refreshJourney', co(function* (t) { + yield testRefreshJourney({ + test: t, + fetchJourneys: client.journeys, + refreshJourney: client.refreshJourney, + validate, + fromId: magdeburgHbf, + toId: magdeburgBuckau, + when + }) + t.end() +})) + test('trip details', co(function* (t) { const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { results: 1, departure: when diff --git a/test/lib/refresh-journey.js b/test/lib/refresh-journey.js new file mode 100644 index 00000000..8311e689 --- /dev/null +++ b/test/lib/refresh-journey.js @@ -0,0 +1,51 @@ +'use strict' + +const co = require('./co') + +const simplify = j => j.legs.map(l => { + let departure = null + if (l.departure) { + departure = +new Date(l.departure) + if ('number' === typeof l.departureDelay) departure -= l.departureDelay * 1000 + } + let arrival = null + if (l.arrival) { + arrival = +new Date(l.arrival) + if ('number' === typeof l.arrivalDelay) arrival -= l.arrivalDelay * 1000 + } + return { + origin: l.origin, + destination: l.destination, + scheduledDeparture: departure, + scheduledArrival: arrival, + line: l.line + } +}) + +const testRefreshJourney = co(function* (cfg) { + const { + test: t, + fetchJourneys, + refreshJourney, + fromId, + toId, + when, + // todo: validate + } = cfg + + const [model] = yield fetchJourneys(fromId, toId, { + results: 1, departure: when, + stopovers: false + }) + + // todo: move to journeys validator? + t.equal(typeof model.refreshToken, 'string') + t.ok(model.refreshToken) + + const refreshed = yield refreshJourney(model.refreshToken, { + stopovers: false + }) + t.deepEqual(simplify(refreshed), simplify(model)) +}) + +module.exports = testRefreshJourney diff --git a/test/nahsh.js b/test/nahsh.js index f17c1c62..bd5bd8d4 100644 --- a/test/nahsh.js +++ b/test/nahsh.js @@ -18,6 +18,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testRefreshJourney = require('./lib/refresh-journey') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testDeparturesInDirection = require('./lib/departures-in-direction') @@ -186,6 +187,19 @@ test('earlier/later journeys, Kiel Hbf -> Flensburg', co(function* (t) { t.end() })) +test('refreshJourney', co(function* (t) { + yield testRefreshJourney({ + test: t, + fetchJourneys: client.journeys, + refreshJourney: client.refreshJourney, + validate, + fromId: kielHbf, + toId: flensburg, + when + }) + t.end() +})) + // todo: with detour test // todo: without detour test diff --git a/test/oebb.js b/test/oebb.js index f383e6ba..6ee84408 100644 --- a/test/oebb.js +++ b/test/oebb.js @@ -19,6 +19,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testRefreshJourney = require('./lib/refresh-journey') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testJourneysWithDetour = require('./lib/journeys-with-detour') const testDeparturesInDirection = require('./lib/departures-in-direction') @@ -213,6 +214,19 @@ test('earlier/later journeys, Salzburg Hbf -> Wien Westbahnhof', co(function* (t t.end() })) +test('refreshJourney', co(function* (t) { + yield testRefreshJourney({ + test: t, + fetchJourneys: client.journeys, + refreshJourney: client.refreshJourney, + validate, + fromId: salzburgHbf, + toId: wienWestbahnhof, + when + }) + t.end() +})) + test('trip details', co(function* (t) { const journeys = yield client.journeys(wienWestbahnhof, muenchenHbf, { results: 1, departure: when diff --git a/test/vbb.js b/test/vbb.js index 9968fcd4..219ee366 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -24,6 +24,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testRefreshJourney = require('./lib/refresh-journey') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testDeparturesInDirection = require('./lib/departures-in-direction') @@ -189,6 +190,19 @@ test('earlier/later journeys', co(function* (t) { t.end() })) +test('refreshJourney', co(function* (t) { + yield testRefreshJourney({ + test: t, + fetchJourneys: client.journeys, + refreshJourney: client.refreshJourney, + validate, + fromId: spichernstr, + toId: bismarckstr, + when + }) + t.end() +})) + test('trip details', co(function* (t) { const journeys = yield client.journeys(spichernstr, amrumerStr, { results: 1, departure: when diff --git a/test/vbn.js b/test/vbn.js index 241e442a..6b6bacea 100644 --- a/test/vbn.js +++ b/test/vbn.js @@ -14,6 +14,7 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testRefreshJourney = require('./lib/refresh-journey') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testArrivals = require('./lib/arrivals') @@ -146,6 +147,19 @@ test.skip('earlier/later journeys', co(function* (t) { t.end() })) +test.skip('refreshJourney', co(function* (t) { + yield testRefreshJourney({ + test: t, + fetchJourneys: client.journeys, + refreshJourney: client.refreshJourney, + validate, + fromId: bremenHbf, + toId: bremerhavenHbf, + when + }) + t.end() +})) + test.skip('trip details', co(function* (t) { const journeys = yield client.journeys(bremenHbf, bremerhavenHbf, { results: 1, departure: when From 067e807db738f915e339522705e9bc474c784eb0 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Tue, 24 Jul 2018 18:28:33 +0200 Subject: [PATCH 166/176] INSA doesn't support refreshJourney :bug: --- index.js | 3 ++- lib/default-profile.js | 3 ++- p/insa/index.js | 3 ++- test/insa.js | 14 -------------- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index 54235449..86adec98 100644 --- a/index.js +++ b/index.js @@ -458,9 +458,10 @@ const createClient = (profile, userAgent, request = _request) => { }) } - const client = {departures, arrivals, journeys, refreshJourney, locations, station, nearby} + const client = {departures, arrivals, journeys, locations, station, nearby} if (profile.trip) client.trip = trip if (profile.radar) client.radar = radar + if (profile.refreshJourney) client.refreshJourney = refreshJourney Object.defineProperty(client, 'profile', {value: profile}) return client } diff --git a/lib/default-profile.js b/lib/default-profile.js index c7c0f89b..1ed38de5 100644 --- a/lib/default-profile.js +++ b/lib/default-profile.js @@ -67,7 +67,8 @@ const defaultProfile = { journeysNumF: true, // `journeys()` method: support for `numF` field? trip: false, - radar: false + radar: false, + refreshJourney: true } module.exports = defaultProfile diff --git a/p/insa/index.js b/p/insa/index.js index c02b3445..bfd6b5b1 100644 --- a/p/insa/index.js +++ b/p/insa/index.js @@ -26,7 +26,8 @@ const insaProfile = { products: products, trip: true, - radar: true + radar: true, + refreshJourney: false } module.exports = insaProfile; diff --git a/test/insa.js b/test/insa.js index cca2762b..90aee588 100644 --- a/test/insa.js +++ b/test/insa.js @@ -14,7 +14,6 @@ const testJourneysStationToStation = require('./lib/journeys-station-to-station' const testJourneysStationToAddress = require('./lib/journeys-station-to-address') const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') -const testRefreshJourney = require('./lib/refresh-journey') const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') const testDepartures = require('./lib/departures') const testDeparturesInDirection = require('./lib/departures-in-direction') @@ -155,19 +154,6 @@ test('earlier/later journeys', co(function* (t) { t.end() })) -test('refreshJourney', co(function* (t) { - yield testRefreshJourney({ - test: t, - fetchJourneys: client.journeys, - refreshJourney: client.refreshJourney, - validate, - fromId: magdeburgHbf, - toId: magdeburgBuckau, - when - }) - t.end() -})) - test('trip details', co(function* (t) { const journeys = yield client.journeys(magdeburgHbf, magdeburgBuckau, { results: 1, departure: when From bec3249b01a4668a35b848003e30157a0f54341f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 25 Jul 2018 11:17:44 +0200 Subject: [PATCH 167/176] changelog for refreshJourney :memo:, tap-spec@5 --- docs/changelog.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 34450707..028445a1 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,6 +8,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( - 0db84ce #61 parse remarks for stopovers and journey legs - ac9819b `arrivals()` method – [docs](arrivals.md) +- 5b754aa `refreshJourney()` method – [docs](refresh-journey.md) - 21c273c `journeys()`/`trip()`: leg stopovers: parse & expose delays - 021ae45 `journeys()`/`trip()`: leg stopovers: parse & expose platforms - 84bce0c `arrivals()`/`departures()`: parse & expose platforms diff --git a/package.json b/package.json index 48374738..49e0c460 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "db-stations": "^2.3.0", "is-coordinates": "^2.0.2", "is-roughly-equal": "^0.1.0", - "tap-spec": "^4.1.1", + "tap-spec": "^5.0.0", "tape": "^4.8.0", "tape-promise": "^3.0.0", "validate-fptf": "^2.0.1", From 8653e43b2b903f4a841db1d2f5666dfb01112304 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 25 Jul 2018 11:24:01 +0200 Subject: [PATCH 168/176] refreshJourney: pass through userAgent :bug: --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 86adec98..3c3d1371 100644 --- a/index.js +++ b/index.js @@ -236,7 +236,7 @@ const createClient = (profile, userAgent, request = _request) => { remarks: true // parse & expose hints & warnings? }, opt) - return request(profile, opt, { + return request(profile, userAgent, opt, { meth: 'Reconstruction', req: { ctxRecon: refreshToken, From 96c1df5c3a28c5d13274fae385333ac61108cc84 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 26 Jul 2018 19:03:28 +0200 Subject: [PATCH 169/176] generate & send install-unique IDs --- .gitignore | 2 ++ lib/generate-install-id.js | 6 ++++++ lib/request.js | 12 +++++++++++- package.json | 3 ++- 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100755 lib/generate-install-id.js diff --git a/.gitignore b/.gitignore index e4ed3d2b..21bd24e5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ node_modules npm-debug.log package-lock.json + +/id.json diff --git a/lib/generate-install-id.js b/lib/generate-install-id.js new file mode 100755 index 00000000..14235608 --- /dev/null +++ b/lib/generate-install-id.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +const {randomBytes} = require('crypto') + +const id = randomBytes(6).toString('hex') +process.stdout.write(JSON.stringify(id) + '\n') diff --git a/lib/request.js b/lib/request.js index 00396906..2567f082 100644 --- a/lib/request.js +++ b/lib/request.js @@ -1,5 +1,6 @@ 'use strict' +const {join} = require('path') const createHash = require('create-hash') let captureStackTrace = () => {} if (process.env.NODE_DEBUG === 'hafas-client') { @@ -9,6 +10,15 @@ const {stringify} = require('query-string') const Promise = require('pinkie-promise') const {fetch} = require('fetch-ponyfill')({Promise}) +let id +try { + id = require('../id.json') +} catch (err) { + const p = join(__dirname, '..', 'id.json') + console.error(`Failed to load the install-unique ID from ${p}.`) + process.exit(1) +} + const md5 = input => createHash('md5').update(input).digest() const request = (profile, userAgent, opt, data) => { @@ -24,7 +34,7 @@ const request = (profile, userAgent, opt, data) => { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'Accept': 'application/json', - 'user-agent': userAgent + 'user-agent': id + ' ' + userAgent }, query: {} }) diff --git a/package.json b/package.json index 49e0c460..83ec9439 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ }, "scripts": { "test": "env NODE_ENV=dev NODE_DEBUG=hafas-client node test/index.js", - "prepublishOnly": "npm test | tap-spec" + "prepublishOnly": "npm test | tap-spec", + "install": "lib/generate-install-id.js >id.json" } } From e20f65823bf4f5ff281c49ffe85eac7264944257 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 28 Jul 2018 14:55:14 +0200 Subject: [PATCH 170/176] adapt profile docs to 5d9d738 :memo: --- p/db/readme.md | 2 +- p/insa/readme.md | 2 +- p/nahsh/readme.md | 2 +- p/oebb/readme.md | 2 +- p/vbb/readme.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/p/db/readme.md b/p/db/readme.md index 0d566c74..d55e0870 100644 --- a/p/db/readme.md +++ b/p/db/readme.md @@ -9,7 +9,7 @@ const createClient = require('hafas-client') const dbProfile = require('hafas-client/p/db') // create a client with DB profile -const client = createClient(dbProfile) +const client = createClient(dbProfile, 'my-awesome-program') ``` diff --git a/p/insa/readme.md b/p/insa/readme.md index 4918b2b4..4c0675e7 100644 --- a/p/insa/readme.md +++ b/p/insa/readme.md @@ -9,7 +9,7 @@ const createClient = require('hafas-client') const insaProfile = require('hafas-client/p/insa') // create a client with INSA profile -const client = createClient(insaProfile) +const client = createClient(insaProfile, 'my-awesome-program') ``` diff --git a/p/nahsh/readme.md b/p/nahsh/readme.md index 9d9bdad7..c67a4136 100644 --- a/p/nahsh/readme.md +++ b/p/nahsh/readme.md @@ -9,7 +9,7 @@ const createClient = require('hafas-client') const nahshProfile = require('hafas-client/p/nahsh') // create a client with NAH.SH profile -const client = createClient(nahshProfile) +const client = createClient(nahshProfile, 'my-awesome-program') ``` diff --git a/p/oebb/readme.md b/p/oebb/readme.md index f45b9c28..7a9a6d19 100644 --- a/p/oebb/readme.md +++ b/p/oebb/readme.md @@ -9,7 +9,7 @@ const createClient = require('hafas-client') const oebbProfile = require('hafas-client/p/oebb') // create a client with ÖBB profile -const client = createClient(oebbProfile) +const client = createClient(oebbProfile, 'my-awesome-program') ``` diff --git a/p/vbb/readme.md b/p/vbb/readme.md index 35b4b89a..51d4c0eb 100644 --- a/p/vbb/readme.md +++ b/p/vbb/readme.md @@ -9,7 +9,7 @@ const createClient = require('hafas-client') const vbbProfile = require('hafas-client/p/vbb') // create a client with VBB profile -const client = createClient(vbbProfile) +const client = createClient(vbbProfile, 'my-awesome-program') ``` From a3ad876bf5d324cd5f0bd41c1e994f4bd66cb2ec Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 28 Jul 2018 13:43:15 +0200 Subject: [PATCH 171/176] BVG profile --- p/bvg/example.js | 37 +++++++++++++++ p/bvg/index.js | 112 ++++++++++++++++++++++++++++++++++++++++++++++ p/bvg/products.js | 60 +++++++++++++++++++++++++ p/bvg/readme.md | 22 +++++++++ 4 files changed, 231 insertions(+) create mode 100644 p/bvg/example.js create mode 100644 p/bvg/index.js create mode 100644 p/bvg/products.js create mode 100644 p/bvg/readme.md diff --git a/p/bvg/example.js b/p/bvg/example.js new file mode 100644 index 00000000..5a79930f --- /dev/null +++ b/p/bvg/example.js @@ -0,0 +1,37 @@ +'use strict' + +const createClient = require('../..') +const vbbProfile = require('.') + +const client = createClient(vbbProfile, 'hafas-client-example') + +// Hauptbahnhof to Charlottenburg +client.journeys('900000003201', '900000024101', {results: 1, polylines: true}) +// client.departures('900000013102', {duration: 1}) +// client.arrivals('900000013102', {duration: 10, stationLines: true}) +// client.locations('Alexanderplatz', {results: 2}) +// client.station('900000042101', {stationLines: true}) // Spichernstr +// client.nearby({ +// type: 'location', +// latitude: 52.5137344, +// longitude: 13.4744798 +// }, {distance: 60}) +// client.radar({ +// north: 52.52411, +// west: 13.41002, +// south: 52.51942, +// east: 13.41709 +// }, {results: 10}) + +// .then(([journey]) => { +// const leg = journey.legs[0] +// return client.trip(leg.id, leg.line.name, {polyline: true}) +// }) + +// .then(([journey]) => { +// return client.refreshJourney(journey.refreshToken, {stopovers: true, remarks: true}) +// }) +.then((data) => { + console.log(require('util').inspect(data, {depth: null})) +}) +.catch(console.error) diff --git a/p/bvg/index.js b/p/bvg/index.js new file mode 100644 index 00000000..b3057a44 --- /dev/null +++ b/p/bvg/index.js @@ -0,0 +1,112 @@ +'use strict' + +const shorten = require('vbb-short-station-name') +const {to12Digit, to9Digit} = require('vbb-translate-ids') +const parseLineName = require('vbb-parse-line') +const getStations = require('vbb-stations') + +const _createParseLine = require('../../parse/line') +const _parseLocation = require('../../parse/location') +const _createParseDeparture = require('../../parse/departure') +const _formatStation = require('../../format/station') + +const products = require('./products') + +const transformReqBody = (body) => { + body.client = {type: 'IPA', id: 'BVG', name: 'FahrInfo', v: '4070700'} + body.ext = 'BVG.1' + body.ver = '1.15' // todo: 1.16 with `mic` and `mac` query params + body.auth = {type: 'AID', aid: '1Rxs112shyHLatUX4fofnmdxK'} + + return body +} + +const createParseLine = (profile, opt, data) => { + const parseLine = _createParseLine(profile, opt, data) + + const parseLineWithMoreDetails = (l) => { + const res = parseLine(l) + + res.name = l.name.replace(/^(bus|tram)\s+/i, '') + const details = parseLineName(res.name) + res.symbol = details.symbol + res.nr = details.nr + res.metro = details.metro + res.express = details.express + res.night = details.night + + return res + } + return parseLineWithMoreDetails +} + +const parseLocation = (profile, opt, data, l) => { + const res = _parseLocation(profile, opt, data, l) + + if (res.type === 'stop' || res.type === 'station') { + res.name = shorten(res.name) + res.id = to12Digit(res.id) + if (!res.location.latitude || !res.location.longitude) { + const [s] = getStations(res.id) + if (s) Object.assign(res.location, s.location) + } + } + return res +} + +const createParseDeparture = (profile, opt, data) => { + const parseDeparture = _createParseDeparture(profile, opt, data) + + const ringbahnClockwise = /^ringbahn s\s?41$/i + const ringbahnAnticlockwise = /^ringbahn s\s?42$/i + const parseDepartureRenameRingbahn = (j) => { + const res = parseDeparture(j) + + if (res.line && res.line.product === 'suburban') { + const d = res.direction && res.direction.trim() + if (ringbahnClockwise.test(d)) res.direction = 'Ringbahn S41 ⟳' + else if (ringbahnAnticlockwise.test(d)) res.direction = 'Ringbahn S42 ⟲' + } + + return res + } + + return parseDepartureRenameRingbahn +} + +const validIBNR = /^\d+$/ +const formatStation = (id) => { + if ('string' !== typeof id) throw new Error('station ID must be a string.') + const l = id.length + if ((l !== 7 && l !== 9 && l !== 12) || !validIBNR.test(id)) { + throw new Error('station ID must be a valid IBNR.') + } + // BVG has some 7-digit stations. We don't convert them to 12 digits, + // because it only recognizes in the 7-digit format. see derhuerst/vbb-hafas#22 + if (l !== 7) id = to9Digit(id) + return _formatStation(id) +} + +// todo: adapt/extend `vbb-parse-ticket` to support the BVG markup + +const bvgProfile = { + locale: 'de-DE', + timezone: 'Europe/Berlin', + endpoint: 'https://bvg-apps.hafas.de/bin/mgate.exe', + + transformReqBody, + + products, + + parseStationName: shorten, + parseLocation, + parseLine: createParseLine, + parseDeparture: createParseDeparture, + + formatStation, + + trip: true, + radar: true +} + +module.exports = bvgProfile diff --git a/p/bvg/products.js b/p/bvg/products.js new file mode 100644 index 00000000..6d091247 --- /dev/null +++ b/p/bvg/products.js @@ -0,0 +1,60 @@ +'use strict' + +module.exports = [ + { + id: 'suburban', + mode: 'train', + bitmasks: [1], + name: 'S-Bahn', + short: 'S', + default: true + }, + { + id: 'subway', + mode: 'train', + bitmasks: [2], + name: 'U-Bahn', + short: 'U', + default: true + }, + { + id: 'tram', + mode: 'train', + bitmasks: [4], + name: 'Tram', + short: 'T', + default: true + }, + { + id: 'bus', + mode: 'bus', + bitmasks: [8], + name: 'Bus', + short: 'B', + default: true + }, + { + id: 'ferry', + mode: 'watercraft', + bitmasks: [16], + name: 'Fähre', + short: 'F', + default: true + }, + { + id: 'express', + mode: 'train', + bitmasks: [32], + name: 'IC/ICE', + short: 'E', + default: true + }, + { + id: 'regional', + mode: 'train', + bitmasks: [64], + name: 'RB/RE', + short: 'R', + default: true + } +] diff --git a/p/bvg/readme.md b/p/bvg/readme.md new file mode 100644 index 00000000..35b4b89a --- /dev/null +++ b/p/bvg/readme.md @@ -0,0 +1,22 @@ +# VBB profile for `hafas-client` + +[*Verkehrsverbund Berlin-Brandenburg (VBB)*](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) is a group of public transport companies, running the public transport network in [Berlin](https://en.wikipedia.org/wiki/Berlin). This profile adds *VBB*-specific customizations to `hafas-client`. Consider using [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas#vbb-hafas), to always get the customized client right away. + +## Usage + +```js +const createClient = require('hafas-client') +const vbbProfile = require('hafas-client/p/vbb') + +// create a client with VBB profile +const client = createClient(vbbProfile) +``` + + +## Customisations + +- parses *VBB*-specific products (such as *X-Bus*) +- strips parts from station names that are unnecessary in the Berlin context +- parses line names to give more information (e.g. "Is it an express bus?") +- parses *VBB*-specific tickets +- renames *Ringbahn* line names to contain `⟳` and `⟲` From 05bd54f021de9c27310570e38364e4732eb533c6 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 28 Jul 2018 13:43:58 +0200 Subject: [PATCH 172/176] tests for BVG :white_check_mark: --- test/bvg.js | 369 +++++++++++++++++++++++++++++++++ test/index.js | 1 + test/lib/vbb-bvg-validators.js | 87 ++++++++ test/vbb.js | 81 +------- 4 files changed, 465 insertions(+), 73 deletions(-) create mode 100644 test/bvg.js create mode 100644 test/lib/vbb-bvg-validators.js diff --git a/test/bvg.js b/test/bvg.js new file mode 100644 index 00000000..f721d3a2 --- /dev/null +++ b/test/bvg.js @@ -0,0 +1,369 @@ +'use strict' + +// todo: DRY with vbb tests + +const stations = require('vbb-stations-autocomplete') +const a = require('assert') +const shorten = require('vbb-short-station-name') +const tapePromise = require('tape-promise').default +const tape = require('tape') +const isRoughlyEqual = require('is-roughly-equal') + +const co = require('./lib/co') +const createClient = require('..') +const bvgProfile = require('../p/bvg') +const products = require('../p/bvg/products') +const createValidate = require('./lib/validate-fptf-with') +const { + cfg, + validateStation, + validateLine, + validateJourneyLeg, + validateDeparture, + validateMovement +} = require('./lib/vbb-bvg-validators') +const testJourneysStationToStation = require('./lib/journeys-station-to-station') +const testJourneysStationToAddress = require('./lib/journeys-station-to-address') +const testJourneysStationToPoi = require('./lib/journeys-station-to-poi') +const testEarlierLaterJourneys = require('./lib/earlier-later-journeys') +const testRefreshJourney = require('./lib/refresh-journey') +const journeysFailsWithNoProduct = require('./lib/journeys-fails-with-no-product') +const testDepartures = require('./lib/departures') +const testDeparturesInDirection = require('./lib/departures-in-direction') +const testDeparturesWithoutRelatedStations = require('./lib/departures-without-related-stations') +const testArrivals = require('./lib/arrivals') +const testJourneysWithDetour = require('./lib/journeys-with-detour') + +const when = cfg.when + +const validateDirection = (dir, name) => { + if (!stations(dir, true, false)[0]) { + console.error(name + `: station "${dir}" is unknown`) + } +} + +const validate = createValidate(cfg, { + station: validateStation, + line: validateLine, + journeyLeg: validateJourneyLeg, + departure: validateDeparture, + movement: validateMovement +}) + +const test = tapePromise(tape) +const client = createClient(bvgProfile, 'public-transport/hafas-client:test') + +const amrumerStr = '900000009101' +const spichernstr = '900000042101' +const bismarckstr = '900000024201' +const westhafen = '900000001201' +const wedding = '900000009104' +const württembergallee = '900000026153' + +test('journeys – Spichernstr. to Bismarckstr.', co(function* (t) { + const journeys = yield client.journeys(spichernstr, bismarckstr, { + results: 3, + departure: when, + stopovers: true + }) + + yield testJourneysStationToStation({ + test: t, + journeys, + validate, + fromId: spichernstr, + toId: bismarckstr + }) + // todo: find a journey where there ticket info is always available + + t.end() +})) + +test('journeys – only subway', co(function* (t) { + const journeys = yield client.journeys(spichernstr, bismarckstr, { + results: 20, + departure: when, + products: { + suburban: false, + subway: true, + tram: false, + bus: false, + ferry: false, + express: false, + regional: false + } + }) + + validate(t, journeys, 'journeys', 'journeys') + t.ok(journeys.length > 1) + for (let i = 0; i < journeys.length; i++) { + const journey = journeys[i] + for (let j = 0; j < journey.legs.length; j++) { + const leg = journey.legs[j] + + const name = `journeys[${i}].legs[${i}].line` + if (leg.line) { + t.equal(leg.line.mode, 'train', name + '.mode is invalid') + t.equal(leg.line.product, 'subway', name + '.product is invalid') + } + t.ok(journey.legs.some(l => l.line), name + '.legs has no subway leg') + } + } + + t.end() +})) + +test('journeys – fails with no product', (t) => { + journeysFailsWithNoProduct({ + test: t, + fetchJourneys: client.journeys, + fromId: spichernstr, + toId: bismarckstr, + when, + products + }) + t.end() +}) + +test('earlier/later journeys', co(function* (t) { + yield testEarlierLaterJourneys({ + test: t, + fetchJourneys: client.journeys, + validate, + fromId: spichernstr, + toId: bismarckstr + }) + + t.end() +})) + +test('refreshJourney', co(function* (t) { + yield testRefreshJourney({ + test: t, + fetchJourneys: client.journeys, + refreshJourney: client.refreshJourney, + validate, + fromId: spichernstr, + toId: bismarckstr, + when + }) + t.end() +})) + +test('trip details', co(function* (t) { + const journeys = yield client.journeys(spichernstr, amrumerStr, { + results: 1, departure: when + }) + + const p = journeys[0].legs[0] + t.ok(p.id, 'precondition failed') + t.ok(p.line.name, 'precondition failed') + const trip = yield client.trip(p.id, p.line.name, {when}) + + validate(t, trip, 'journeyLeg', 'trip') + t.end() +})) + +test('journeys – station to address', co(function* (t) { + const torfstr = { + type: 'location', + address: '13353 Berlin-Wedding, Torfstr. 17', + latitude: 52.541797, + longitude: 13.350042 + } + const journeys = yield client.journeys(spichernstr, torfstr, { + results: 3, + departure: when + }) + + yield testJourneysStationToAddress({ + test: t, + journeys, + validate, + fromId: spichernstr, + to: torfstr + }) + t.end() +})) + +test('journeys – station to POI', co(function* (t) { + const atze = { + type: 'location', + id: '900980720', + name: 'Berlin, Atze Musiktheater für Kinder', + latitude: 52.543333, + longitude: 13.351686 + } + const journeys = yield client.journeys(spichernstr, atze, { + results: 3, + departure: when + }) + + yield testJourneysStationToPoi({ + test: t, + journeys, + validate, + fromId: spichernstr, + to: atze + }) + t.end() +})) + +test('journeys: via works – with detour', co(function* (t) { + // Going from Westhafen to Wedding via Württembergalle without detour + // is currently impossible. We check if the routing engine computes a detour. + const journeys = yield client.journeys(westhafen, wedding, { + via: württembergallee, + results: 1, + departure: when, + stopovers: true + }) + + yield testJourneysWithDetour({ + test: t, + journeys, + validate, + detourIds: [württembergallee] + }) + t.end() +})) + +// todo: without detour test + +test('departures', co(function* (t) { + const departures = yield client.departures(spichernstr, { + duration: 5, when + }) + + yield testDepartures({ + test: t, + departures, + validate, + id: spichernstr + }) + t.end() +})) + +test('departures with station object', co(function* (t) { + const deps = yield client.departures({ + type: 'station', + id: spichernstr, + name: 'U Spichernstr', + location: { + type: 'location', + latitude: 1.23, + longitude: 2.34 + } + }, {when}) + + validate(t, deps, 'departures', 'departures') + t.end() +})) + +test('departures at Spichernstr. in direction of Westhafen', co(function* (t) { + yield testDeparturesInDirection({ + test: t, + fetchDepartures: client.departures, + fetchTrip: client.trip, + id: spichernstr, + directionIds: [westhafen], + when, + validate + }) + t.end() +})) + +test('departures at 7-digit station', co(function* (t) { + const eisenach = '8010097' // see derhuerst/vbb-hafas#22 + yield client.departures(eisenach, {when}) + t.pass('did not fail') + t.end() +})) + +test('departures without related stations', co(function* (t) { + yield testDeparturesWithoutRelatedStations({ + test: t, + fetchDepartures: client.departures, + id: '900000024101', // Charlottenburg + when, + products: {bus: false, suburban: false, regional: false}, + linesOfRelatedStations: ['U7'] + }) + t.end() +})) + +test('arrivals', co(function* (t) { + const arrivals = yield client.arrivals(spichernstr, { + duration: 5, when + }) + + yield testArrivals({ + test: t, + arrivals, + validate, + id: spichernstr + }) + t.end() +})) + +test('nearby', co(function* (t) { + const berlinerStr = '900000044201' + const landhausstr = '900000043252' + + // Berliner Str./Bundesallee + const nearby = yield client.nearby({ + type: 'location', + latitude: 52.4873452, + longitude: 13.3310411 + }, {distance: 200}) + + validate(t, nearby, 'locations', 'nearby') + + t.equal(nearby[0].id, berlinerStr) + t.equal(nearby[0].name, 'U Berliner Str.') + t.ok(nearby[0].distance > 0) + t.ok(nearby[0].distance < 100) + + t.equal(nearby[1].id, landhausstr) + t.equal(nearby[1].name, 'Landhausstr.') + t.ok(nearby[1].distance > 100) + t.ok(nearby[1].distance < 200) + + t.end() +})) + +test('locations', co(function* (t) { + const locations = yield client.locations('Alexanderplatz', {results: 20}) + + validate(t, locations, 'locations', 'locations') + t.ok(locations.length <= 20) + + t.ok(locations.find(s => s.type === 'stop' || s.type === 'station')) + t.ok(locations.find(s => s.id && s.name)) // POIs + t.ok(locations.find(s => !s.name && s.address)) // addresses + + t.end() +})) + +test('station', co(function* (t) { + const s = yield client.station(spichernstr) + + validate(t, s, ['stop', 'station'], 'station') + t.equal(s.id, spichernstr) + + t.end() +})) + +test('radar', co(function* (t) { + const vehicles = yield client.radar({ + north: 52.52411, + west: 13.41002, + south: 52.51942, + east: 13.41709 + }, { + duration: 5 * 60, when + }) + + validate(t, vehicles, 'movements', 'vehicles') + t.end() +})) diff --git a/test/index.js b/test/index.js index 4762f78a..2b893d16 100644 --- a/test/index.js +++ b/test/index.js @@ -2,6 +2,7 @@ require('./db') require('./vbb') +require('./bvg') require('./oebb') require('./insa') require('./nahsh') diff --git a/test/lib/vbb-bvg-validators.js b/test/lib/vbb-bvg-validators.js new file mode 100644 index 00000000..4801e216 --- /dev/null +++ b/test/lib/vbb-bvg-validators.js @@ -0,0 +1,87 @@ +'use strict' + +const stations = require('vbb-stations-autocomplete') +const a = require('assert') +const shorten = require('vbb-short-station-name') +const products = require('../../p/bvg/products') + +const {createWhen} = require('./util') +const { + station: createValidateStation, + line: createValidateLine, + journeyLeg: createValidateJourneyLeg, + departure: createValidateDeparture, + movement: _validateMovement +} = require('./validators') + +const when = createWhen('Europe/Berlin', 'de-DE') + +const cfg = { + when, + stationCoordsOptional: false, + products +} + +const validateDirection = (dir, name) => { + if (!stations(dir, true, false)[0]) { + console.error(name + `: station "${dir}" is unknown`) + } +} + +// todo: coordsOptional = false +const _validateStation = createValidateStation(cfg) +const validateStation = (validate, s, name) => { + _validateStation(validate, s, name) + // todo: find station by ID + a.equal(s.name, shorten(s.name), name + '.name must be shortened') +} + +const _validateLine = createValidateLine(cfg) +const validateLine = (validate, l, name) => { + _validateLine(validate, l, name) + if (l.symbol !== null) { + a.strictEqual(typeof l.symbol, 'string', name + '.symbol must be a string') + a.ok(l.symbol, name + '.symbol must not be empty') + } + if (l.nr !== null) { + a.strictEqual(typeof l.nr, 'number', name + '.nr must be a string') + a.ok(l.nr, name + '.nr must not be empty') + } + if (l.metro !== null) { + a.strictEqual(typeof l.metro, 'boolean', name + '.metro must be a boolean') + } + if (l.express !== null) { + a.strictEqual(typeof l.express, 'boolean', name + '.express must be a boolean') + } + if (l.night !== null) { + a.strictEqual(typeof l.night, 'boolean', name + '.night must be a boolean') + } +} + +const _validateJourneyLeg = createValidateJourneyLeg(cfg) +const validateJourneyLeg = (validate, l, name) => { + _validateJourneyLeg(validate, l, name) + if (l.mode !== 'walking') { + validateDirection(l.direction, name + '.direction') + } +} + +const _validateDeparture = createValidateDeparture(cfg) +const validateDeparture = (validate, dep, name) => { + _validateDeparture(validate, dep, name) + validateDirection(dep.direction, name + '.direction') +} + +const validateMovement = (validate, m, name) => { + _validateMovement(validate, m, name) + validateDirection(m.direction, name + '.direction') +} + +module.exports = { + cfg, + validateStation, + validateLine, + validateJourneyLeg, + validateDeparture, + validateMovement +} diff --git a/test/vbb.js b/test/vbb.js index 219ee366..01aad2e6 100644 --- a/test/vbb.js +++ b/test/vbb.js @@ -1,24 +1,20 @@ 'use strict' -const stations = require('vbb-stations-autocomplete') -const a = require('assert') -const shorten = require('vbb-short-station-name') const tapePromise = require('tape-promise').default const tape = require('tape') -const isRoughlyEqual = require('is-roughly-equal') -const {createWhen} = require('./lib/util') const co = require('./lib/co') const createClient = require('..') const vbbProfile = require('../p/vbb') const products = require('../p/vbb/products') const { - station: createValidateStation, - line: createValidateLine, - journeyLeg: createValidateJourneyLeg, - departure: createValidateDeparture, - movement: _validateMovement -} = require('./lib/validators') + cfg, + validateStation, + validateLine, + validateJourneyLeg, + validateDeparture, + validateMovement +} = require('./lib/vbb-bvg-validators') const createValidate = require('./lib/validate-fptf-with') const testJourneysStationToStation = require('./lib/journeys-station-to-station') const testJourneysStationToAddress = require('./lib/journeys-station-to-address') @@ -32,68 +28,7 @@ const testDeparturesWithoutRelatedStations = require('./lib/departures-without-r const testArrivals = require('./lib/arrivals') const testJourneysWithDetour = require('./lib/journeys-with-detour') -const when = createWhen('Europe/Berlin', 'de-DE') - -const cfg = { - when, - stationCoordsOptional: false, - products -} - -const validateDirection = (dir, name) => { - if (!stations(dir, true, false)[0]) { - console.error(name + `: station "${dir}" is unknown`) - } -} - -// todo: coordsOptional = false -const _validateStation = createValidateStation(cfg) -const validateStation = (validate, s, name) => { - _validateStation(validate, s, name) - // todo: find station by ID - a.equal(s.name, shorten(s.name), name + '.name must be shortened') -} - -const _validateLine = createValidateLine(cfg) -const validateLine = (validate, l, name) => { - _validateLine(validate, l, name) - if (l.symbol !== null) { - a.strictEqual(typeof l.symbol, 'string', name + '.symbol must be a string') - a.ok(l.symbol, name + '.symbol must not be empty') - } - if (l.nr !== null) { - a.strictEqual(typeof l.nr, 'number', name + '.nr must be a string') - a.ok(l.nr, name + '.nr must not be empty') - } - if (l.metro !== null) { - a.strictEqual(typeof l.metro, 'boolean', name + '.metro must be a boolean') - } - if (l.express !== null) { - a.strictEqual(typeof l.express, 'boolean', name + '.express must be a boolean') - } - if (l.night !== null) { - a.strictEqual(typeof l.night, 'boolean', name + '.night must be a boolean') - } -} - -const _validateJourneyLeg = createValidateJourneyLeg(cfg) -const validateJourneyLeg = (validate, l, name) => { - _validateJourneyLeg(validate, l, name) - if (l.mode !== 'walking') { - validateDirection(l.direction, name + '.direction') - } -} - -const _validateDeparture = createValidateDeparture(cfg) -const validateDeparture = (validate, dep, name) => { - _validateDeparture(validate, dep, name) - validateDirection(dep.direction, name + '.direction') -} - -const validateMovement = (validate, m, name) => { - _validateMovement(validate, m, name) - validateDirection(m.direction, name + '.direction') -} +const when = cfg.when const validate = createValidate(cfg, { station: validateStation, From 0466e570ad3fcdc952dc99da1ef30a084ab79f13 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Sat, 28 Jul 2018 13:54:44 +0200 Subject: [PATCH 173/176] docs for BVG profile :memo: --- docs/changelog.md | 1 + p/bvg/readme.md | 13 ++++++------- readme.md | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 028445a1..dcefb19f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,7 @@ This version is not fully backwords-compatible. Check out [the migration guide]( ### new features ✨ +- 2d3796a BVG profile - 0db84ce #61 parse remarks for stopovers and journey legs - ac9819b `arrivals()` method – [docs](arrivals.md) - 5b754aa `refreshJourney()` method – [docs](refresh-journey.md) diff --git a/p/bvg/readme.md b/p/bvg/readme.md index 35b4b89a..0304acb2 100644 --- a/p/bvg/readme.md +++ b/p/bvg/readme.md @@ -1,22 +1,21 @@ -# VBB profile for `hafas-client` +# BVG profile for `hafas-client` -[*Verkehrsverbund Berlin-Brandenburg (VBB)*](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) is a group of public transport companies, running the public transport network in [Berlin](https://en.wikipedia.org/wiki/Berlin). This profile adds *VBB*-specific customizations to `hafas-client`. Consider using [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas#vbb-hafas), to always get the customized client right away. +[*Verkehrsverbund Berlin-Brandenburg (BVG)*](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) is the major local transport provider in [Berlin](https://en.wikipedia.org/wiki/Berlin). This profile adds *BVG*-specific customizations to `hafas-client`. ## Usage ```js const createClient = require('hafas-client') -const vbbProfile = require('hafas-client/p/vbb') +const bvgProfile = require('hafas-client/p/bvg') -// create a client with VBB profile -const client = createClient(vbbProfile) +// create a client with BVG profile +const client = createClient(bvgProfile, 'my-awesome-program') ``` ## Customisations -- parses *VBB*-specific products (such as *X-Bus*) +- parses *BVG*-specific products (such as *X-Bus*) - strips parts from station names that are unnecessary in the Berlin context - parses line names to give more information (e.g. "Is it an express bus?") -- parses *VBB*-specific tickets - renames *Ringbahn* line names to contain `⟳` and `⟲` diff --git a/readme.md b/readme.md index bad03961..e972c704 100644 --- a/readme.md +++ b/readme.md @@ -6,6 +6,7 @@ HAFAS endpoint | wrapper library | docs | example code | source code ---------------|------------------|------|---------|------------ [Deutsche Bahn (DB)](https://en.wikipedia.org/wiki/Deutsche_Bahn) | [`db-hafas`](https://github.com/derhuerst/db-hafas) | [docs](p/db/readme.md) | [example code](p/db/example.js) | [src](p/db/index.js) [Berlin & Brandenburg public transport (VBB)](https://en.wikipedia.org/wiki/Verkehrsverbund_Berlin-Brandenburg) | [`vbb-hafas`](https://github.com/derhuerst/vbb-hafas) | [docs](p/vbb/readme.md) | [example code](p/vbb/example.js) | [src](p/vbb/index.js) +[Berlin public transport (BVG)](https://en.wikipedia.org/wiki/Berliner_Verkehrsbetriebe) | – | [docs](p/bvg/readme.md) | [example code](p/bvg/example.js) | [src](p/bvg/index.js) [Österreichische Bundesbahnen (ÖBB)](https://en.wikipedia.org/wiki/Austrian_Federal_Railways) | [`oebb-hafas`](https://github.com/juliuste/oebb-hafas) | [docs](p/oebb/readme.md) | [example code](p/oebb/example.js) | [src](p/oebb/index.js) [Nahverkehr Sachsen-Anhalt (NASA)](https://de.wikipedia.org/wiki/Nahverkehrsservice_Sachsen-Anhalt)/[INSA](https://insa.de) | [`insa-hafas`](https://github.com/derhuerst/insa-hafas) | [docs](p/insa/readme.md) | [example code](p/insa/example.js) | [src](p/insa/index.js) [Nahverkehrsverbund Schleswig-Holstein (NAH.SH)](https://de.wikipedia.org/wiki/Nahverkehrsverbund_Schleswig-Holstein) | [`nahsh-hafas`](https://github.com/juliuste/nahsh-hafas) | [docs](p/nahsh/readme.md) | [example code](p/nahsh/example.js) | [src](p/nahsh/index.js) From ffc392b66b5f11a94cf6586ceae156ffed396aa8 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Mon, 30 Jul 2018 10:45:45 +0200 Subject: [PATCH 174/176] docs: link to FPTF 1.1.1, minor fixes :memo: [ci skip] --- docs/departures.md | 2 +- docs/journeys.md | 2 +- docs/nearby.md | 2 +- docs/radar.md | 2 +- docs/refresh-journey.md | 2 +- docs/trip.md | 2 +- docs/writing-a-profile.md | 8 ++++---- p/oebb/readme.md | 2 +- readme.md | 4 ++-- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/departures.md b/docs/departures.md index 33651a1d..7614d7c8 100644 --- a/docs/departures.md +++ b/docs/departures.md @@ -38,7 +38,7 @@ With `opt`, you can override the default options, which look like this: ## Response -*Note:* As stated in the [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1), the `when` field includes the current delay. The `delay` field, if present, expresses how much the former differs from the schedule. +*Note:* As stated in the [*Friendly Public Transport Format* `1.1.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.1.1), the `when` field includes the current delay. The `delay` field, if present, expresses how much the former differs from the schedule. You may pass the `tripId` field into [`trip(id, lineName, [opt])`](trip.md) to get details on the vehicle's trip. diff --git a/docs/journeys.md b/docs/journeys.md index f7be35c1..3242eb23 100644 --- a/docs/journeys.md +++ b/docs/journeys.md @@ -74,7 +74,7 @@ With `opt`, you can override the default options, which look like this: ## Response -*Note:* As stated in the [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule. +*Note:* As stated in the [*Friendly Public Transport Format* `1.1.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.1.1), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule. As an example, we're going to use the [VBB profile](../p/vbb): diff --git a/docs/nearby.md b/docs/nearby.md index 20186c46..21123b8f 100644 --- a/docs/nearby.md +++ b/docs/nearby.md @@ -2,7 +2,7 @@ This method can be used to find stations close to a location. Note that it is not supported by every profile/endpoint. -`location` must be an [*FPTF* `location` object](https://github.com/public-transport/friendly-public-transport-format/blob/1.0.1/spec/readme.md#location-objects). +`location` must be an [*FPTF* `location` object](https://github.com/public-transport/friendly-public-transport-format/blob/1.1.1/spec/readme.md#location-objects). With `opt`, you can override the default options, which look like this: diff --git a/docs/radar.md b/docs/radar.md index f5502308..308f6dce 100644 --- a/docs/radar.md +++ b/docs/radar.md @@ -18,7 +18,7 @@ With `opt`, you can override the default options, which look like this: ## Response -*Note:* As stated in the [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule. +*Note:* As stated in the [*Friendly Public Transport Format* `1.1.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.1.1), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule. As an example, we're going to use the [VBB profile](../p/vbb): diff --git a/docs/refresh-journey.md b/docs/refresh-journey.md index 94e6025c..1d2da5e6 100644 --- a/docs/refresh-journey.md +++ b/docs/refresh-journey.md @@ -35,4 +35,4 @@ client.journeys('900000003201', '900000100008', {results: 1}) .catch(console.error) ``` -`refreshJourney()` will return a *single* [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1) `journey`, in the same format as with `journeys()`. +`refreshJourney()` will return a *single* [*Friendly Public Transport Format* `1.1.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.1.1) `journey`, in the same format as with `journeys()`. diff --git a/docs/trip.md b/docs/trip.md index 9d1b34fe..b4f0976e 100644 --- a/docs/trip.md +++ b/docs/trip.md @@ -36,7 +36,7 @@ With `opt`, you can override the default options, which look like this: ## Response -*Note:* As stated in the [*Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.0.1), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule. +*Note:* As stated in the [*Friendly Public Transport Format* `1.1.1`](https://github.com/public-transport/friendly-public-transport-format/tree/1.1.1), the returned `departure` and `arrival` times include the current delay. The `departureDelay`/`arrivalDelay` fields express how much they differ from the schedule. As an example, we're going to use the [VBB profile](../p/vbb): diff --git a/docs/writing-a-profile.md b/docs/writing-a-profile.md index 01bee324..426dfae2 100644 --- a/docs/writing-a-profile.md +++ b/docs/writing-a-profile.md @@ -84,7 +84,7 @@ You may want to use the [profile boilerplate code](profile-boilerplate.js). In `hafas-client`, there's a difference between the `mode` and the `product` field: -- The `mode` field describes the mode of transport in general. [Standardised by the *Friendly Public Transport Format* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/blob/1.0.1/spec/readme.md#modes), it is on purpose limited to a very small number of possible values, e.g. `train` or `bus`. +- The `mode` field describes the mode of transport in general. [Standardised by the *Friendly Public Transport Format* `1.1.1`](https://github.com/public-transport/friendly-public-transport-format/blob/1.1.1/spec/readme.md#modes), it is on purpose limited to a very small number of possible values, e.g. `train` or `bus`. - The value for `product` relates to how a means of transport "works" *in local context*. Example: Even though [*S-Bahn*](https://en.wikipedia.org/wiki/Berlin_S-Bahn) and [*U-Bahn*](https://en.wikipedia.org/wiki/Berlin_U-Bahn) in Berlin are both `train`s, they have different operators, service patterns, stations and look different. Therefore, they are two distinct `product`s `subway` and `suburban`. **Specify `product`s that appear in the app** you recorded requests of. For a fictional transit network, this may look like this: @@ -113,13 +113,13 @@ const products = [ Let's break this down: - `id`: A sensible, [camelCased](https://en.wikipedia.org/wiki/Camel_case#Variations_and_synonyms), alphanumeric identifier. Use it for the key in the `products` array as well. -- `mode`: A [valid *Friendly Public Transport Format* `1.0.1` mode](https://github.com/public-transport/friendly-public-transport-format/blob/1.0.1/spec/readme.md#modes). +- `mode`: A [valid *Friendly Public Transport Format* `1.1.1` mode](https://github.com/public-transport/friendly-public-transport-format/blob/1.1.1/spec/readme.md#modes). - `bitmasks`: HAFAS endpoints work with a [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)#Arguments_to_functions) that toggles the individual products. It should be an array of values that toggle the appropriate bit(s) in the bitmask (see below). - `name`: A short, but distinct name for the means of transport, *just precise enough in local context*, and in the local language. In Berlin, `S-Bahn-Schnellzug` would be too much, because everyone knows what `S-Bahn` means. - `short`: The shortest possible symbol that identifies the product. - `default`: Should the product be used for queries (e.g. journeys) by default? -If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example/0.2.0) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. +If you want, you can now **verify that the profile works**; We've prepared [a script](https://runkit.com/derhuerst/hafas-client-profile-example/0.2.1) for that. Alternatively, [submit a Pull Request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) and we will help you out with testing and improvements. ### Finding the right values for the `bitmasks` field @@ -138,7 +138,7 @@ all but product F 30 11110 31 - 2^0 2^0 We consider these improvements to be *optional*: -- **Check if the endpoint supports the journey legs call.** +- **Check if the endpoint supports the trips call.** - In the app, check if you can query details for the status of a single journey leg. It should load realtime delays and the current progress. - If this feature is supported, add `trip: true` to the profile. - **Check if the endpoint supports the live map call.** Does the app have a "live map" showing all vehicles within an area? If so, add `radar: true` to the profile. diff --git a/p/oebb/readme.md b/p/oebb/readme.md index 7a9a6d19..1771c420 100644 --- a/p/oebb/readme.md +++ b/p/oebb/readme.md @@ -16,4 +16,4 @@ const client = createClient(oebbProfile, 'my-awesome-program') ## Customisations - parses *ÖBB*-specific products (such as *RailJet*) -- parses invalid empty stations from the API as [`location`](https://github.com/public-transport/friendly-public-transport-format/blob/1.0.1/spec/readme.md#location-objects)s +- parses invalid empty stations from the API as [`location`](https://github.com/public-transport/friendly-public-transport-format/blob/1.1.1/spec/readme.md#location-objects)s diff --git a/readme.md b/readme.md index e972c704..45e8a331 100644 --- a/readme.md +++ b/readme.md @@ -22,7 +22,7 @@ HAFAS endpoint | wrapper library | docs | example code | source code There's [a company called HaCon](http://hacon.de) that sells [a public transport management system called HAFAS](https://de.wikipedia.org/wiki/HAFAS). It is [used by companies all over Europe](https://gist.github.com/derhuerst/2b7ed83bfa5f115125a5) to serve routing and departure information for apps. All those endpoints are similar, with the same terms and API routes, but have slightly different options, filters and sets of enabled features. -`hafas-client` contains all logic for communicating with these, as well as serialising from and parsing to [*Friendly Public Transport Format (FPTF)* `1.0.1`](https://github.com/public-transport/friendly-public-transport-format/blob/1.0.1/spec/readme.md). Endpoint-specific customisations (called *profiles* here) increase the quality of the returned data. +`hafas-client` contains all logic for communicating with these, as well as serialising from and parsing to [*Friendly Public Transport Format (FPTF)* `1.1.1`](https://github.com/public-transport/friendly-public-transport-format/blob/1.1.1/spec/readme.md). Endpoint-specific customisations (called *profiles* here) increase the quality of the returned data. ## Installing @@ -56,7 +56,7 @@ client.journeys('8011167', '8000261', {results: 1}) .catch(console.error) ``` -The returned [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/promise) will resolve with an array of one [*FPTF* `journey`](https://github.com/public-transport/friendly-public-transport-format/blob/1.0.1/spec/readme.md#journey). +The returned [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/promise) will resolve with an array of one [*FPTF* `journey`](https://github.com/public-transport/friendly-public-transport-format/blob/1.1.1/spec/readme.md#journey). ```js [ { From ae31807eb7fbea8e6d75fd64ccbce1f25939817f Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 8 Aug 2018 18:40:35 +0200 Subject: [PATCH 175/176] better User-Agent randomization :bug: --- lib/request.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/request.js b/lib/request.js index 2567f082..50247888 100644 --- a/lib/request.js +++ b/lib/request.js @@ -19,6 +19,11 @@ try { process.exit(1) } +const randomizeUserAgent = (userAgent) => { + const i = Math.round(Math.random() * userAgent.length) + return userAgent.slice(0, i) + id + userAgent.slice(i) +} + const md5 = input => createHash('md5').update(input).digest() const request = (profile, userAgent, opt, data) => { @@ -34,7 +39,7 @@ const request = (profile, userAgent, opt, data) => { 'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate', 'Accept': 'application/json', - 'user-agent': id + ' ' + userAgent + 'user-agent': randomizeUserAgent(userAgent) }, query: {} }) From 8f7358b46265733df5b36a3b1adb6ece4c0c1c52 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 17 Aug 2018 21:40:06 +0200 Subject: [PATCH 176/176] Nah.sh: mode `bus` for on-call transit We might want to rename "on-call" to "demand-responsive". --- p/nahsh/products.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p/nahsh/products.js b/p/nahsh/products.js index 9b9f118e..8a1029c4 100644 --- a/p/nahsh/products.js +++ b/p/nahsh/products.js @@ -75,7 +75,7 @@ const p = [ }, { id: 'onCall', - mode: null, // todo + mode: 'bus', // todo: is this correct? bitmasks: [512], name: 'On-call transit', short: 'on-call',