From e85241c99846b92da2c550cdaeff82029911d741 Mon Sep 17 00:00:00 2001 From: Zoe Moore Date: Sun, 16 Oct 2022 00:21:28 -0700 Subject: [PATCH] Really rough bart implementation --- actransit.nim | 5 +-- bart.nim | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++ toolbox.nim | 35 +++++++++++++--- 3 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 bart.nim diff --git a/actransit.nim b/actransit.nim index 324d29e..ae0bcf4 100644 --- a/actransit.nim +++ b/actransit.nim @@ -15,8 +15,7 @@ proc format_predictions*(prd: JSONNode): string = else: prd["prd"].getElems.map(time => time["prdctdn"].getStr).join(", ") -proc home_predictions*: string = - let client = newHttpClient() +proc home_predictions*(client: HTTPClient): string = const home_nl = "51067" const home_12 = "58995" const home_29 = "56557" @@ -27,7 +26,7 @@ proc home_predictions*: string = &"Home\nNL: {nl_predictions}\n12: {twelve_predictions}\n29: {twenty_nine_predictions}" -proc office_predictions*: string = +proc office_predictions*(client: HTTPClient): string = let client = newHttpClient() const office_nl = "56565" const office_12 = "57111" diff --git a/bart.nim b/bart.nim new file mode 100644 index 0000000..5b49903 --- /dev/null +++ b/bart.nim @@ -0,0 +1,109 @@ +import std/[json, httpclient, sequtils, strformat, strutils] + +type Station = object + id: string + name: string + +type Departure = ref object + destination: string + color: string + estimates: seq[uint] + +proc newStation(id: string, name: string): Station = + Station(id: id, name: name) + + +const bart_stations = @[ + newStation("12th", "12th St. Oakland City Center"), + newStation("16th", "16th St. Mission (SF)"), + newStation("19th", "19th St. Oakland"), + newStation("24th", "24th St. Mission (SF)"), + newStation("ashb", "Ashby (Berkeley)"), + newStation("antc", "Antioch"), + newStation("balb", "Balboa Park (SF)"), + newStation("bayf", "Bay Fair (San Leandro)"), + newStation("bery", "Berryessa / North San Jose"), + newStation("cast", "Castro Valley"), + newStation("civc", "Civic Center (SF)"), + newStation("cols", "Coliseum"), + newStation("colm", "Colma"), + newStation("conc", "Concord"), + newStation("daly", "Daly City"), + newStation("dbrk", "Downtown Berkeley"), + newStation("dubl", "Dublin/Pleasanton"), + newStation("deln", "El Cerrito del Norte"), + newStation("plza", "El Cerrito Plaza"), + newStation("embr", "Embarcadero (SF)"), + newStation("frmt", "Fremont"), + newStation("ftvl", "Fruitvale (Oakland)"), + newStation("glen", "Glen Park (SF)"), + newStation("hayw", "Hayward"), + newStation("lafy", "Lafayette"), + newStation("lake", "Lake Merritt (Oakland)"), + newStation("mcar", "MacArthur (Oakland)"), + newStation("mlbr", "Millbrae"), + newStation("mlpt", "Milpitas"), + newStation("mont", "Montgomery St. (SF)"), + newStation("nbrk", "North Berkeley"), + newStation("ncon", "North Concord/Martinez"), + newStation("oakl", "Oakland Int'l Airport"), + newStation("orin", "Orinda"), + newStation("pitt", "Pittsburg/Bay Point"), + newStation("pctr", "Pittsburg Center"), + newStation("phil", "Pleasant Hill"), + newStation("powl", "Powell St. (SF)"), + newStation("rich", "Richmond"), + newStation("rock", "Rockridge (Oakland)"), + newStation("sbrn", "San Bruno"), + newStation("sfia", "San Francisco Int'l Airport"), + newStation("sanl", "San Leandro"), + newStation("shay", "South Hayward"), + newStation("ssan", "South San Francisco"), + newStation("ucty", "Union City"), + newStation("warm", "Warm Springs/South Fremont"), + newStation("wcrk", "Walnut Creek"), + newStation("wdub", "West Dublin"), + newStation("woak", "West Oakland"), +] + +proc bart_station_names*(): seq[string] = + bart_stations.map(proc(s: Station): string = s.name) + +proc get_departures(client: HTTPClient, station: Station): JSONNode = + let id = station.id + let response = client.get(&"http://api.bart.gov/api/etd.aspx?cmd=etd&orig={id}&key=MW9S-E7SL-26DU-VV8V&json=y") + parseJSON(response.body) + +proc format_estimate_minutes(minutes: string): uint = + case minutes: + of "Leaving": + return 0.uint + else: + return parseUInt(minutes) + +proc parse_departures(etd: JSONNode): seq[Departure] = + if not etd["root"].hasKey("station"): + return @[] + + let lines = etd["root"]["station"][0]["etd"].getElems + + lines.map(proc (line: JSONNode): Departure = + let dest = line["destination"].getStr + let color = line["estimate"][0]["color"].getStr + + let estimates = line["estimate"].getElems.map(proc(estimate: JSONNode): uint = + format_estimate_minutes(estimate["minutes"].getStr) + ) + + Departure(destination: dest, color: color, estimates: estimates) + ) + +proc format_departures(departures: seq[Departure]): string = + departures.map(proc (d: Departure): string = + let estimates = d.estimates.join(", ") + &"{d.destination}\t{estimates}" + ).join("\n") + +proc prediction_for_station_idx*(client: HTTPClient, idx: int): string = + let station = bart_stations[idx] + client.get_departures(station).parse_departures().format_departures() diff --git a/toolbox.nim b/toolbox.nim index e490903..16dc54b 100644 --- a/toolbox.nim +++ b/toolbox.nim @@ -1,9 +1,12 @@ import gintro/[gtk4, gobject, gio, adw] -import std/[with] +import std/[with, httpclient] import actransit import metar +import bart -const airports = @["khwd", "koak", "klvk", "khaf"] +const airports = @["khwd", "koak", "klvk", "khaf", "kmry", "ksac"] + +let client = newHttpClient() proc clearMetars(metar_list: gtk4.ListBox) = for i in 1..airports.len: @@ -24,12 +27,16 @@ proc addMetars(metar_list: gtk4.ListBox) = proc button_refresh_signal(b: Button, widgets: tuple[vs: adw.ViewStack, home: Label, office: Label, metar_list: gtk4.ListBox]) = if widgets.vs.get_visible_child_name() == "act_view": - widgets.home.text = home_predictions().cstring - widgets.office.text = office_predictions().cstring + widgets.home.text = client.home_predictions().cstring + widgets.office.text = client.office_predictions().cstring elif widgets.vs.get_visible_child_name() == "metar_view": clearMetars(widgets.metar_list) addMetars(widgets.metar_list) +proc bart_departures_signal(b: Button, widgets: tuple[dropdown: DropDown, label: Label]) = + let prediction = client.prediction_for_station_idx(widgets.dropdown.getSelected()) + widgets.label.text = prediction.cstring + proc activate(app: adw.Application) = let window = adw.newApplicationWindow(app) @@ -37,6 +44,9 @@ proc activate(app: adw.Application) = switcher_bar = adw.newViewSwitcherBar() bart_box = newBox(Orientation.vertical, 0) + bart_station_dropdown = newDropDownFromStrings(bart_station_names()) + bart_get_departures_button = newButton() + bart_label = newLabel() metar_box = newBox(Orientation.vertical, 0) metar_list = newListBox() @@ -77,6 +87,19 @@ proc activate(app: adw.Application) = append home_label append office_label + with bart_box: + marginTop = 10 + marginEnd = 10 + marginStart = 10 + append bart_station_dropdown + append bart_get_departures_button + append bart_label + + with bart_get_departures_button: + marginTop = 10 + label = "Get departures".cstring + connect("clicked", bart_departures_signal, (bart_station_dropdown, bart_label)) + with metar_box: append metar_list @@ -92,7 +115,7 @@ proc activate(app: adw.Application) = iconName = "view-refresh" with home_label: - text = home_predictions().cstring + text = client.home_predictions().cstring marginTop = 10 marginBottom = 10 marginStart = 10 @@ -100,7 +123,7 @@ proc activate(app: adw.Application) = halign = Align.start with office_label: - text = office_predictions().cstring + text = client.office_predictions().cstring marginTop = 10 marginBottom = 10 marginStart = 10