From 809c6ac61673c7ac85fde6c691d79dc287ac37cd Mon Sep 17 00:00:00 2001 From: Julius Tens Date: Fri, 29 Dec 2017 10:01:02 +0100 Subject: [PATCH] add profile and tests for oebb --- p/oebb/index.js | 88 ++++++++++- p/oebb/products.js | 7 +- test/index.js | 1 + test/oebb.js | 377 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 467 insertions(+), 6 deletions(-) create mode 100644 test/oebb.js diff --git a/p/oebb/index.js b/p/oebb/index.js index e309bc91..32d4849e 100644 --- a/p/oebb/index.js +++ b/p/oebb/index.js @@ -5,18 +5,92 @@ const createParseBitmask = require('../../parse/products-bitmask') const createFormatBitmask = require('../../format/products-bitmask') +const _parseLine = require('../../parse/line') +const _parseLocation = require('../../parse/location') +const _createParseMovement = require('../../parse/movement') const products = require('./products') const transformReqBody = (body) => { - body.client = {type: 'IPA', id: 'OEBB'} + // todo: necessary headers? + body.client = { + type: 'IPA', + id: 'OEBB', + v: '6000500', + name: 'oebbIPAD_ADHOC', + os: 'iOS 10.3.3' + } // todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L33 shows 1.16 - body.ver = '1.15' - body.auth = {type: 'AID', aid: 'OWDL4fE4ixNiPBBm'} + body.ver = '1.16' + body.auth = {aid: 'OWDL4fE4ixNiPBBm'} + body.lang = 'de' return body } +const parseLine = (profile, l) => { + const res = _parseLine(profile, 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 +} + +const parseLocation = (profile, l) => { + // ÖBB has some 'stations' **in austria** with no departures/products, like station entrances, that are actually POI + const res = _parseLocation(profile, l) + if(res.type === 'station' && !res.products && res.name && res.id && res.id.length !== 7){ + const newRes = { + type: 'location', + id: res.id, + name: res.name + } + return Object.assign({}, newRes, res.location) + } + return res +} + +const createParseMovement = (profile, locations, lines, remarks) => { + const _parseMovement = _createParseMovement(profile, locations, lines, remarks) + const parseMovement = (m) => { + const res = _parseMovement(m) + // filter POI + res.nextStops = res.nextStops.filter(s => s.type === 'station') + res.frames = res.frames.filter(f => !(f.origin.type === 'location' && f.destination.type === 'location')) + return res + } + 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', @@ -27,8 +101,14 @@ const oebbProfile = { products: products.allProducts, parseProducts: createParseBitmask(products.bitmasks), + parseLine, + parseLocation, + parseMovement: createParseMovement, - formatProducts: createFormatBitmask(products) + formatProducts, + + journeyLeg: true, + radar: true } module.exports = oebbProfile diff --git a/p/oebb/products.js b/p/oebb/products.js index 13c2b125..646d4588 100644 --- a/p/oebb/products.js +++ b/p/oebb/products.js @@ -20,7 +20,7 @@ const p = { name: 'Durchgangszug & EuroNight', short: 'D/EN', mode: 'train', - product: 'regional' + product: 'interregional' }, regional: { bitmask: 16, @@ -61,7 +61,7 @@ const p = { bitmask: 512, name: 'Tram', short: 'T', - mode: 'tram', + mode: 'train', product: 'tram' }, onCall: { @@ -83,6 +83,7 @@ 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 @@ -90,8 +91,10 @@ 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, diff --git a/test/index.js b/test/index.js index 85c39e86..5675edb4 100644 --- a/test/index.js +++ b/test/index.js @@ -2,3 +2,4 @@ require('./db') require('./vbb') +require('./oebb') diff --git a/test/oebb.js b/test/oebb.js new file mode 100644 index 00000000..a9630c53 --- /dev/null +++ b/test/oebb.js @@ -0,0 +1,377 @@ +'use strict' + +// todo +// const getStations = require('db-stations').full +const tapePromise = require('tape-promise').default +const tape = require('tape') +const co = require('co') +const isRoughlyEqual = require('is-roughly-equal') + +const createClient = require('..') +const oebbProfile = require('../p/oebb') +const products = require('../p/oebb/products') +const { + assertValidStation, + assertValidPoi, + assertValidAddress, + assertValidLocation, + assertValidLine, + assertValidStopover, + hour, createWhen, assertValidWhen +} = require('./util.js') + +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 +// 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 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)) +} + +// todo: this doesnt seem to work +// todo: DRY with assertValidStationProducts +const assertValidProducts = (t, p) => { + for (let k of Object.keys(products)) { + t.ok('boolean', typeof products[k], 'mode ' + k + ' must be a boolean') + } +} + +const assertValidPrice = (t, p) => { + t.ok(p) + if (p.amount !== null) { + t.equal(typeof p.amount, 'number') + t.ok(p.amount > 0) + } + if (p.hint !== null) { + t.equal(typeof p.hint, 'string') + t.ok(p.hint) + } +} + +const test = tapePromise(tape) +const client = createClient(oebbProfile) + +test('Salzburg Hbf to Wien Westbahnhof', co.wrap(function* (t) { + const salzburgHbf = '8100002' + const wienWestbahnhof = '1291501' + const journeys = yield client.journeys(salzburgHbf, wienWestbahnhof, { + when, passedStations: true + }) + + 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) + // 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] + + 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) + // } + assertValidWhen(t, leg.departure, when) + 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) + // } + 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() +})) + +test('Salzburg Hbf to 1220 Wien, Wagramer Straße 5', co.wrap(function* (t) { + const salzburgHbf = '8100002' + const wagramerStr = { + type: 'location', + latitude: 48.236216, + longitude: 16.425863, + address: '1220 Wien, Wagramer Straße 5' + } + + 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] + + 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) + 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)) + + t.end() +})) + +test('Albertina to Salzburg Hbf', co.wrap(function* (t) { + const albertina = { + type: 'location', + latitude: 48.204699, + longitude: 16.368404, + name: 'Albertina', + id: '975900003' + } + const salzburgHbf = '8100002' + const journeys = yield client.journeys(albertina, salzburgHbf, {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] + + 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) + 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) + // } + + t.end() +})) + +test('leg details for Wien Westbahnhof to München Hbf', co.wrap(function* (t) { + const wienWestbahnhof = '1291501' + const muenchenHbf = '8000261' + const journeys = yield client.journeys(wienWestbahnhof, muenchenHbf, { + 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}) + + 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) + + t.end() +})) + +test('departures at Salzburg Hbf', co.wrap(function* (t) { + const salzburgHbf = '8100002' + const deps = yield client.departures(salzburgHbf, { + duration: 5, when + }) + + 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) + } + + t.end() +})) + +test('nearby Salzburg Hbf', co.wrap(function* (t) { + const salzburgHbfPosition = { + longitude: 13.045604, + latitude: 47.812851 + } + const nearby = yield client.nearby(salzburgHbfPosition.latitude, salzburgHbfPosition.longitude, { + results: 2, distance: 400 + }) + + t.ok(Array.isArray(nearby)) + t.equal(nearby.length, 2) + + 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) + } + + t.end() +})) + +test('locations named Salzburg', co.wrap(function* (t) { + const locations = yield client.locations('Salzburg', { + results: 10 + }) + + t.ok(Array.isArray(locations)) + t.ok(locations.length > 0) + 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(isSalzburgHbf)) + + t.end() +})) + +test('radar Salzburg', co.wrap(function* (t) { + const vehicles = yield client.radar(47.827203, 13.001261, 47.773278, 13.07562, { + 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 <= 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) { + 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) + t.equal(typeof f.t, 'number') + } + } + t.end() +}))