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

Ash Radio API, Feed with admin panel #150

Merged
merged 4 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion server/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ config :orcasite, OrcasiteWeb.Auth.AuthAccessPipeline,
error_handler: OrcasiteWeb.Auth.AuthErrorHandler

config :ash, :use_all_identities_in_manage_relationship?, false
config :orcasite, :ash_apis, [Orcasite.Notifications, Orcasite.Accounts]
config :orcasite, :ash_apis, [Orcasite.Notifications, Orcasite.Accounts, Orcasite.Radio]
config :orcasite, :ecto_repos, [Orcasite.Repo]
config :ash, :custom_types, [geometry: Orcasite.Types.Geometry]

config :orcasite, Oban,
repo: Orcasite.Repo,
Expand Down
6 changes: 5 additions & 1 deletion server/lib/orcasite/accounts/user.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Orcasite.Accounts.User do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication]
extensions: [AshAuthentication, AshAdmin.Resource]


attributes do
Expand Down Expand Up @@ -46,4 +46,8 @@ defmodule Orcasite.Accounts.User do
actions do
defaults [:read, :create, :update, :destroy]
end

admin do
table_columns [:id, :email, :first_name, :last_name, :admin, :inserted_at]
end
end
4 changes: 4 additions & 0 deletions server/lib/orcasite/notifications/resources/notification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ defmodule Orcasite.Notifications.Notification do
end

admin do
table_columns [:id, :meta, :event_type, :inserted_at]

format_fields meta: {Jason, :encode!, []}

form do
field :event_type, type: :default
end
Expand Down
10 changes: 10 additions & 0 deletions server/lib/orcasite/notifications/resources/subscriber.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,14 @@ defmodule Orcasite.Notifications.Subscriber do
relationships do
has_many :subscriptions, Subscription
end

admin do
table_columns [:id, :name, :meta, :inserted_at]

format_fields meta: {Jason, :encode!, []}

form do
field :event_type, type: :default
end
end
end
6 changes: 6 additions & 0 deletions server/lib/orcasite/notifications/resources/subscription.ex
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,10 @@ defmodule Orcasite.Notifications.Subscription do
{:ok, token} = AshAuthentication.Strategy.MagicLink.request_token_for(strategy, subscription)
token
end

admin do
table_columns [:id, :name, :meta, :active, :event_type, :subscriber_id, :inserted_at]

format_fields meta: {Jason, :encode!, []}
end
end
11 changes: 11 additions & 0 deletions server/lib/orcasite/radio.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Orcasite.Radio do
use Ash.Api, extensions: [AshAdmin.Api]

resources do
registry Orcasite.Radio.Registry
end

admin do
show? true
end
end
15 changes: 15 additions & 0 deletions server/lib/orcasite/radio/calculations/longitude_latitude.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Orcasite.Radio.Calculations.LongitudeLatitude do
use Ash.Calculation

@impl true
def load(_query, _opts, _context) do
[:location_point]
end

@impl true
def calculate(records, _opts, _arguments) do
Enum.map(records, fn %{location_point: %{coordinates: {lng, lat}}} ->
"#{lng},#{lat}"
end)
end
end
127 changes: 100 additions & 27 deletions server/lib/orcasite/radio/feed.ex
Original file line number Diff line number Diff line change
@@ -1,40 +1,113 @@
defmodule Orcasite.Radio.Feed do
use Ecto.Schema
import Ecto.Changeset
use Ash.Resource,
extensions: [AshAdmin.Resource],
data_layer: AshPostgres.DataLayer

alias __MODULE__
attributes do
integer_primary_key :id

schema "feeds" do
field(:name, :string)
field(:slug, :string)
field(:node_name, :string)
field(:location_point, Geo.PostGIS.Geometry)
attribute :name, :string
attribute :node_name, :string
attribute :slug, :string
attribute :location_point, :geometry

timestamps()
create_timestamp :inserted_at
update_timestamp :updated_at
end

@doc false
def changeset(feed, attrs) do
feed
|> cast(attrs, [:name, :node_name, :slug, :location_point])
|> validate_required([:name, :node_name, :slug])
postgres do
table "feeds"
repo Orcasite.Repo
end

def latlong_to_geo(lat, long) when is_float(lat) and is_float(long),
do: Geo.WKT.decode!("SRID=4326;POINT(#{lat} #{long})")
identities do
identity :unique_slug, [:slug]
end

actions do
defaults [:destroy]

read :read do
primary? true

prepare fn query, _context ->
query
|> Ash.Query.load(:longitude_latitude)
end
end

read :get_by_slug do
get_by :slug
end

create :create do
primary? true
reject [:location_point]

argument :longitude_latitude, :string do
description "A comma-separated string of longitude and latitude"
end

change &change_longitude_latitude/2
end

update :update do
primary? true
reject [:location_point]

argument :longitude_latitude, :string do
description "A comma-separated string of longitude and latitude"
end

# TODO: Find the actual json -> schema function
def from_json(attrs) do
%Feed{}
|> cast(
decode_location_point(attrs),
Map.keys(Orcasite.Utils.atomize_keys(attrs))
)
|> apply_changes()
change &change_longitude_latitude/2
end
end

def decode_location_point(%{"location_point" => point} = attrs) when is_binary(point),
do: %{attrs | "location_point" => Geo.WKB.decode!(attrs["location_point"])}
code_interface do
define_for Orcasite.Radio

def decode_location_point(attrs), do: attrs
define :get_feed_by_slug, action: :get_by_slug, args: [:slug], get?: true
end

