mirror of
https://github.com/public-transport/db-vendo-client.git
synced 2025-02-23 15:19:35 +02:00
DB: parse Reisezentrum opening hours & station facilities
This commit is contained in:
parent
d92eb154c2
commit
9e75f42346
2 changed files with 151 additions and 2 deletions
102
p/db/index.js
102
p/db/index.js
|
@ -1,6 +1,9 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const trim = require('lodash/trim')
|
const trim = require('lodash/trim')
|
||||||
|
const uniqBy = require('lodash/uniqBy')
|
||||||
|
const slugg = require('slugg')
|
||||||
|
const without = require('lodash/without')
|
||||||
const {parseHook} = require('../../lib/profile-hooks')
|
const {parseHook} = require('../../lib/profile-hooks')
|
||||||
|
|
||||||
const _parseJourney = require('../../parse/journey')
|
const _parseJourney = require('../../parse/journey')
|
||||||
|
@ -9,6 +12,7 @@ const _parseLine = require('../../parse/line')
|
||||||
const _parseArrival = require('../../parse/arrival')
|
const _parseArrival = require('../../parse/arrival')
|
||||||
const _parseDeparture = require('../../parse/departure')
|
const _parseDeparture = require('../../parse/departure')
|
||||||
const _parseHint = require('../../parse/hint')
|
const _parseHint = require('../../parse/hint')
|
||||||
|
const _parseLocation = require('../../parse/location')
|
||||||
const _formatStation = require('../../format/station')
|
const _formatStation = require('../../format/station')
|
||||||
const {bike} = require('../../format/filters')
|
const {bike} = require('../../format/filters')
|
||||||
|
|
||||||
|
@ -23,12 +27,101 @@ const transformReqBody = (ctx, body) => {
|
||||||
|
|
||||||
body.client = {id: 'DB', v: '16040000', type: 'IPH', name: 'DB Navigator'}
|
body.client = {id: 'DB', v: '16040000', type: 'IPH', name: 'DB Navigator'}
|
||||||
body.ext = 'DB.R19.04.a'
|
body.ext = 'DB.R19.04.a'
|
||||||
body.ver = '1.16'
|
body.ver = '1.15'
|
||||||
body.auth = {type: 'AID', aid: 'n91dB8Z77MLdoR0K'}
|
body.auth = {type: 'AID', aid: 'n91dB8Z77MLdoR0K'}
|
||||||
|
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const slices = (n, arr) => {
|
||||||
|
const initialState = {slices: [], count: Infinity}
|
||||||
|
return arr.reduce(({slices, count}, item) => {
|
||||||
|
if (count >= n) {
|
||||||
|
slices.push([item])
|
||||||
|
count = 1
|
||||||
|
} else {
|
||||||
|
slices[slices.length - 1].push(item)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return {slices, count}
|
||||||
|
}, initialState).slices
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseGrid = (g) => {
|
||||||
|
// todo: g.type, e.g. `S`
|
||||||
|
return {
|
||||||
|
title: g.title,
|
||||||
|
rows: slices(g.nCols, g.itemL.map(item => item.msgL[0]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ausstattungKeys = Object.assign(Object.create(null), {
|
||||||
|
'3-s-zentrale': '3SZentrale',
|
||||||
|
'parkplatze': 'parkingLots',
|
||||||
|
'fahrrad-stellplatze': 'bicycleParkingRacks',
|
||||||
|
'opnv-anbindung': 'localPublicTransport',
|
||||||
|
'wc': 'toilets',
|
||||||
|
'schliessfacher': 'lockers',
|
||||||
|
'reisebedarf': 'travelShop',
|
||||||
|
'stufenfreier-zugang': 'stepFreeAccess',
|
||||||
|
'ein-umsteigehilfe': 'boardingAid',
|
||||||
|
'taxi-am-bahnhof': 'taxis'
|
||||||
|
})
|
||||||
|
const parseAusstattungVal = (val) => {
|
||||||
|
val = val.toLowerCase()
|
||||||
|
return val === 'ja' ? true : (val === 'nein' ? false : val)
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseAusstattungGrid = (g) => {
|
||||||
|
// filter duplicate hint rows
|
||||||
|
const rows = uniqBy(g.rows, ([key, val]) => key + ':' + val)
|
||||||
|
|
||||||
|
const res = {raw: rows}
|
||||||
|
for (let [key, val] of rows) {
|
||||||
|
key = ausstattungKeys[slugg(key)]
|
||||||
|
if (key) res[key] = parseAusstattungVal(val)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseReisezentrumÖffnungszeiten = (g) => {
|
||||||
|
const res = {}
|
||||||
|
for (const [dayOfWeek, val] of g.rows) res[dayOfWeek] = val
|
||||||
|
res.raw = g.rows
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseLocWithDetails = ({parsed, common}, l) => {
|
||||||
|
if (!parsed) return parsed
|
||||||
|
if (parsed.type !== 'stop' && parsed.type !== 'station') return parsed
|
||||||
|
|
||||||
|
if (Array.isArray(l.gridL)) {
|
||||||
|
const resolveCell = cell => 'hint' in cell ? cell.hint.text : cell
|
||||||
|
const resolveCells = grid => ({
|
||||||
|
...grid,
|
||||||
|
rows: grid.rows.map(row => row.map(resolveCell))
|
||||||
|
})
|
||||||
|
|
||||||
|
let grids = l.gridL
|
||||||
|
.map(grid => parseGrid(grid, common))
|
||||||
|
.map(resolveCells)
|
||||||
|
|
||||||
|
const ausstattung = grids.find(g => slugg(g.title) === 'ausstattung')
|
||||||
|
if (ausstattung) {
|
||||||
|
parsed.facilities = parseAusstattungGrid(ausstattung)
|
||||||
|
}
|
||||||
|
const öffnungszeiten = grids.find(g => slugg(g.title) === 'offnungszeiten-reisezentrum')
|
||||||
|
if (öffnungszeiten) {
|
||||||
|
parsed.reisezentrumOpeningHours = parseReisezentrumÖffnungszeiten(öffnungszeiten)
|
||||||
|
}
|
||||||
|
|
||||||
|
grids = without(grids, ausstattung, öffnungszeiten)
|
||||||
|
if (grids.length > 0) parsed.grids = grids
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.bahn.de/p/view/service/buchung/auslastungsinformation.shtml
|
// https://www.bahn.de/p/view/service/buchung/auslastungsinformation.shtml
|
||||||
const loadFactors = []
|
const loadFactors = []
|
||||||
loadFactors[1] = 'low-to-medium'
|
loadFactors[1] = 'low-to-medium'
|
||||||
|
@ -324,6 +417,11 @@ const codesByText = Object.assign(Object.create(null), {
|
||||||
})
|
})
|
||||||
|
|
||||||
const parseHintByCode = ({parsed}, raw) => {
|
const parseHintByCode = ({parsed}, raw) => {
|
||||||
|
// plain-text hints used e.g. for stop metadata
|
||||||
|
if (raw.type === 'K') {
|
||||||
|
return {type: 'hint', text: raw.txtN}
|
||||||
|
}
|
||||||
|
|
||||||
if (raw.type === 'A') {
|
if (raw.type === 'A') {
|
||||||
const hint = hintsByCode[raw.code && raw.code.trim().toLowerCase()]
|
const hint = hintsByCode[raw.code && raw.code.trim().toLowerCase()]
|
||||||
if (hint) {
|
if (hint) {
|
||||||
|
@ -360,7 +458,7 @@ const dbProfile = {
|
||||||
|
|
||||||
products: products,
|
products: products,
|
||||||
|
|
||||||
// todo: parseLocation
|
parseLocation: parseHook(_parseLocation, parseLocWithDetails),
|
||||||
parseJourney: parseHook(_parseJourney, parseJourneyWithPrice),
|
parseJourney: parseHook(_parseJourney, parseJourneyWithPrice),
|
||||||
parseJourneyLeg: parseHook(_parseJourneyLeg, parseJourneyLegWithLoadFactor),
|
parseJourneyLeg: parseHook(_parseJourneyLeg, parseJourneyLegWithLoadFactor),
|
||||||
parseLine: parseHook(_parseLine, parseLineWithAdditionalName),
|
parseLine: parseHook(_parseLine, parseLineWithAdditionalName),
|
||||||
|
|
|
@ -19,6 +19,57 @@ const client = createClient(dbProfile, 'my-awesome-program')
|
||||||
- supports [their loyalty cards](https://en.wikipedia.org/wiki/Deutsche_Bahn#Tickets) with `journey()`
|
- supports [their loyalty cards](https://en.wikipedia.org/wiki/Deutsche_Bahn#Tickets) with `journey()`
|
||||||
- parses *DB*-specific products (such as *InterCity-Express*)
|
- parses *DB*-specific products (such as *InterCity-Express*)
|
||||||
- exposes the cheapest ticket price for a `journey`
|
- exposes the cheapest ticket price for a `journey`
|
||||||
|
- parses [*DB*-specific station info](#additional-station-info)
|
||||||
|
|
||||||
|
### additional station info
|
||||||
|
|
||||||
|
With the `db` profile, `hafas-client` will return more station information whenever the endpoint provides it:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
type: 'station',
|
||||||
|
id: '8004585',
|
||||||
|
name: 'Oberstdorf',
|
||||||
|
// …
|
||||||
|
facilities: {
|
||||||
|
'3SZentrale': '089/13081055',
|
||||||
|
parkingLots: true,
|
||||||
|
bicycleParkingRacks: true,
|
||||||
|
localPublicTransport: true,
|
||||||
|
toilets: true,
|
||||||
|
lockers: true,
|
||||||
|
travelShop: true,
|
||||||
|
stepFreeAccess: true,
|
||||||
|
boardingAid: 'ja, um voranmeldung unter 01806 512 512* wird gebeten',
|
||||||
|
taxis: true
|
||||||
|
},
|
||||||
|
reisezentrumOpeningHours: {
|
||||||
|
Mo: '08:00-18:00',
|
||||||
|
Di: '08:00-18:00',
|
||||||
|
Mi: '08:00-18:00',
|
||||||
|
Do: '08:00-18:00',
|
||||||
|
Fr: '08:00-18:00',
|
||||||
|
Sa: '09:00-14:00',
|
||||||
|
So: '09:00-14:00'
|
||||||
|
},
|
||||||
|
// …
|
||||||
|
stops: [{
|
||||||
|
type: 'stop',
|
||||||
|
id: '965503',
|
||||||
|
name: 'Busbahnhof, Oberstdorf',
|
||||||
|
// …
|
||||||
|
reisezentrumOpeningHours: {
|
||||||
|
Mo: '08:00-18:00',
|
||||||
|
Di: '08:00-18:00',
|
||||||
|
Mi: '08:00-18:00',
|
||||||
|
Do: '08:00-18:00',
|
||||||
|
Fr: '08:00-18:00',
|
||||||
|
Sa: '09:00-14:00',
|
||||||
|
So: '09:00-14:00'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Using the `loyaltyCard` option
|
## Using the `loyaltyCard` option
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue