Skip to content

Commit

Permalink
Provide agency information for route (#834)
Browse files Browse the repository at this point in the history
* chore: agency model

* chore: agency parser

* chore: agency state module

* chore: parse agency.txt from GTFS zip

* feat: surface agency relationship in route view

* docs: document agency relationship on route

* docs: also document line relationship from route

* docs: also document route_patterns relationship

* fix: indicate that all of the relationships on routes are nullable
  • Loading branch information
lemald authored Oct 10, 2024
1 parent 4bfc9b2 commit 5c34a9c
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 2 deletions.
4 changes: 4 additions & 0 deletions apps/api_web/lib/api_web/controllers/route_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions apps/api_web/lib/api_web/views/agency_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule ApiWeb.AgencyView do
use ApiWeb.Web, :api_view

attributes([:agency_name])
end
10 changes: 10 additions & 0 deletions apps/api_web/lib/api_web/views/route_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ defmodule ApiWeb.RouteControllerTest do
"type" => "route_pattern"
}
]
}
},
"agency" => %{"data" => %{"id" => "1", "type" => "agency"}}
}
}
end
Expand Down
22 changes: 22 additions & 0 deletions apps/model/lib/model/agency.ex
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions apps/parse/lib/parse/agency.ex
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions apps/parse/test/parse/agency_test.exs
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions apps/state/lib/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ defmodule State do
def start(_type, _args) do
children = [
State.Metadata,
State.Agency,
State.Service,
State.Stop,
State.Vehicle,
Expand Down
21 changes: 21 additions & 0 deletions apps/state/lib/state/agency.ex
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions apps/state/test/state/agency_test.exs
Original file line number Diff line number Diff line change
@@ -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
3 changes: 2 additions & 1 deletion apps/state_mediator/lib/gtfs_decompress.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ defmodule GtfsDecompress do
"""
require Logger

@filename_prefixes ~w(calendar
@filename_prefixes ~w(agency
calendar
calendar_attributes
calendar_dates
feed_info
Expand Down

0 comments on commit 5c34a9c

Please sign in to comment.