axdc
axdc
AEAsh Elixir
Created by axdc on 5/2/2023 in #support
Multiple checkbox group with Ash Forms
for sure! might be dragons, haven't touched this in a while, had a lot going on but things have changed and i should get to be active with elixir again very soon 🙂
22 replies
AEAsh Elixir
Created by axdc on 5/2/2023 in #support
Multiple checkbox group with Ash Forms
22 replies
AEAsh Elixir
Created by axdc on 5/2/2023 in #support
Multiple checkbox group with Ash Forms
okay so the answer is yes because it works, but I absolutely don't remember how. reviewing the code now
22 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
require Ash.Query
require Ash.Query
read :nearest do
argument :location, :geo_any do
allow_nil? false
constraints geo_types: :point, force_srid: 4326
end

prepare fn query, _ ->
query
|> Ash.Query.filter(not is_nil(location))
|> Ash.Query.sort(distance_from: {:asc, %{location: query.arguments.location}})
end

pagination do
offset? true
end
end
read :nearest do
argument :location, :geo_any do
allow_nil? false
constraints geo_types: :point, force_srid: 4326
end

prepare fn query, _ ->
query
|> Ash.Query.filter(not is_nil(location))
|> Ash.Query.sort(distance_from: {:asc, %{location: query.arguments.location}})
end

