diff options
Diffstat (limited to 'src/bahnhofname.gleam')
-rw-r--r-- | src/bahnhofname.gleam | 110 |
1 files changed, 88 insertions, 22 deletions
diff --git a/src/bahnhofname.gleam b/src/bahnhofname.gleam index 0612587..fe3b10f 100644 --- a/src/bahnhofname.gleam +++ b/src/bahnhofname.gleam @@ -89,7 +89,7 @@ type IdKind { type Matched(t) { Exact(t) - Fuzzy(t) + Fuzzy(t, t) Failed } @@ -136,7 +136,7 @@ fn lookup_fuzzy( ) -> #(Int, String) { case fuzzy(query, kind) { Exact(res) -> #(200, res) - Fuzzy(res) -> #(302, res) + Fuzzy(res, _) -> #(302, res) Failed -> #(404, "??") } } @@ -148,31 +148,72 @@ fn if_not(res: #(Int, t), fallback: fn() -> #(Int, t)) -> #(Int, t) { }) } + fn lookup_station( request: Request(t), ds100_to_name: Map(String, String), leitpunkt_to_name: Map(String, String), + lookup_platform: fn(String) -> String, fuzzy: fn(String, IdKind) -> Matched(String), ) -> Response(mist.ResponseData) { - let #(code, text) = case request { + let #(#(code, text), is_html) = case request { // blackhole favicon.ico requests instead of using the index - Request(method: Get, path: "/favicon.ico", ..) -> #(404, "") + Request(method: Get, path: "/favicon.ico", ..) -> #(#(404, ""), False) Request(method: Get, path: "/help", ..) - | Request(method: Get, path: "/", ..) -> #( + | Request(method: Get, path: "/", ..) -> #(#( 200, - "ril100 → Name: " <> proto <> ril100_domain <> "/HG\n" <> "Name → ril100: " <> proto <> ril100_domain <> "/Göttingen\n\n" <> "Leitpunkt → Name: " <> proto <> leitpunkt_domain <> "/GOE\n" <> "Name → Leitpunkt: " <> proto <> leitpunkt_domain <> "/Göttingen\n\n" <> "Fuzzy:" <> proto <> domain <> "/...", - ) + "ril100 → Name: " <> proto <> ril100_domain <> "/HG\n" <> + "Name → ril100: " <> proto <> ril100_domain <> "/Göttingen\n\n" <> + "Leitpunkt → Name: " <> proto <> leitpunkt_domain <> "/GOE\n" <> + "Name → Leitpunkt: " <> proto <> leitpunkt_domain <> "/Göttingen\n\n" <> + "Fuzzy:" <> proto <> domain <> "/...", + ), False) Request(method: Get, path: "/" <> path, ..) -> { - let query = unpercent(path) - case get_header(request, "x-forwarded-host") { - Ok(domain) if domain == leitpunkt_domain -> + let raw_query = unpercent(path) + let show_platforms = string.ends_with(raw_query, "/gleis") + || string.ends_with(raw_query, "/bahnsteig") + || string.ends_with(raw_query, "/platforms") + || string.ends_with(raw_query, "/tracks") + || string.ends_with(raw_query, "/platform") + || string.ends_with(raw_query, "/track") + let query = raw_query + |> string.replace("/gleis","") + |> string.replace("/bahnsteig","") + |> string.replace("/platforms","") + |> string.replace("/tracks","") + |> string.replace("/platform","") + |> string.replace("/track","") + case #(show_platforms, get_header(request, "x-forwarded-host")) { + #(False, Ok(domain)) if domain == leitpunkt_domain -> query |> lookup_exact(leitpunkt_to_name) |> if_not(fn() { lookup_fuzzy(query, Leitpunkt, fuzzy) }) - Ok(domain) if domain == ril100_domain || domain == ds100_domain -> + |> pair.new(False) + #(False, Ok(domain)) if domain == ril100_domain || domain == ds100_domain -> query |> lookup_exact(ds100_to_name) |> if_not(fn() { lookup_fuzzy(query, DS100, fuzzy) }) + |> pair.new(False) + #(True, Ok(domain)) if domain == leitpunkt_domain -> { + let query = case map.get(leitpunkt_to_name, query) { + Ok(name) -> name + _ -> query + } + case fuzzy(query, DS100) { + Exact(code) -> #(200, lookup_platform(code)) + Fuzzy(_, code) -> #(200, lookup_platform(code)) + _ -> #(404, "") + } |> pair.new(True) + } + #(True, Ok(domain)) if domain == ril100_domain || domain == ds100_domain -> + case lookup_exact(query, ds100_to_name) { + #(200,_) -> #(200, lookup_platform(query)) + _ -> case fuzzy(query, DS100) { + Exact(code) -> #(200, lookup_platform(code)) + Fuzzy(_, code) -> #(200, lookup_platform(code)) + _ -> #(404, "") + } + } |> pair.new(True) _ -> { let by_ds100 = lookup_exact(query, ds100_to_name) let by_lp = lookup_exact(query, leitpunkt_to_name) @@ -180,16 +221,21 @@ fn lookup_station( #(200, _) -> #(302, proto <> ril100_domain <> "/" <> path) #(_, 200) -> #(302, proto <> leitpunkt_domain <> "/" <> path) _ -> #(302, proto <> ril100_domain <> "/" <> path) - } + } |> pair.new(False) } } } - _ -> #(404, "intended usage is e.g. curl " <> proto <> domain <> "/FF") + _ -> #(#(404, "intended usage is e.g. curl " <> proto <> domain <> "/FF"), False) } let body = text |> bit_builder.from_string |> mist.Bytes + let content_type = case is_html { + True -> "text/html; charset=utf8" + False -> "text/plain; charset=utf8" + } + response.new(code) |> response.prepend_header( "x-data-source", @@ -199,7 +245,7 @@ fn lookup_station( "x-sources-at", "https://stuebinm.eu/git/bahnhof.name", ) - |> response.prepend_header("content-type", "text/plain; charset=utf8") + |> response.prepend_header("content-type", content_type) |> fn(a) { case code == 302 { True -> response.prepend_header(a, "location", text) @@ -210,10 +256,10 @@ fn lookup_station( } pub fn main() { - let assert Ok(bahn_ril100) = fetch_data() + let assert Ok(bahn_ril100) = file.read("data/DBNetz-Betriebsstellenverzeichnis-Stand2021-10.csv") let ds100s = - read_csv(bahn_ril100) + read_csv(bahn_ril100, ";") |> list.filter_map(fn(fields) { case fields { [_, ds100, name, ..] -> Ok(#(name, ds100)) @@ -222,13 +268,15 @@ pub fn main() { }) let assert Ok(leitpunkte_raw) = file.read("data/leitpunkte.csv") let leitpunkte = - read_csv(leitpunkte_raw) + read_csv(leitpunkte_raw, ";") |> list.filter_map(fn(fields) { case fields { [lp, name, _ds100] -> Ok(#(name, lp)) _ -> Error(fields) } }) + let assert Ok(platforms_raw) = file.read("data/platforms.tsv") + let platforms = read_csv(platforms_raw, "\t") let name_to_ds100 = map.from_list(ds100s) let name_to_leitpunkt = map.from_list(leitpunkte) @@ -321,19 +369,36 @@ pub fn main() { fuzzy(searchterm, kind) |> list.filter_map(fn(res) { map.get(ids, string.uppercase(res)) }) case results { - [res] -> Fuzzy(res) - [res, ..] -> Fuzzy(res) + [res] -> { + let assert Ok(station) = map.get(stations, res) + Fuzzy(res, station) + } + [res, ..] -> { + let assert Ok(station) = map.get(stations, res) + Fuzzy(res, station) + } _ -> Failed } } } } + let lookup_platform = fn(ds100: String) -> String { + inspect(ds100) + platforms + |> list.filter(fn(a) { list.first(a) == Ok(ds100) }) + |> list.map(fn(line) { case line { + [_code,osmid,osmtype,info] -> "<a href=\"https://osm.org/"<>osmtype<>"/"<>osmid<>"\">"<>info<>"</a>" + }}) + |> string.join("<br>\n") + |> inspect + } + io.println("compiled indices, starting server …") let assert Ok(_) = fn(req: Request(mist.Connection)) -> Response(mist.ResponseData) { - lookup_station(req, ds100_to_name, leitpunkt_to_name, exact_then_fuzzy) + lookup_station(req, ds100_to_name, leitpunkt_to_name, lookup_platform, exact_then_fuzzy) } |> mist.new |> mist.port(2345) @@ -348,17 +413,18 @@ fn fetch_data() -> Result(String, hackney.Error) { "https://download-data.deutschebahn.com/static/datasets/betriebsstellen/DBNetz-Betriebsstellenverzeichnis-Stand2021-10.csv", ) let assert Ok(request) = request.from_uri(uri) + io.println("got response") let assert Ok(response) = hackney.send(request) // some ü are corrupted for some reason Ok(string.replace(response.body, "�", "ü")) } -fn read_csv(contents) -> List(List(String)) { +fn read_csv(contents, sep) -> List(List(String)) { contents // the file doesn't use quotes, so this is fine |> string.split(on: "\n") // drop CSV header |> list.drop(1) - |> list.map(fn(a) { string.split(a, on: ";") }) + |> list.map(fn(a) { string.split(a, on: sep) }) } |