calculations do
calculate :longitude_latitude,
:string,
{Orcasite.Radio.Calculations.LongitudeLatitude,
keys: [:location_point], select: [:location_point]}
end

defp change_longitude_latitude(changeset, _context) do
with {:is_string, lng_lat} when is_binary(lng_lat) <-
{:is_string, Ash.Changeset.get_argument(changeset, :longitude_latitude)},
{:two_els, [lng, lat]} <-
{:two_els, lng_lat |> String.split(",") |> Enum.map(&String.trim/1)},
{:two_floats, [{longitude, _}, {latitude, _}]} <-
{:two_floats, [lng, lat] |> Enum.map(&Float.parse/1)} do
changeset
|> Ash.Changeset.change_attribute(:location_point, %Geo.Point{
coordinates: {longitude, latitude},
srid: 4326
})
else
{:is_string, _} ->
changeset

{:two_els, _} ->
changeset
|> Ash.Changeset.add_error(
field: :longitude_latitude,
message: "must be a comma-separated string"
)

{:two_floats, _} ->
changeset
|> Ash.Changeset.add_error(field: :longitude_latitude, message: "must be two floats")
end
end

admin do
table_columns [:id, :name, :slug, :node_name, :location_point]

format_fields location_point: {Jason, :encode!, []}
end
end
8 changes: 8 additions & 0 deletions server/lib/orcasite/radio/registry.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule Orcasite.Radio.Registry do
use Ash.Registry, extensions: [Ash.Registry.ResourceValidations]

entries do
entry Orcasite.Radio.Feed
end

end
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule Orcasite.Radio.Candidate do
defmodule Orcasite.RadioLegacy.Candidate do
use Ecto.Schema
import Ecto.Changeset

alias Orcasite.Radio.{Detection, Feed}
alias Orcasite.RadioLegacy.{Detection, Feed}

schema "candidates" do
field(:detection_count, :integer)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule Orcasite.Radio.Detection do
defmodule Orcasite.RadioLegacy.Detection do
use Ecto.Schema
import Ecto.Changeset

alias Orcasite.Radio.{Feed, Candidate}
alias Orcasite.RadioLegacy.{Feed, Candidate}
alias __MODULE__

schema "detections" do
Expand Down
40 changes: 40 additions & 0 deletions server/lib/orcasite/radio_legacy/feed_legacy.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule Orcasite.RadioLegacy.Feed do
use Ecto.Schema
import Ecto.Changeset

alias __MODULE__

schema "feeds" do
field(:name, :string)
field(:slug, :string)
field(:node_name, :string)
field(:location_point, Geo.PostGIS.Geometry)

timestamps()
end

@doc false
def changeset(feed, attrs) do
feed
|> cast(attrs, [:name, :node_name, :slug, :location_point])
|> validate_required([:name, :node_name, :slug])
end

def latlong_to_geo(lat, long) when is_float(lat) and is_float(long),
do: Geo.WKT.decode!("SRID=4326;POINT(#{lat} #{long})")

# TODO: Find the actual json -> schema function
def from_json(attrs) do
%Feed{}
|> cast(
decode_location_point(attrs),
Map.keys(Orcasite.Utils.atomize_keys(attrs))
)
|> apply_changes()
end

def decode_location_point(%{"location_point" => point} = attrs) when is_binary(point),
do: %{attrs | "location_point" => Geo.WKB.decode!(attrs["location_point"])}

def decode_location_point(attrs), do: attrs
end
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
defmodule Orcasite.Radio do
defmodule Orcasite.RadioLegacy do
@moduledoc """
The Radio context.
The RadioLegacy context.
"""

import Ecto.Query, warn: false
alias Orcasite.Repo
alias Orcasite.Radio.{Feed, Detection, Candidate}

def list_feeds do
Repo.all(Feed)
end

def get_feed!(id), do: Repo.get!(Feed, id)

def get_feed_by_slug(slug) do
Feed
|> where(slug: ^slug)
|> limit(1)
|> Repo.one()
end

def create_feed(attrs \\ %{}) do
%Feed{}
|> Feed.changeset(attrs)
|> Repo.insert()
end

def update_feed(%Feed{} = feed, attrs) do
feed
|> Feed.changeset(attrs)
|> Repo.update()
end
alias Orcasite.RadioLegacy.{Feed, Detection, Candidate}

def verify_can_submit_detection(
feed_id,
Expand Down
43 changes: 43 additions & 0 deletions server/lib/orcasite/types/geometry.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
defmodule Orcasite.Types.Geometry do
@moduledoc false

use Ash.Type

@impl true
def storage_type, do: :geometry

@impl true
def cast_input(nil, _), do: {:ok, nil}

def cast_input(value, _) do
Geo.PostGIS.Geometry.cast(value)
end

@impl true
def cast_stored(nil, _), do: {:ok, nil}

def cast_stored(value, _) do
Geo.PostGIS.Geometry.load(value)
end

@impl true
def dump_to_native(nil, _), do: {:ok, nil}

def dump_to_native(value, _) do
Geo.PostGIS.Geometry.dump(value)
end
end

if Code.ensure_loaded?(Ecto.DevLogger) do
defimpl Ecto.DevLogger.PrintableParameter, for: Geo.Point do
def to_expression(point) do
point
|> to_string_literal()
|> Ecto.DevLogger.Utils.in_string_quotes()
end

def to_string_literal(point) do
Geo.WKT.Encoder.encode!(point)
end
end
end
Loading
Loading