use submodule for hafas-client

This commit is contained in:
Leah 2021-03-08 12:45:22 +01:00
parent 2ff5e8936b
commit 16580bc856
Signed by untrusted user: ctucx
GPG key ID: 725B65F21DB1C2C4
27 changed files with 10 additions and 980 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "src/hafas"]
path = src/hafas
url = https://cgit.ctu.cx/hafas.nim

View file

@ -1,7 +0,0 @@
import hafas/api/journeys
import hafas/api/suggestions
import hafas/api/refresh_journey
export journeys
export suggestions
export refresh_journey

View file

@ -1,79 +0,0 @@
import ../parse/products
import ../parse/point
import ../parse/accessibility
import ../parse/journeys_response
import ../types
import ../util
import json
import times
import asyncdispatch
import options
proc journeys*(params: JourneysParams): Future[JourneysResponse] {.async.} =
var `when` = now().toTime()
var isDeparture = true
if params.departure.isSome:
`when` = params.departure.get.fromUnix
elif params.arrival.isSome:
`when` = params.arrival.get.fromUnix
isDeparture = false
let req = %* {
"cfg": {
"polyEnc": "GPA"
},
"meth": "TripSearch",
"req": {
"ctxScr": nil,
"getPasslist": params.stopovers.get(false),
"maxChg": params.transfers.get(-1),
"minChgTime": params.transferTime.get(0),
"numF": params.results.get(5),
"depLocL": [ params.fromPoint.formatPoint() ],
"viaLocL": [],
"arrLocL": [ params.toPoint.formatPoint() ],
"jnyFltrL": [
{
"type": "PROD",
"mode": "INC",
"value": $formatProducts(params.products.get(parseProducts(1023))),
},
{
"type": "META",
"mode": "INC",
"meta": formatAccessibility(params.accessibility.get(Accessibility.none)),
}
],
"gisFltrL": [],
"getTariff": params.tickets.get(true),
"ushrp": params.startWithWalking.get(true),
"getPT": true,
"getIV": false,
"getPolyline": params.polylines.get(false),
"outFrwd": isDeparture,
"outDate": `when`.format("yyyyMMdd"),
"outTime": `when`.format("HHmmss"),
"trfReq": {
"jnyCl": 2,
"tvlrProf": [
{
"type": "E",
"redtnCard": nil
}
],
"cType": "PK"
}
}
}
if params.laterRef.isSome:
req["req"]["ctxScr"] = %* params.laterRef.get
elif params.earlierRef.isSome:
req["req"]["ctxScr"] = %* params.earlierRef.get
if params.viaPoint.isSome:
let viaPoint = params.viaPoint.get
req["req"]["viaLocL"] = %* [{ "loc": viaPoint.formatPoint() }]
let data = await request(req)
return parseJourneysResponse(data)

View file

@ -1,23 +0,0 @@
import ../types
import ../parse/journeys_response
import ../util
import json
import asyncdispatch
import options
proc refreshJourney*(params: RefreshJourneyParams): Future[Journey] {.async.} =
let req = %* {
"cfg": {
},
"meth": "Reconstruction",
"req": {
"ctxRecon": params.refreshToken,
"getIST": true,
"getPasslist": params.stopovers.get(false),
"getPolyline": params.polylines.get(false),
"getTariff": params.tickets.get(false),
}
}
let data = await request(req)
return parseJourneysResponse(data, true).journeys[0]

View file

@ -1,29 +0,0 @@
import ../../../types
import ../parse/point
import ../util
import json
import asyncdispatch
import sequtils
import options
proc suggestions*(params: SuggestionsParams): Future[seq[Point]] {.async.} =
let req = %* {
"cfg": {
"polyEnc": "GPA"
},
"meth": "LocMatch",
"req": {
"input": {
"loc": {
"type": "ALL",
"name": params.query & "?"
},
"maxLoc": params.results.get(10),
"field": "S"
}
}
}
let data = await request(req)
let locs = data["res"]["match"]["locL"].getElems()
return locs.map(parsePoint)

View file