pagination do
offset? true
end
end
calculations do
calculate :distance_from,
:geometry,
expr(fragment("? <-> ?", location, ^arg(:location))) do
argument :location, :geo_any, constraints: [geo_types: :point, force_srid: 4326]
end
end
calculations do
calculate :distance_from,
:geometry,
expr(fragment("? <-> ?", location, ^arg(:location))) do
argument :location, :geo_any, constraints: [geo_types: :point, force_srid: 4326]
end
end
32 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
Nice, definitely wanna stay in-ecosystem for all the goodies 🙂
32 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
given that correction:
* filter: Invalid reference listing.location at relationship_path [:listing]
at filter
* filter: Invalid reference listing.location at relationship_path [:listing]
at filter
32 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
Is a benefit of using Ash primitives like preparations and calculations rather than modify_query that it will be simpler to integrate with other ash functionality like pagination?
32 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
I'm getting error: undefined variable "listing", reading the prepare docs. Haven't used this part of Ash yet!
32 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
(I'm not sure if this is optimal or the pros/cons vs a calculate-based approach)
32 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
I have my system working! The action:
read :nearest do
argument :location, :geo_any do
allow_nil? false
constraints geo_types: :point, force_srid: 4326
end

modify_query {Panacea.Lociary.ManualNearest, :modify, []}
end
read :nearest do
argument :location, :geo_any do
allow_nil? false
constraints geo_types: :point, force_srid: 4326
end

modify_query {Panacea.Lociary.ManualNearest, :modify, []}
end
The "modified" query (I guess I'm more making one from scratch using the ash argument):
defmodule Panacea.Lociary.ManualNearest do
import Ecto.Query

def modify(ash_query, ecto_query) do

location = ash_query.arguments.location
modified_ecto_query =
from listing in Panacea.Lociary.Listing,
where: not (listing.location |> is_nil()),
order_by: [asc: fragment("? <-> ?", listing.location, ^location)],
limit: 10

IO.inspect(modified_ecto_query)
{:ok, modified_ecto_query}
end
end
defmodule Panacea.Lociary.ManualNearest do
import Ecto.Query

def modify(ash_query, ecto_query) do

location = ash_query.arguments.location
modified_ecto_query =
from listing in Panacea.Lociary.Listing,
where: not (listing.location |> is_nil()),
order_by: [asc: fragment("? <-> ?", listing.location, ^location)],
limit: 10

IO.inspect(modified_ecto_query)
{:ok, modified_ecto_query}
end
end
I'm going to have to filter out some obviously bad data in addition to nils (i've got some null islands and some just... strange entries) but it's working!
32 replies
AEAsh Elixir
Created by axdc on 8/25/2023 in #support
Creating an Identity referencing a key within a :map attribute (postgres-stored jsonb)
ok, got it. so for reference what I'm doing right now (as a "workaround", but it works the same) is just extracting the values I want to key on from the json before sending it all to ash, and I have those keys as real attributes in the resource with an identity defined on them. Working great. Those specific keys should be stable. I /would/ like, in another area, to /select and filter/ based on the contents of the jsonb, which is why I figured I'd try to jump right in with upserting on it. Is reading, filtering, etc supported? (The data has over five hundred fields in an unstable "schema", that's why I'm using jsonb. I've been informed postgres wouldn't take kindly to that many columns.)
7 replies
AEAsh Elixir
Created by axdc on 5/23/2023 in #support
LiveView uploads with AshPhoenix.Form
defp consume_uploads(socket) do
consume_uploaded_entries(socket, :wallpaper_url, fn %{path: path}, _entry ->
file = File.read!(path)

object_key = "wallpaper/" <> socket.assigns.current_site.id <> ".jpg"

case ExAws.S3.put_object(
"panacea",
object_key,
file,
content_type: "image/jpeg",
acl: :public_read
)
|> ExAws.request() do
{:ok, _} -> Logger.info("Uploaded file")
{:error, error} -> Logger.error("Error uploading file: #{IO.inspect(error)}")
end

cache_buster = "?" <> (System.os_time() |> Integer.to_string())

url =
"https://panacea.sfo3.cdn.digitaloceanspaces.com/" <>
object_key <> cache_buster

{:ok, url}
end)
end
defp consume_uploads(socket) do
consume_uploaded_entries(socket, :wallpaper_url, fn %{path: path}, _entry ->
file = File.read!(path)

object_key = "wallpaper/" <> socket.assigns.current_site.id <> ".jpg"

case ExAws.S3.put_object(
"panacea",
object_key,
file,
content_type: "image/jpeg",
acl: :public_read
)
|> ExAws.request() do
{:ok, _} -> Logger.info("Uploaded file")
{:error, error} -> Logger.error("Error uploading file: #{IO.inspect(error)}")
end

cache_buster = "?" <> (System.os_time() |> Integer.to_string())

url =
"https://panacea.sfo3.cdn.digitaloceanspaces.com/" <>
object_key <> cache_buster

{:ok, url}
end)
end
27 replies
AEAsh Elixir
Created by axdc on 5/23/2023 in #support
LiveView uploads with AshPhoenix.Form
that would be fantastic 🙂
27 replies
AEAsh Elixir
Created by axdc on 5/23/2023 in #support
LiveView uploads with AshPhoenix.Form
still getting used to elixir's scope and mindset of assigning from private functions, instead of the nested if hell i find myself reaching for first. but this is sooo much cleaner 😭
27 replies
AEAsh Elixir
Created by axdc on 5/23/2023 in #support
LiveView uploads with AshPhoenix.Form
okay this feels like a breakthrough, excited rn is there anything devastatingly busted about this approach? it's working :>
def handle_event("save", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)

{socket, params} = maybe_put_uploaded_wallpaper_url(socket, params)

IO.inspect(params)

case AshPhoenix.Form.submit(form,
params: params
) do
{:ok, configuration} ->
{:noreply,
socket
|> assign(form: form)
|> assign(configuration: configuration)
|> put_flash(:info, "Settings updated successfully")
|> push_patch(to: ~p"/rosegarden/configuration")}

{:error, form} ->
Logger.error(AshPhoenix.Form.errors(form, for_path: :all))

{:noreply, assign(socket, form: form)}
end
end


defp maybe_put_uploaded_wallpaper_url(socket, params) do
wallpaper_url = consume_uploads(socket) |> List.first()

if(wallpaper_url) do
# a wallpaper was uploaded
Logger.info("handling uploaded wallpaper")
socket = assign(socket, :wallpaper_url, wallpaper_url)
params = Map.put(params, "wallpaper_url", socket.assigns.wallpaper_url)
{socket, params}
else
# pass through unchanged
{socket, params}
end
end
def handle_event("save", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)

{socket, params} = maybe_put_uploaded_wallpaper_url(socket, params)

IO.inspect(params)

case AshPhoenix.Form.submit(form,
params: params
) do
{:ok, configuration} ->
{:noreply,
socket
|> assign(form: form)
|> assign(configuration: configuration)
|> put_flash(:info, "Settings updated successfully")
|> push_patch(to: ~p"/rosegarden/configuration")}

{:error, form} ->
Logger.error(AshPhoenix.Form.errors(form, for_path: :all))

{:noreply, assign(socket, form: form)}
end
end


defp maybe_put_uploaded_wallpaper_url(socket, params) do
wallpaper_url = consume_uploads(socket) |> List.first()

if(wallpaper_url) do
# a wallpaper was uploaded
Logger.info("handling uploaded wallpaper")
socket = assign(socket, :wallpaper_url, wallpaper_url)
params = Map.put(params, "wallpaper_url", socket.assigns.wallpaper_url)
{socket, params}
else
# pass through unchanged
{socket, params}
end
end
27 replies
AEAsh Elixir
Created by axdc on 5/23/2023 in #support
LiveView uploads with AshPhoenix.Form
EDIT: I'm getting ** (KeyError) key :wallpaper_url not found in: %{__changed__: %{}, ... when I don't change the wallpaper, so I think I might have to assign that to something at mount? But when I upload it persists now, fixed a bug with using an atom instead of a string as a key
def handle_event("save", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)

socket = socket |> maybe_put_uploaded_wallpaper_url()

params = Map.put(params, "wallpaper_url", socket.assigns.wallpaper_url)
IO.inspect(params)

case AshPhoenix.Form.submit(form,
params: params
) do
{:ok, configuration} ->
{:noreply,
socket
|> assign(form: form)
|> assign(configuration: configuration)
|> put_flash(:info, "Settings updated successfully")
|> push_patch(to: ~p"/rosegarden/configuration")}

{:error, form} ->
Logger.error(AshPhoenix.Form.errors(form, for_path: :all))

{:noreply, assign(socket, form: form)}
end
end

defp maybe_put_uploaded_wallpaper_url(socket) do
wallpaper_url = consume_uploads(socket) |> List.first()

if(wallpaper_url) do
# a wallpaper was uploaded
Logger.info("handling uploaded wallpaper")
assign(socket, :wallpaper_url, wallpaper_url)
else
# pass through form unchanged
socket
end
end
def handle_event("save", %{"form" => params}, socket) do
form = AshPhoenix.Form.validate(socket.assigns.form, params)

socket = socket |> maybe_put_uploaded_wallpaper_url()

params = Map.put(params, "wallpaper_url", socket.assigns.wallpaper_url)
IO.inspect(params)

case AshPhoenix.Form.submit(form,
params: params
) do
{:ok, configuration} ->
{:noreply,
socket
|> assign(form: form)
|> assign(configuration: configuration)
|> put_flash(:info, "Settings updated successfully")
|> push_patch(to: ~p"/rosegarden/configuration")}

{:error, form} ->
Logger.error(AshPhoenix.Form.errors(form, for_path: :all))

{:noreply, assign(socket, form: form)}
end
end

defp maybe_put_uploaded_wallpaper_url(socket) do
wallpaper_url = consume_uploads(socket) |> List.first()

if(wallpaper_url) do
# a wallpaper was uploaded
Logger.info("handling uploaded wallpaper")
assign(socket, :wallpaper_url, wallpaper_url)
else
# pass through form unchanged
socket
end
end
27 replies
AEAsh Elixir
Created by axdc on 5/2/2023 in #support
Multiple checkbox group with Ash Forms
Confirmed that with or without the hidden field, the checked values don't stay checked
22 replies
AEAsh Elixir
Created by axdc on 5/2/2023 in #support
Multiple checkbox group with Ash Forms
I would like to do it in a not-strange way 😓
22 replies