diff --git a/apps/api_web/lib/api_web/controllers/route_controller.ex b/apps/api_web/lib/api_web/controllers/route_controller.ex index 5232ba9f6..59aed1a9c 100644 --- a/apps/api_web/lib/api_web/controllers/route_controller.ex +++ b/apps/api_web/lib/api_web/controllers/route_controller.ex @@ -311,6 +311,10 @@ defmodule ApiWeb.RouteController do direction_attribute(:direction_destinations, """ The destinations for direction ids for this route in ascending ordering starting at `0` for the first index. """) + + relationship(:agency, nullable: true) + relationship(:line, nullable: true) + relationship(:route_patterns, type: :has_many, nullable: true) end, Routes: page(:RouteResource), Route: single(:RouteResource) diff --git a/apps/api_web/lib/api_web/views/agency_view.ex b/apps/api_web/lib/api_web/views/agency_view.ex new file mode 100644 index 000000000..29c2503be --- /dev/null +++ b/apps/api_web/lib/api_web/views/agency_view.ex @@ -0,0 +1,5 @@ +defmodule ApiWeb.AgencyView do + use ApiWeb.Web, :api_view + + attributes([:agency_name]) +end diff --git a/apps/api_web/lib/api_web/views/route_view.ex b/apps/api_web/lib/api_web/views/route_view.ex index 9d18d03eb..8c0e1e5a2 100644 --- a/apps/api_web/lib/api_web/views/route_view.ex +++ b/apps/api_web/lib/api_web/views/route_view.ex @@ -5,6 +5,12 @@ defmodule ApiWeb.RouteView do def route_location(route, conn), do: route_path(conn, :show, route.id) + has_one( + :agency, + type: :agency, + serializer: ApiWeb.AgencyView + ) + has_one( :line, type: :line, @@ -48,6 +54,10 @@ defmodule ApiWeb.RouteView do direction_destinations)a) end + def agency(%{agency_id: agency_id}, conn) do + optional_relationship("agency", agency_id, &State.Agency.by_id/1, conn) + end + def line(%{line_id: line_id}, conn) do optional_relationship("line", line_id, &State.Line.by_id/1, conn) end diff --git a/apps/api_web/test/api_web/controllers/route_controller_test.exs b/apps/api_web/test/api_web/controllers/route_controller_test.exs index 2dddbc989..d5b33b295 100644 --- a/apps/api_web/test/api_web/controllers/route_controller_test.exs +++ b/apps/api_web/test/api_web/controllers/route_controller_test.exs @@ -456,7 +456,8 @@ defmodule ApiWeb.RouteControllerTest do "type" => "route_pattern" } ] - } + }, + "agency" => %{"data" => %{"id" => "1", "type" => "agency"}} } } end diff --git a/apps/model/lib/model/agency.ex b/apps/model/lib/model/agency.ex new file mode 100644 index 000000000..2c4fcc38d --- /dev/null +++ b/apps/model/lib/model/agency.ex @@ -0,0 +1,22 @@ +defmodule Model.Agency do + @moduledoc """ + Agency represents a branded agency operating transit services. + """ + + use Recordable, [ + :id, + :agency_name + ] + + @type id :: String.t() + + @typedoc """ + * `:id` - Unique ID + * `:agency_name` - Full name of the agency. See + [GTFS `agency.txt` `agency_name`](https://github.com/google/transit/blob/master/gtfs/spec/en/reference.md#agencytxt) + """ + @type t :: %__MODULE__{ + id: id, + agency_name: String.t() + } +end diff --git a/apps/parse/lib/parse/agency.ex b/apps/parse/lib/parse/agency.ex new file mode 100644 index 000000000..5cdadbbcc --- /dev/null +++ b/apps/parse/lib/parse/agency.ex @@ -0,0 +1,26 @@ +defmodule Parse.Agency do + @moduledoc """ + Parses `agency.txt` CSV from GTFS zip + + agency_id,agency_name,agency_url,agency_timezone,agency_lang,agency_phone + 1,MBTA,http://www.mbta.com,America/New_York,EN,617-222-3200 + """ + + use Parse.Simple + alias Model.Agency + + @doc """ + Parses (non-header) row of `agency.txt` + + ## Columns + + * `"agency_id"` - `Model.Agency.t` - `id` + * `"agency_name"` - `Model.Agency.t` - `agency_name` + """ + def parse_row(row) do + %Agency{ + id: copy(row["agency_id"]), + agency_name: copy(row["agency_name"]) + } + end +end diff --git a/apps/parse/test/parse/agency_test.exs b/apps/parse/test/parse/agency_test.exs new file mode 100644 index 000000000..c0cadfb01 --- /dev/null +++ b/apps/parse/test/parse/agency_test.exs @@ -0,0 +1,26 @@ +defmodule Parse.AgencyTest do + use ExUnit.Case, async: true + + import Parse.Agency + alias Model.Agency + + describe "parse_row/1" do + test "parses a route CSV map into an %Agency{}" do + row = %{ + "agency_id" => "1", + "agency_name" => "MBTA", + "agency_url" => "http://www.mbta.com", + "agency_timezone" => "America/New_York", + "agency_lang" => "EN", + "agency_phone" => "617-222-3200" + } + + expected = %Agency{ + id: "1", + agency_name: "MBTA" + } + + assert parse_row(row) == expected + end + end +end diff --git a/apps/state/lib/state.ex b/apps/state/lib/state.ex index 0281cd167..3638c4dc5 100644 --- a/apps/state/lib/state.ex +++ b/apps/state/lib/state.ex @@ -14,6 +14,7 @@ defmodule State do def start(_type, _args) do children = [ State.Metadata, + State.Agency, State.Service, State.Stop, State.Vehicle, diff --git a/apps/state/lib/state/agency.ex b/apps/state/lib/state/agency.ex new file mode 100644 index 000000000..8d2fd0756 --- /dev/null +++ b/apps/state/lib/state/agency.ex @@ -0,0 +1,21 @@ +defmodule State.Agency do + @moduledoc """ + Stores and indexes `Model.Agency.t` from `agency.txt`. + """ + + use State.Server, + indices: [:id], + fetched_filename: "agency.txt", + parser: Parse.Agency, + recordable: Model.Agency + + alias Model.Agency + + @spec by_id(Agency.id()) :: Agency.t() | nil + def by_id(id) do + case super(id) do + [] -> nil + [agency] -> agency + end + end +end diff --git a/apps/state/test/state/agency_test.exs b/apps/state/test/state/agency_test.exs new file mode 100644 index 000000000..8fc2676bd --- /dev/null +++ b/apps/state/test/state/agency_test.exs @@ -0,0 +1,23 @@ +defmodule State.AgencyTest do + use ExUnit.Case + alias Model.Agency + + setup do + State.Agency.new_state([]) + end + + test "returns nil for unknown agency" do + assert State.Agency.by_id("1") == nil + end + + test "it can add an agency and query it" do + agency = %Agency{ + id: "1", + agency_name: "Made-Up Transit Agency" + } + + State.Agency.new_state([agency]) + + assert State.Agency.by_id("1") == agency + end +end diff --git a/apps/state_mediator/lib/gtfs_decompress.ex b/apps/state_mediator/lib/gtfs_decompress.ex index 137f5d028..b0cc238c3 100644 --- a/apps/state_mediator/lib/gtfs_decompress.ex +++ b/apps/state_mediator/lib/gtfs_decompress.ex @@ -7,7 +7,8 @@ defmodule GtfsDecompress do """ require Logger - @filename_prefixes ~w(calendar + @filename_prefixes ~w(agency + calendar calendar_attributes calendar_dates feed_info