@ -1,199 +0,0 @@
import asynchttpserver
import ../../types
proc parseError*(errstr: string): hafasException =
case errstr:
of "H_UNKNOWN":
return hafasException(
code: SERVER_ERROR,
message: "unknown internal error",
statusCode: Http500,
)
of "AUTH":
return hafasException(
code: ACCESS_DENIED,
message: "invalid or missing authentication data",
statusCode: Http401,
)
of "R0001":
return hafasException(
code: INVALID_REQUEST,
message: "unknown method",
statusCode: Http400,
)
of "R0002":
return hafasException(
code: INVALID_REQUEST,
message: "invalid or missing request parameters",
statusCode: Http400,
)
of "R0007":
return hafasException(
code: SERVER_ERROR,
message: "internal communication error",
statusCode: Http500,
)
of "R5000":
return hafasException(
code: ACCESS_DENIED,
message: "access denied",
statusCode: Http401,
)
of "S1":
return hafasException(
code: SERVER_ERROR,
message: "journeys search: a connection to the backend server couldn\'t be established",
statusCode: Http503,
)
of "LOCATION":
return hafasException(
code: INVALID_REQUEST,
message: "location/stop not found",
statusCode: Http400,
)
of "H390":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: departure/arrival station replaced",
statusCode: Http400,
)
of "H410":
return hafasException(
code: SERVER_ERROR,
message: "journeys search: incomplete response due to timetable change"
)
of "H455":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: prolonged stop",
statusCode: Http400,
)
of "H460":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: stop(s) passed multiple times",
statusCode: Http400,
)
of "H500":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: too many trains, connection is not complete",
statusCode: Http400,
)
of "H890":
return hafasException(
code: NOT_FOUND,
message: "journeys search unsuccessful",
statusCode: Http404,
)
of "H891":
return hafasException(
code: NOT_FOUND,
message: "journeys search: no route found, try with an intermediate stations",
statusCode: Http404,
)
of "H892":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: query too complex, try less intermediate stations",
statusCode: Http400,
)
of "H895":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: departure & arrival are too near",
statusCode: Http400,
)
of "H899":
return hafasException(
code: SERVER_ERROR,
message: "journeys search unsuccessful or incomplete due to timetable change"
)
of "H900":
return hafasException(
code: SERVER_ERROR,
message: "journeys search unsuccessful or incomplete due to timetable change"
)
of "H9220":
return hafasException(
code: NOT_FOUND,
message: "journeys search: no stations found close to the address",
statusCode: Http400,
)
of "H9230":
return hafasException(
code: SERVER_ERROR,
message: "journeys search: an internal error occured",
statusCode: Http500,
)
of "H9240":
return hafasException(
code: NOT_FOUND,
message: "journeys search unsuccessful",
statusCode: Http404,
)
of "H9250":
return hafasException(
code: SERVER_ERROR,
message: "journeys search: leg query interrupted",
statusCode: Http500,
)
of "H9260":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: unknown departure station",
statusCode: Http400,
)
of "H9280":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: unknown intermediate station",
statusCode: Http400,
)
of "H9300":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: unknown arrival station",
statusCode: Http400,
)
of "H9320":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: the input is incorrect or incomplete",
statusCode: Http400,
)
of "H9360":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: error in a data field",
statusCode: Http400,
)
of "H9380":
return hafasException(
code: INVALID_REQUEST,
message: "journeys search: departure/arrival/intermediate station defined more than once",
statusCode: Http400,
)
of "SQ001":
return hafasException(
code: SERVER_ERROR,
message: "no departures/arrivals data available",
statusCode: Http503,
)
of "SQ005":
return hafasException(
code: NOT_FOUND,
message: "no trips found",
statusCode: Http404,
)
of "TI001":
return hafasException(
code: SERVER_ERROR,
message: "no trip info available",
statusCode: Http503,
)
return hafasException(
code: SERVER_ERROR,
message: "unknown HAFAS exception " & errstr,
statusCode: Http500,
)

View file

@ -1,6 +0,0 @@
import ../types
proc formatAccessibility*(a: Accessibility): string =
if a == none: result = "notBarrierfree"
elif a == partial: result = "limitedBarrierfree"
elif a == complete: result = "completeBarrierfree"

View file

