Skip to content

Commit 695741c

Browse files
authored
Send new_detection notification on machine detection using AshOban trigger (#957)
* improvement: send new_detection notification on machine detection using AshOban trigger * Fix validation for detection * chore: fix wrong names * chore: fix policies * fix: set proper policy for machine detection action * chore: remove unused notify_bout action, fix event_type during notify_live_bout * chore: run format
1 parent 92085ca commit 695741c

File tree

15 files changed

+159
-147
lines changed

15 files changed

+159
-147
lines changed

server/config/runtime.exs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,4 @@ if config_env() == :prod do
9494
audio_image_bucket:
9595
System.get_env("ORCASITE_AUDIO_IMAGE_BUCKET", "audio-deriv-orcasound-net"),
9696
audio_image_bucket_region: System.get_env("ORCASITE_AUDIO_IMAGE_BUCKET_REGION", "us-west-2")
97-
9897
end

server/lib/orcasite/accounts/api_key.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ defmodule Orcasite.Accounts.ApiKey do
2626
attribute :expires_at, :utc_datetime_usec
2727
end
2828

29-
code_interface do
30-
define :create
31-
end
32-
3329
calculations do
3430
calculate :invalid,
3531
:boolean,
@@ -41,6 +37,10 @@ defmodule Orcasite.Accounts.ApiKey do
4137
calculate :valid, :boolean, expr(not invalid)
4238
end
4339

40+
code_interface do
41+
define :create
42+
end
43+
4444
relationships do
4545
belongs_to :user, Orcasite.Accounts.User
4646
end

server/lib/orcasite/notifications/event.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ defmodule Orcasite.Notifications.Event do
44
def humanize(:confirmed_candidate, true), do: "confirmed candidates"
55
def humanize(:new_detection, false), do: "new detection"
66
def humanize(:new_detection, true), do: "new detections"
7+
def humanize(:live_bout, false), do: "live bout"
8+
def humanize(:live_bout, true), do: "live bouts"
79
end
File renamed without changes.

server/lib/orcasite/notifications/resources/notification.ex renamed to server/lib/orcasite/notifications/notification.ex

Lines changed: 42 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ defmodule Orcasite.Notifications.Notification do
6565
end
6666

6767
policies do
68-
bypass actor_attribute_equals(:admin, true) do
69-
authorize_if always()
68+
bypass always() do
69+
authorize_if actor_attribute_equals(:admin, true)
70+
authorize_if AshOban.Checks.AshObanInteraction
7071
end
7172

7273
bypass actor_attribute_equals(:moderator, true) do
@@ -89,6 +90,43 @@ defmodule Orcasite.Notifications.Notification do
8990
define :increment_notified_count
9091
end
9192

93+
changes do
94+
change fn changeset, _context ->
95+
changeset
96+
|> Ash.Changeset.after_action(fn _, %{id: notification_id} = notification ->
97+
Task.Supervisor.async_nolink(Orcasite.TaskSupervisor, fn ->
98+
target_count =
99+
Orcasite.Notifications.Subscription
100+
|> Ash.Query.for_read(:available_for_notification, %{
101+
notification_id: notification_id,
102+
event_type: notification.event_type
103+
})
104+
|> Ash.stream!()
105+
|> Stream.map(fn subscription ->
106+
Orcasite.Notifications.NotificationInstance
107+
|> Ash.Changeset.for_create(:create_with_relationships, %{
108+
notification: notification,
109+
subscription: subscription
110+
})
111+
|> Ash.create!()
112+
end)
113+
|> Enum.reduce(0, fn _, sum -> sum + 1 end)
114+
115+
notification
116+
|> Ash.Changeset.for_update(:update, %{target_count: target_count})
117+
|> Ash.update(authorize?: false)
118+
end)
119+
120+
{:ok, notification}
121+
end)
122+
end,
123+
on: :create
124+
end
125+
126+
preparations do
127+
prepare build(load: [:progress, :finished])
128+
end
129+
92130
actions do
93131
defaults [:create, :read, :destroy]
94132

@@ -175,39 +213,6 @@ defmodule Orcasite.Notifications.Notification do
175213
change atomic_update(:notified_count_updated_at, expr(now()))
176214
end
177215

178-
create :notify_bout do
179-
description "Create a notification for a live bout"
180-
argument :bout_id, :string, allow_nil?: false
181-
182-
argument :message, :string do
183-
description """
184-
What primary message subscribers will get (e.g. 'Southern Resident Killer Whales calls
185-
and clicks can be heard at Orcasound Lab!')
186-
"""
187-
188-
allow_nil? false
189-
end
190-
191-
change set_attribute(:event_type, :confirmed_candidate)
192-
193-
change before_action(fn changeset, _context ->
194-
bout_id =
195-
Ash.Changeset.get_argument(changeset, :bout_id)
196-
197-
bout =
198-
Orcasite.Radio.Bout
199-
|> Ash.get(bout_id)
200-
|> Ash.load!(:feed)
201-
202-
changeset
203-
|> Ash.Changeset.force_change_attribute(:meta, %{
204-
bout_id: bout_id,
205-
node: bout.feed.slug,
206-
message: Ash.Changeset.get_argument(changeset, :message)
207-
})
208-
end)
209-
end
210-
211216
create :notify_confirmed_candidate do
212217
description "Create a notification for confirmed candidate (i.e. detection group)"
213218
argument :candidate_id, :string, allow_nil?: false
@@ -280,6 +285,8 @@ defmodule Orcasite.Notifications.Notification do
280285
argument :feed, :map, allow_nil?: false
281286
argument :candidate, :map, allow_nil?: false
282287

288+
validate __MODULE__.Validations.RecentDetection
289+
283290
change set_attribute(:event_type, :new_detection)
284291

285292
change fn changeset, _context ->
@@ -305,43 +312,6 @@ defmodule Orcasite.Notifications.Notification do
305312
end
306313
end
307314

308-
changes do
309-
change fn changeset, _context ->
310-
changeset
311-
|> Ash.Changeset.after_action(fn _, %{id: notification_id} = notification ->
312-
Task.Supervisor.async_nolink(Orcasite.TaskSupervisor, fn ->
313-
target_count =
314-
Orcasite.Notifications.Subscription
315-
|> Ash.Query.for_read(:available_for_notification, %{
316-
notification_id: notification_id,
317-
event_type: notification.event_type
318-
})
319-
|> Ash.stream!()
320-
|> Stream.map(fn subscription ->
321-
Orcasite.Notifications.NotificationInstance
322-
|> Ash.Changeset.for_create(:create_with_relationships, %{
323-
notification: notification,
324-
subscription: subscription
325-
})
326-
|> Ash.create!()
327-
end)
328-
|> Enum.reduce(0, fn _, sum -> sum + 1 end)
329-
330-
notification
331-
|> Ash.Changeset.for_update(:update, %{target_count: target_count})
332-
|> Ash.update(authorize?: false)
333-
end)
334-
335-
{:ok, notification}
336-
end)
337-
end,
338-
on: :create
339-
end
340-
341-
preparations do
342-
prepare build(load: [:progress, :finished])
343-
end
344-
345315
admin do
346316
table_columns [:id, :meta, :event_type, :inserted_at]
347317

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
defmodule Orcasite.Notifications.Notification.Validations.RecentDetection do
2+
use Ash.Resource.Validation
3+
4+
@impl Ash.Resource.Validation
5+
def validate(change, _opts, _context) do
6+
change
7+
|> Ash.Changeset.get_argument(:detection)
8+
|> Map.get(:timestamp)
9+
|> DateTime.compare(
10+
DateTime.utc_now()
11+
|> DateTime.add(-1, :hour)
12+
)
13+
|> case do
14+
:gt -> :ok
15+
:eq -> :ok
16+
_ -> {:error, "detection must be within the last hour"}
17+
end
18+
end
19+
end

server/lib/orcasite/notifications/resources/notification_instance.ex renamed to server/lib/orcasite/notifications/notification_instance.ex

File renamed without changes.

server/lib/orcasite/notifications/resources/subscriber.ex renamed to server/lib/orcasite/notifications/subscriber.ex

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@ defmodule Orcasite.Notifications.Subscriber do
7979
end
8080
end
8181

82+
validations do
83+
validate fn changeset, _context ->
84+
# Check if email subscriber already exists
85+
with email when is_binary(email) <- changeset |> Ash.Changeset.get_argument(:email),
86+
%{action_type: :create} <- changeset,
87+
{:get, {:error, _}} <- {:get, Orcasite.Notifications.Subscriber.by_email(email)} do
88+
:ok
89+
else
90+
{:get, other} ->
91+
{:error, [field: :email, message: "email already exists"]}
92+
93+
err ->
94+
:ok
95+
end
96+
end
97+
end
98+
8299
actions do
83100
defaults [:create, :read, :update, :destroy]
84101

@@ -155,23 +172,6 @@ defmodule Orcasite.Notifications.Subscriber do
155172
end
156173
end
157174

158-
validations do
159-
validate fn changeset, _context ->
160-
# Check if email subscriber already exists
161-
with email when is_binary(email) <- changeset |> Ash.Changeset.get_argument(:email),
162-
%{action_type: :create} <- changeset,
163-
{:get, {:error, _}} <- {:get, Orcasite.Notifications.Subscriber.by_email(email)} do
164-
:ok
165-
else
166-
{:get, other} ->
167-
{:error, [field: :email, message: "email already exists"]}
168-
169-
err ->
170-
:ok
171-
end
172-
end
173-
end
174-
175175
admin do
176176
table_columns [:id, :name, :meta, :inserted_at]
177177
read_actions [:read, :by_email]

server/lib/orcasite/notifications/resources/subscription.ex renamed to server/lib/orcasite/notifications/subscription.ex

File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)