Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: RoutePatterns table, part 1: Adding to db #2900

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion lib/schedule/gtfs/direction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ defmodule Schedule.Gtfs.Direction do
alias Schedule.Gtfs.Route

@type id :: 0 | 1
@type name :: String.t()

@type t :: %__MODULE__{
route_id: Route.id(),
direction_id: id(),
direction_name: String.t()
direction_name: name()
}

@enforce_keys [
Expand Down
2 changes: 2 additions & 0 deletions lib/skate/detours/db/detour.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ defmodule Skate.Detours.Db.Detour do
import Ecto.Changeset

alias Skate.Settings.Db.User
alias Skate.Detours.Db.RoutePattern

typed_schema "detours" do
field :state, :map
belongs_to :author, User
belongs_to :route_pattern, RoutePattern

timestamps()
end
Expand Down
63 changes: 63 additions & 0 deletions lib/skate/detours/db/route_pattern.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Should this live here under detours?
# I don't like how the db folders are scattered throughout /lib subdirectories.
# I'd rather they all live in one subfolder.
# Example: there's db files in /notifications and /skate/detours and /skate/settings, etc
defmodule Skate.Detours.Db.RoutePattern do
@moduledoc """
Ecto Model for `route_patterns` Database table
"""

use Skate.Schema
import Ecto.Changeset

alias Skate.Detours.Db.Detour
alias Schedule.Gtfs.{Direction, Route, RoutePattern}

@required_fields [
:gtfs_route_pattern_id,
:gtfs_route_pattern_name,
:gtfs_route_pattern_headsign,
# :gtfs_route_pattern_shape,
:gtfs_route_pattern_direction_name,
:gtfs_route_id,
:gtfs_route_name
]

typed_schema "route_patterns" do
field(:gtfs_route_pattern_id, :string) :: RoutePattern.id()
field(:gtfs_route_pattern_name, :string)
field(:gtfs_route_pattern_headsign, :string)
field(:gtfs_route_pattern_direction_name, :string) :: Direction.name()

# The given route pattern will always have the given route
# Does it need to be stored here as well?
field(:gtfs_route_id, :string) :: Route.id()
field(:gtfs_route_name, :string)

field(:gtfs_route_pattern_time_description, :string)

# To be added: gtfs_route_pattern_shape (separate table)

has_many(:detours, Detour)

timestamps()
end

def changeset(route_pattern, attrs \\ %{}) do
route_pattern
|> cast(attrs, @required_fields, [:gtfs_route_pattern_time_description])
|> validate_required(@required_fields)
|> unique_constraint(
[
:gtfs_route_pattern_id,
:gtfs_route_pattern_name,
:gtfs_route_pattern_headsign,
:gtfs_route_pattern_direction_name,
:gtfs_route_id,
:gtfs_route_name,
:gtfs_route_pattern_time_description
],
name: :route_patterns_unique_index
)
end
end
17 changes: 14 additions & 3 deletions lib/skate/detours/detours.ex
Original file line number Diff line number Diff line change
Expand Up @@ -198,18 +198,18 @@ defmodule Skate.Detours.Detours do
"""
def upsert_from_snapshot(author_id, %{} = snapshot) do
previous_record =
case Skate.Detours.SnapshotSerde.id_from_snapshot(snapshot) do
case SnapshotSerde.id_from_snapshot(snapshot) do
nil -> nil
id -> Skate.Repo.get(Detour, id)
end

detour_db_result =
author_id
|> Skate.Detours.SnapshotSerde.deserialize(snapshot)
|> SnapshotSerde.deserialize(snapshot)
|> Skate.Repo.insert(
returning: true,
conflict_target: [:id],
on_conflict: {:replace, [:state, :updated_at]}
on_conflict: {:replace, [:state, :updated_at, :route_pattern_id]}
)

case detour_db_result do
Expand All @@ -223,6 +223,17 @@ defmodule Skate.Detours.Detours do
detour_db_result
end

@doc """
Retrieve or insert-and-return a route_pattern given a deserialized route_pattern.
"""
def get_or_create_route_pattern(route_pattern) do
Skate.Repo.insert!(
route_pattern,
returning: true,
on_conflict: :nothing
)
end

@doc """
Retrieves a `Skate.Detours.Db.Detour` from the database by it's ID and then resolves the
detour's category via `categorize_detour/2`
Expand Down
38 changes: 37 additions & 1 deletion lib/skate/detours/snapshot_serde.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@ defmodule Skate.Detours.SnapshotSerde do
Converts a XState JSON Snapshot to Detours Database Changeset
"""
def deserialize(user_id, %{} = snapshot) do
route_pattern =
snapshot
|> deserialize_route_pattern()
|> Skate.Detours.Detours.get_or_create_route_pattern()

Skate.Detours.Db.Detour.changeset(
%Skate.Detours.Db.Detour{
# `id` is `nil` by default, so a `nil` `id` should be fine
id: id_from_snapshot(snapshot),
author_id: user_id
author_id: user_id,
route_pattern_id: Map.get(route_pattern, :id)
},
%{
# Save Snapshot to DB until we've fully transitioned to serializing
Expand All @@ -32,6 +38,36 @@ defmodule Skate.Detours.SnapshotSerde do
def id_from_snapshot(%{"context" => %{"uuid" => id}}), do: id
def id_from_snapshot(%{"context" => %{}}), do: nil

# Converts the RoutePattern from a XState Snapshot to ready for DB insertion
defp deserialize_route_pattern(%{
"context" => %{
"route" => %{
"id" => route_id,
"name" => route_name,
"directionNames" => direction_map
},
"routePattern" =>
%{
"id" => route_pattern_id,
"name" => route_pattern_name,
"headsign" => headsign,
"directionId" => direction_id
} = route_pattern
}
}) do
direction_name = direction_map[Integer.to_string(direction_id)]

%Skate.Detours.Db.RoutePattern{
gtfs_route_pattern_id: route_pattern_id,
gtfs_route_pattern_name: route_pattern_name,
gtfs_route_pattern_headsign: headsign,
gtfs_route_pattern_direction_name: direction_name,
gtfs_route_id: route_id,
gtfs_route_name: route_name,
gtfs_route_pattern_time_description: Map.get(route_pattern, "timeDescription")
}
end

@doc """
Builds XState Snapshot from Detours Database object
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
defmodule Skate.Repo.Migrations.CreateRoutePatternsTable do
use Ecto.Migration

def change do
create table(:route_patterns) do
add :gtfs_route_pattern_id, :string
add :gtfs_route_pattern_name, :string
add :gtfs_route_pattern_headsign, :string
add :gtfs_route_pattern_direction_name, :string
add :gtfs_route_id, :string
add :gtfs_route_name, :string
add :gtfs_route_pattern_time_description, :string
timestamps()
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule Skate.Repo.Migrations.AddUniqueConstraintRoutePattern do
use Ecto.Migration

@disable_ddl_transaction true
@disable_migration_lock true

def change do
create(
index(
:route_patterns,
[
:gtfs_route_pattern_id,
:gtfs_route_pattern_name,
:gtfs_route_pattern_headsign,
:gtfs_route_pattern_direction_name,
:gtfs_route_id,
:gtfs_route_name,
:gtfs_route_pattern_time_description
],
unique: true,
nulls_distinct: false,
concurrently: true,
name: "route_patterns_unique_index"
)
)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule Skate.Repo.Migrations.DetourHasOneRoutePattern do
use Ecto.Migration

def change do
alter table(:detours) do
add :route_pattern_id, references(:route_patterns)
end
end
end
Loading
Loading