@ -1,31 +0,0 @@
import ../types
import options
import strutils
import times
proc parseDate*(common: CommonData, time: Option[string], tzoffset: Option[int]): Option[int64] =
if time.isNone:
return none(int64)
let tzoffset = tzoffset.get(60) # FIXME: sometimes no time zone is given. how to deal with that?
let date = common.dateStr
var time = time.get
var dayoffset = 0
if time.len == 8:
dayoffset = parseInt(time[0..1])
time = time[2..7]
var tzoffhours = align($(int(tzoffset / 60)), 2, '0')
var tzoffmins = align($(tzoffset mod 60), 2, '0')
var tzoff = tzoffhours & ":" & tzoffmins
if tzoffset >= 0:
tzoff = "+" & tzoff
let datestr = date & time & tzoff
let dateformat = "yyyyMMddHHmmsszzz"
var dt = datestr.parse(dateformat)
dt = dt + initTimeInterval(days = dayoffset)
return some(dt.toTime().toUnix())

View file

@ -1,38 +0,0 @@
import ../types
import json
import leg
import options
proc mkParseJourney*(common: CommonData): proc =
proc parseJourney(j: JsonNode): Journey =
var common = common
common.dateStr = j{"date"}.getStr()
if j{"trfRes"}{"statusCode"}.getStr == "OK":
result.price = some(Price(
amount: j["trfRes"]["fareSetL"][0]["fareL"][0]["prc"].getInt / 100,
currency: some("Euro"),
))
result.refreshToken = j{"ctxRecon"}.getStr()
result.legs = j{"secL"}.getElems().map(mkParseLeg(common))
result.lastUpdated = common.timeStamp
# combine walking legs
var i = -1
var firstWalking = -1
while true:
inc(i)
if i >= len(result.legs): break
if result.legs[i].isWalking:
if firstWalking == -1:
firstWalking = i
else:
result.legs[firstWalking].arrival = result.legs[i].arrival
result.legs[firstWalking].distance.get += result.legs[i].distance.get
result.legs.delete(i)
dec(i)
else:
firstWalking = -1
return parseJourney

View file

@ -1,27 +0,0 @@
import ../types
import ./remark
import ./point
import ./operator
import ./journey
import ./line
import ./polyline
import json
import sequtils
import strutils
proc parseJourneysResponse*(data: JsonNode, isRefresh: bool = false): JourneysResponse =
let points = map(data["res"]["common"]["locL"].getElems(), parsePoint)
let operators = map(data["res"]["common"]["opL"].getElems(), parseOperator)
let remarks = map(data["res"]["common"]["remL"].getElems(), parseRemark)
let lines = data["res"]["common"]["prodL"]
let polylines = map(data["res"]["common"]["polyL"].getElems(), mkParsePolyline(points))
let timestamp = parseInt(data["res"]["planrtTS"].getStr())
let common = CommonData(points: points, operators: operators, remarks: remarks, lines: lines, polylines: polylines, timestamp: timestamp)
result.journeys = data["res"]["outConL"].getElems().map(mkParseJourney(common))
if not isRefresh:
if data["res"].hasKey("outCtxScrB"):
result.earlierRef = data["res"]["outCtxScrB"].getStr()
if data["res"].hasKey("outCtxScrF"):
result.laterRef = data["res"]["outCtxScrF"].getStr()

View file

@ -1,70 +0,0 @@
import ../types
import ./stopover
import ./msg
import ./date
import ./line
import json
import options
proc parseLegPart(common: CommonData, lp: JsonNode): LegPart =
let h = lp.to(HafasStopParams)
let plannedDepartureTime = parseDate(common, h.dTimeS, h.dTZOffset)
let plannedArrivalTime = parseDate(common, h.aTimeS, h.aTZOffset)
if h.dPlatfS.isSome: result.plannedPlatform = h.dPlatfS
elif h.aPlatfS.isSome: result.plannedPlatform = h.aPlatfS
if h.dPlatfR.isSome: result.prognosedPlatform = h.dPlatfR
elif h.aPlatfR.isSome: result.prognosedPlatform = h.aPlatfR
if h.dTimeR.isSome: result.prognosedTime = parseDate(common, h.dTimeR, h.dTZOffset)
elif h.aTimeR.isSome: result.prognosedTime = parseDate(common, h.aTimeR, h.aTZOffset)
if plannedDepartureTime.isSome: result.plannedTime = plannedDepartureTime.get
elif plannedArrivalTime.isSome: result.plannedTime = plannedArrivalTime.get
else: raise newException(CatchableError, "missing departure and arrival time for leg")
result.point = common.points[h.locX.get]
proc mkParseLeg*(common: CommonData): proc =
proc parseLeg(l: JsonNode): Leg =
if l{"jny"}{"polyG"}{"polyXL"}.getElems().len() > 0:
result.polyline = some(Polyline(
type: "FeatureCollection",
))
for n in l{"jny"}{"polyG"}{"polyXL"}.getElems():
result.polyline.get.features &= common.polylines[n.getInt()].features
let typeStr = l{"type"}.getStr()
echo typeStr
if typeStr == "JNY":
result.direction = some(l{"jny"}{"dirTxt"}.getStr())
result.tripId = some(l{"jny"}{"jid"}.getStr())
result.line = common.parseLine(l["jny"]["prodX"].getInt())
let stopovers = l{"jny"}{"stopL"}.getElems()
if stopovers.len > 0:
result.stopovers = some(stopovers.map(mkParseStopover(common)))
let remarks = l{"jny"}{"msgL"}.getElems()
if remarks.len > 0:
result.remarks = some(remarks.map(mkParseMsg(common)))
elif typeStr == "WALK":
result.isWalking = true
result.distance = some(l{"gis"}{"dist"}.getInt())
elif typeStr == "TRSF" or typeStr == "DEVI":
result.isTransfer = true
else:
raise newException(CatchableError, "Unimplemented hafas leg type: " & typeStr)
result.departure = common.parseLegPart(l{"dep"})
result.arrival = common.parseLegPart(l{"arr"})
result.cancelled = l{"dep"}{"dCncl"}.getBool(false) or l{"arr"}{"aCncl"}.getBool(false)
return parseLeg

View file

@ -1,71 +0,0 @@
import ../types
import ../util
import ./products
import json
import options
import tables
import httpClient
import asyncdispatch
var trainTypes = initTable[string, string]()
var trainTypesShort = initTable[string, string]()
proc fetchTrainTypes() {.async.} =
var client = newAsyncHttpClient()
let resp = await client.getContent("https://lib.finalrewind.org/dbdb/ice_type.json")
let data = parseJson(resp)
for key, info in pairs(data):
if info{"type"}.getStr != "" and info{"type"}.getStr != "EC" and info{"type"}.getStr != "IC":
trainTypes[key] = info{"type"}.getStr
if info{"short"}.getStr != "":
trainTypesShort[key] = info{"short"}.getStr
asyncCheck fetchTrainTypes()
proc parseLine*(common: CommonData, i: int): Option[Line] =
let l = common.lines[i]
# unparsable
if l{"cls"}.getInt == 0:
return options.none(Line)
let line = l.to(HafasProd)
var res = Line()
res.name = line.name
res.product = parseProduct(line.cls)
res.tripNum = line.prodCtx.num
if not isNone(line.prodCtx.catOut):
res.productName = get(line.prodCtx.catOut)
else:
res.productName = "?"
res.fullProductName = line.prodCtx.catOutL
res.id = slug(line.prodCtx.lineId.get(line.name))
if line.opX.isSome:
res.operator = some(common.operators[line.opX.get])
# DB
if res.productName == "IC" or res.productName == "ICE" or res.productName == "EC" or res.productName == "ECE":
if trainTypes.contains(res.tripNum) and trainTypes[res.tripNum] != res.productName:
res.trainType = some(trainTypes[res.tripNum])
if trainTypesShort.contains(res.tripNum):
res.trainTypeShort = some(trainTypesShort[res.tripNum])
if line.nameS.isSome and (res.product == bus or res.product == tram or res.product == ferry):
res.name = line.nameS.get
if line.addName.isSome:
# swap name and addName
res.additionalName = some(res.name)
res.name = line.addName.get
# End DB
res.mode = MODES[int(res.product)]
return some(res)

View file

@ -1,12 +0,0 @@
import ../types
import json
proc mkParseMsg*(common: CommonData): proc =
proc parseMsg(m: JsonNode): Remark =
let typeStr = m{"type"}.getStr()
if typeStr != "REM":
raise newException(CatchableError, "Unimplemented hafas msg type: " & typeStr)
return common.remarks[m{"remX"}.getInt()]
return parseMsg

View file

@ -1,9 +0,0 @@
import ../../../types
import ../util
import json
proc parseOperator*(op: JsonNode): Operator =
return (%* {
"name": op{"name"},
"id": %* slug(op{"name"}.getStr()),
}).to(Operator)

View file

@ -1,78 +0,0 @@
import ../types
import ./products
import json
import options
import tables
proc parsePoint*(loc: JsonNode): Point =
let typeStr = loc{"type"}.getStr()
if typeStr == "S":
result.stop = some(Stop(
id: loc{"extId"}.getStr(),
name: loc{"name"}.getStr(),
location: Location(
latitude: loc{"crd"}{"y"}.getInt() / 1000000,
longitude: loc{"crd"}{"x"}.getInt() / 1000000,
),
products: parseProducts(loc{"pCls"}.getInt()),
))
elif typeStr == "P":
result.location = some(Location(
id: some(loc{"extId"}.getStr()),
name: some(loc{"name"}.getStr()),
latitude: loc{"crd"}{"y"}.getInt() / 1000000,
longitude: loc{"crd"}{"x"}.getInt() / 1000000,
))
elif typeStr == "A":
result.location = some(Location(
address: some(loc{"name"}.getStr()),
latitude: loc{"crd"}{"y"}.getInt() / 1000000,
longitude: loc{"crd"}{"x"}.getInt() / 1000000,
))
else:
raise newException(CatchableError, "Unimplemented hafas loc type: " & typeStr)
proc formatLocationIdentifier(d: Table[string, string]): string =
for key, val in d:
result &= key
result &= "="
result &= val
result &= "@"
proc formatCoord(c: float): string =
return $int(c * 1000000)
proc formatPoint*(point: Point): JsonNode =
if point.stop.isSome:
let stop = point.stop.get
return %* {
"type": "S",
"lid": formatLocationIdentifier({
"A": "1",
"L": $stop.id,
}.toTable),
}
elif point.location.isSome:
let loc = point.location.get
if loc.address.isSome:
return %* {
"type": "A",
"lid": formatLocationIdentifier({
"A": "2",
"O": loc.address.get,
"X": formatCoord(loc.longitude),
"Y": formatCoord(loc.latitude),
}.toTable),
}
elif loc.name.isSome and loc.id.isSome:
return %* {
"type": "P",
"lid": formatLocationIdentifier({
"A": "4",
"O": loc.address.get,
"L": loc.id.get,
"X": formatCoord(loc.longitude),
"Y": formatCoord(loc.latitude),
}.toTable),
}
raise newException(CatchableError, "Cannot format HAFAS location")

View file

@ -1,73 +0,0 @@
import ../types
import json
import options
import math
proc gpsDistance(fromLat: float, fromLon: float, toLat: float, toLon: float): float =
proc toRad(x: float): float = x * PI / 180
let dLat = toRad(toLat - fromLat)
let dLon = toRad(toLon - fromLon)
let fromLat = toRad(fromLat)
let toLat = toRad(toLat)
let a = pow(sin(dLat / 2), 2) + (pow(sin(dLon / 2), 2) * cos(fromLat) * cos(toLat))
let c = 2 * arctan2(sqrt(a), sqrt(1 - a))
return 6371 * c
proc parseIntegers(str: string): seq[int] =
var byte = 0
var current = 0
var bits = 0
for c in str:
byte = int(c) - 63
current = current or (( byte and 31 ) shl bits)
bits += 5
if byte < 32:
if (current and 1) == 1:
current = -current
current = current shr 1
result.add(current)
current = 0
bits = 0
proc mkParsePolyline*(points: seq[Point]): proc =
proc parsePolyline(l: JsonNode): Polyline =
let line = l.to(HafasPolyLine)
result.type = "FeatureCollection"
var lat = 0
var lon = 0
let ints = parseIntegers(line.crdEncYX)
var i = 0
while i < len(ints):
lat += ints[i]
lon += ints[i+1]
result.features.add(Feature(
type: "Feature",
geometry: FeatureGeometry(
type: "Point",
coordinates: @[lon / 100000, lat / 100000],
),
))
i += 2
if line.ppLocRefL.isSome:
for p in line.ppLocRefL.get:
result.features[p.ppIdx].properties = points[p.locX].stop
# sort out coordinates closer than 5m to their neighbours
var j = 1
while true:
if j >= len(result.features): break
let last = result.features[j-1].geometry.coordinates
let current = result.features[j].geometry.coordinates
if gpsDistance(last[1], last[0], current[1], current[0]) <= 0.005:
result.features.delete(j)
continue
j += 1
return parsePolyline

View file

@ -1,37 +0,0 @@
import ../../../types
import bitops
proc parseProduct*(cls: int): Product =
var tmp = cls
var res = 0
while tmp > 1:
tmp = tmp shr 1
res += 1
return Product(res)
proc parseProducts*(pCls: int): Products =
return Products(
nationalExp: pCls.testBit(0),
national: pCls.testBit(1),
regionalExp: pCls.testBit(2),
regional: pCls.testBit(3),
suburban: pCls.testBit(4),
bus: pCls.testBit(5),
ferry: pCls.testBit(6),
subway: pCls.testBit(7),
tram: pCls.testBit(8),
taxi: pCls.testBit(9),
)
proc formatProducts*(p: Products): int =
if p.nationalExp: result.setBit(0)
if p.national: result.setBit(1)
if p.regionalExp: result.setBit(2)
if p.regional: result.setBit(3)
if p.suburban: result.setBit(4)
if p.bus: result.setBit(5)
if p.ferry: result.setBit(6)
if p.subway: result.setBit(7)
if p.tram: result.setBit(8)
if p.taxi: result.setBit(9)

View file

@ -1,32 +0,0 @@
import ../../../types
import json
proc parseRemark*(rem: JsonNode): Remark =
let typeStr = rem{"type"}.getStr()
if typeStr == "M" or typeStr == "P":
return (%* {
"type": %* "status",
"summary": rem{"txtS"},
"code": rem{"code"},
"text": rem{"txtN"},
}).to(Remark)
elif typeStr == "L":
return (%* {
"type": %* "status",
"code": %* "alternative-trip",
"text": rem{"txtN"},
"tripId": rem{"jid"},
}).to(Remark)
elif typeStr == "A" or typeStr == "I" or typeStr == "H":
return (%* {
"type": %* "hint",
"code": rem{"code"},
"text": rem{"txtN"},
}).to(Remark)
else:
# TODO: parse more accurately
return (%* {
"type": %* "status",
"code": rem{"code"},
"text": rem{"txtN"},
}).to(Remark)

View file

@ -1,33 +0,0 @@
import ../types
import ./date
import options
import json
proc parseStopoverPart(common: CommonData, mode: string, h: HafasStopParams): StopoverPart =
if (mode != "arrival"):
result.plannedPlatform = h.dPlatfS
result.prognosedPlatform = h.dPlatfR
result.plannedTime = parseDate(common, h.dTimeS, h.dTZOffset)
result.prognosedTime = parseDate(common, h.dTimeR, h.dTZOffset)
else:
result.plannedPlatform = h.aPlatfS
result.prognosedPlatform = h.aPlatfR
result.plannedTime = parseDate(common, h.aTimeS, h.aTZOffset)
result.prognosedTime = parseDate(common, h.aTimeR, h.aTZOffset)
proc mkParseStopover*(common: CommonData): proc =
proc parseStopover(s: JsonNode): Stopover =
let typeStr = s{"type"}.getStr()
if typeStr != "N":
echo pretty(s)
raise newException(CatchableError, "Unimplemented hafas stopover type: " & typeStr)
let h = s.to(HafasStopParams)
result.stop = common.points[s{"locX"}.getInt()].stop.get
result.cancelled = h.aCncl.get(false) or h.dCncl.get(false)
result.arrival = common.parseStopoverPart("arrival", h)
result.departure = common.parseStopoverPart("departure", h)
return parseStopover

View file

@ -1,10 +0,0 @@
- implement leg parsing with nim types
- check and pass back hafas errors
- isAdd (is additional stop)
- hafas information manager (him) remarks
- cycle information
- price information
- reachable
- profiles
- mark current alternatives
- stations

View file

@ -1,53 +0,0 @@
import ../../types
import options
export types
import json
type
CommonData* = object
lines*: JsonNode
remarks*: seq[Remark]
operators*: seq[Operator]
points*: seq[Point]
polylines*: seq[Polyline]
dateStr*: string
timestamp*: int64
HafasStopParams* = object
aTimeS*: Option[string]
aPlatfS*: Option[string]
aTZOffset*: Option[int]
aCncl*: Option[bool]
aTimeR*: Option[string]
aPlatfR*: Option[string]
dTimeS*: Option[string]
dPlatfS*: Option[string]
dTZOffset*: Option[int]
dCncl*: Option[bool]
dTimeR*: Option[string]
dPlatfR*: Option[string]
locX*: Option[int]
HafasProdCtx* = object
name*: string
num*: string
catOut*: Option[string]
catOutL*: string
lineId*: Option[string]
HafasProd* = object
name*: string
cls*: int
icoX*: int
nameS*: Option[string]
addName*: Option[string]
opX*: Option[int]
prodCtx*: HafasProdCtx
HafasLocRef* = object
ppIdx*: int
locX*: int
HafasPolyline* = object
crdEncYX*: string
ppLocRefL*: Option[seq[HafasLocRef]]

View file

@ -1,57 +0,0 @@
import httpclient
import asyncdispatch
import md5
import json
import strutils
import errors
proc slug*(s: string): string =
for c in s:
if c.isAlphaNumeric():
result &= c.toLowerAscii()
else:
result &= '-'
proc request*(req: JsonNode): Future[JsonNode] {.async.} =
let client = newAsyncHttpClient()
let body = %*{
"lang": "de",
"svcReqL": [req]
}
# TODO: move to profile
body["svcReqL"][0]["cfg"]["rtMode"] = %* "HYBRID"
body["client"] = %* {
"id": "DB",
"v": "16040000",
"type": "IPH",
"name": "DB Navigator"
}
body["ext"] = %* "DB.R19.04.a"
body["ver"] = %* "1.16"
body["auth"] = %* {
"type": "AID",
"aid": "n91dB8Z77MLdoR0K"
}
let salt = "bdI8UVj40K5fvxwf"
let bodytext = $body
let checksum = $toMD5(bodytext & salt)
let url = "https://reiseauskunft.bahn.de/bin/mgate.exe?checksum=" & checksum
client.headers = newHttpHeaders({
"Content-Type": "application/json",
"Accept": "application/json",
"user-agent": "my-awesome-e5f276d8fe6cprogram",
})
#echo pretty body
let req = await client.request(url, httpMethod = HttpPost, body = $body)
let resp = await req.body
let data = parseJson(resp)
let error = data{"svcResL"}{0}{"err"}.getStr()
if error != "OK":
raise parseError(error)
return data{"svcResL"}{0}

View file

@ -1,5 +1,5 @@
import json, tables, options, asyncdispatch
import ../types, ../backend/hafas, ../cache
import ../types, ../hafas/hafas, ../cache
when not defined(release):
import hotcodereloading

View file

@ -1,5 +1,5 @@
import json, tables, options, asyncdispatch, strutils
import ../types, ../cache_types, ../backend/hafas, ../cache
import ../types, ../cache_types, ../hafas/hafas, ../cache
proc moreJourneysEndpoint*(requestData: JsonNode): Future[JsonNode] {.async.} =
var reqId = requestData{"reqId"}.getStr()
@ -39,4 +39,4 @@ proc moreJourneysEndpoint*(requestData: JsonNode): Future[JsonNode] {.async.} =
return response
else:
raise newException(errorException, "MISSING_VALUES")
raise newException(errorException, "MISSING_VALUES")

View file

@ -1,5 +1,5 @@
import json, tables, options, asyncdispatch, strutils
import ../types, ../cache_types, ../backend/hafas, ../cache
import ../types, ../cache_types, ../hafas/hafas, ../cache
proc refreshJourneyEndpoint*(requestData: JsonNode): Future[JsonNode] {.async.} =
let reqId = requestData{"reqId"}.getStr()
@ -34,4 +34,4 @@ proc refreshJourneyEndpoint*(requestData: JsonNode): Future[JsonNode] {.async.}
delete(response["journeys"][journeyId]["legs"][legKey], "cycle")
delete(response["journeys"][journeyId]["legs"][legKey], "tripId")
return response
return response

View file

@ -1,5 +1,5 @@
import json, options, asyncdispatch
import ../types, ../backend/hafas
import ../types, ../hafas/hafas
const ds100Json = staticRead "../../ds100reverse.json"

1
src/hafas Submodule

@ -0,0 +1 @@
Subproject commit e815bbfd2ef4f49c4e3a757739a60d802bcc53f5