Skip to content

Commit

Permalink
Notifications - Add details to new candidate notification (#164)
Browse files Browse the repository at this point in the history
* Add extra details to new detection notifications

* Add listener count to new detection notification, add a table

* Add number since last in subject

* Add extra notifications in subject

* Fix rendering message for confirmed candidate
  • Loading branch information
skanderm committed Aug 11, 2023
1 parent 1175bb3 commit 83e541d
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 77 deletions.
87 changes: 34 additions & 53 deletions server/lib/orcasite/notifications/email.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ defmodule Orcasite.Notifications.Email do
new()
|> to({name, email})
|> from({"Orcasound", "info@orcasound.net"})
|> subject("New detection on #{node_name}")
|> subject(
"New detection at #{node_name}#{if params.notifications_since_count > 0, do: " [+#{params.notifications_since_count}]", else: ""}"
)
|> html_body(mjml_new_detection_body(params |> Map.put(:node_name, node_name)))
end

Expand All @@ -36,6 +38,36 @@ defmodule Orcasite.Notifications.Email do
A new detection has been submitted at {{ node_name }} ({{ node }})!
</mj-text>
<mj-text font-size="20px" font-family="helvetica">
Description: {{#if meta["description"]}}{{ meta["description"] }}{{else}}(no description){{/if}}
</mj-text>
{{#if meta["listener_count"]}}
<mj-text font-size="20px" font-family="helvetica">
Listeners: {{ meta["listener_count"] }}
</mj-text>
{{/if}}
{{#if notifications_since_count > 0}}
<mj-text font-size="20px" font-family="helvetica">
There have been {{ notifications_since_count }} other detections since the last notification.
</mj-text>
<mj-table>
<tr style="border-bottom:1px solid #ecedee;text-align:left;padding:15px 0;">
<th style="padding: 0 5px 0 0;">Feed</th>
<th style="padding: 0 15px; text-align: right;">#</th>
<th style="padding: 0 0 0 15px;">Description</th>
</tr>
{{#each notifications_since as |notif_meta|}}
<tr>
<td style="padding: 0 5px 0 0;white-space:nowrap;">{{ notif_meta["node"] }}</td>
<td style="padding: 0 15px;text-align:right;">{{ notif_meta["listener_count"] }}</td>
<td style="padding: 0 0 0 15px;">{{ notif_meta["description"] }}</td>
</tr>
{{/each}}
</mj-table>
{{/if}}
<mj-text font-size="20px">
Listen here: <a href="https://live.orcasound.net/{{node}}">https://live.orcasound.net/{{ node }}</a>
</mj-text>
Expand Down Expand Up @@ -74,7 +106,7 @@ defmodule Orcasite.Notifications.Email do
<mj-section>
<mj-column background-color="#404040" padding="18px">
<mj-text font-size="14px" color="#F2F2F2" font-family="Helvetica" line-height="150%">
{{ message }}
{{ meta["message"] }}
</mj-text>
</mj-column>
</mj-section>
Expand Down Expand Up @@ -130,31 +162,6 @@ defmodule Orcasite.Notifications.Email do
|> compile_mjml(assigns)
end

def new_detection_body(assigns) do
~H"""
<div>
<p>
A new detection has been submitted at <%= @node_name %> (<%= @node %>)!
</p>
<p>
Listen here:
<a href={"https://live.orcasound.net/#{@node}"}>https://live.orcasound.net/<%= @node %></a>
</p>
<%= if @unsubscribe_token do %>
<p>
If you no longer wish to receive these emails, you can unsubscribe <a href={
url(~p"/auth/subscription/unsubscribe?token=#{@unsubscribe_token}")
}>here</a>.
</p>
<% end %>
</div>
"""
|> Phoenix.HTML.Safe.to_iodata()
|> List.to_string()
end

def confirmed_candidate_email(
%{
to: email,
Expand All @@ -172,36 +179,10 @@ defmodule Orcasite.Notifications.Email do
mjml_confirmed_candidate_body(
params
|> Map.put(:node_name, node_name)
|> Map.put_new(:message, "Orcas can be heard near #{node_name}!")
)
)
end

def confirmed_candidate_body(assigns) do
~H"""
<div>
<p>
Don't miss the concert!
</p>
<p>
Listen to orcas here:
<a href={"https://live.orcasound.net/#{@node}"}>https://live.orcasound.net/<%= @node %></a>
</p>
<%= if @unsubscribe_token do %>
<p>
If you no longer wish to receive these emails, you can unsubscribe <a href={
url(~p"/auth/subscription/unsubscribe?token=#{@unsubscribe_token}")
}>here</a>.
</p>
<% end %>
</div>
"""
|> Phoenix.HTML.Safe.to_iodata()
|> List.to_string()
end

def compile_mjml(mjml, assigns) do
mjml
|> Zappa.compile!()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule Orcasite.Notifications.ManualReadNotificationsSince do
use Ash.Resource.ManualRead

alias Orcasite.Notifications
alias Orcasite.Notifications.Notification

require Ash.Query

def read(query, _ecto_query, _opts, _context) do
# Get notifications of the same type after the passed-in notification_id
notification_id =
query
|> Ash.Query.get_argument(:notification_id)

notification =
Notification
|> Notifications.get!(notification_id)

Notification
|> Ash.Query.filter(
event_type == ^notification.event_type and
inserted_at > ^notification.inserted_at
)
|> Notifications.read()
end
end
15 changes: 13 additions & 2 deletions server/lib/orcasite/notifications/resources/notification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Orcasite.Notifications.Notification do

code_interface do
define_for Orcasite.Notifications
define :notify_new_detection, action: :notify_new_detection, args: [:detection_id, :node]
define :notify_new_detection, action: :notify_new_detection, args: [:detection_id, :node, :description, :listener_count]

define :notify_confirmed_candidate,
action: :notify_confirmed_candidate,
Expand All @@ -26,6 +26,13 @@ defmodule Orcasite.Notifications.Notification do
actions do
defaults [:create, :read, :update, :destroy]

read :since_notification do
description "Get all notifications after a given notification ID."
argument :notification_id, :uuid

manual Orcasite.Notifications.ManualReadNotificationsSince
end

create :notify_confirmed_candidate do
description "Create a notification for confirmed candidate (i.e. detection group)"
accept [:candidate_id]
Expand Down Expand Up @@ -56,6 +63,8 @@ defmodule Orcasite.Notifications.Notification do
accept [:detection_id]
argument :detection_id, :integer
argument :node, :string, allow_nil?: false
argument :description, :string, allow_nil?: true
argument :listener_count, :integer, allow_nil?: true

change set_attribute(:event_type, :new_detection)

Expand All @@ -64,7 +73,9 @@ defmodule Orcasite.Notifications.Notification do
changeset
|> Ash.Changeset.change_attribute(:meta, %{
detection_id: Ash.Changeset.get_argument(changeset, :detection_id),
node: Ash.Changeset.get_argument(changeset, :node)
node: Ash.Changeset.get_argument(changeset, :node),
description: Ash.Changeset.get_argument(changeset, :description),
listener_count: Ash.Changeset.get_argument(changeset, :listener_count)
})
end
end
Expand Down
14 changes: 7 additions & 7 deletions server/lib/orcasite/notifications/resources/subscription.ex
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,15 @@ defmodule Orcasite.Notifications.Subscription do
argument :minutes_ago, :integer, default: 5

filter expr(
active == true and
active and
event_type == ^arg(:event_type) and
(is_nil(last_notification_id) or last_notification_id != ^arg(:notification_id)) and
fragment(
"? is null or ? < timezone('UTC', now()) - ?::numeric * interval '1 minute'",
last_notified_at,
last_notified_at,
^arg(:minutes_ago)
)
(is_nil(last_notified_at) or
fragment(
"? < timezone('UTC', now()) - ?::numeric * interval '1 minute'",
last_notified_at,
^arg(:minutes_ago)
))
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Orcasite.Notifications.Workers.SendNotificationEmail do
use Oban.Worker, queue: :email, unique: [keys: [:notification_id, :subscription_id]]

alias Orcasite.Notifications
alias Orcasite.Notifications.{Subscription, NotificationInstance}
alias Orcasite.Notifications.{Subscription, NotificationInstance, Notification}

@impl Oban.Worker
def perform(%Oban.Job{
Expand All @@ -15,20 +15,37 @@ defmodule Orcasite.Notifications.Workers.SendNotificationEmail do

params =
notif_instance.meta
|> Map.merge(notif_instance.subscription.meta)
|> Map.merge(notif_instance.notification.meta)
|> stringify_map()
|> Map.merge(stringify_map(notif_instance.subscription.meta))
|> Map.merge(stringify_map(notif_instance.notification.meta))

unsubscribe_token =
Orcasite.Notifications.Subscription.unsubscribe_token(notif_instance.subscription)

notifications_since =
notif_instance.subscription.last_notification_id
|> case do
nil ->
[]

notif_id ->
Notification
|> Ash.Query.for_read(:since_notification, %{notification_id: notif_id})
|> Orcasite.Notifications.read!()
end
|> Enum.filter(& &1.id != notif_instance.notification_id)

:ok = Orcasite.RateLimiter.continue?(:ses, 1)
%{

%{meta: params}
|> Map.merge(%{
to: params["email"],
name: params["subscriber_name"],
node: params["node"] || "",
unsubscribe_token: unsubscribe_token
}
unsubscribe_token: unsubscribe_token,
notifications_since: notifications_since |> Enum.map(& &1.meta),
notifications_since_count: Enum.count(notifications_since)
})
|> email_for_notif(stringify(params["event_type"]))
|> Orcasite.Mailer.deliver()

Expand Down
8 changes: 7 additions & 1 deletion server/lib/orcasite_web/graphql/resolvers/detection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ defmodule OrcasiteWeb.Resolvers.Detection do
# Send notification for new detection
Task.Supervisor.async_nolink(Orcasite.TaskSupervisor, fn ->
%{slug: node} = Orcasite.Radio.get!(Orcasite.Radio.Feed, feed_id)
Orcasite.Notifications.Notification.notify_new_detection(detection.id, node)

Orcasite.Notifications.Notification.notify_new_detection(
detection.id,
node,
detection.description,
detection.listener_count
)
end)

{:ok,
Expand Down
4 changes: 1 addition & 3 deletions server/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,9 @@ defmodule Orcasite.Mixfile do
{:oban, "~> 2.14"},
{:gen_smtp, "~> 1.0"},
{:ash_authentication, "~> 3.11.6"},
{:ash_authentication_phoenix,
github: "skanderm/ash_authentication_phoenix", branch: "pass-reset-banner-overrides"},
{:ash_authentication_phoenix, "~> 1.7.3"},
{:syn, "~> 3.3"},
{:mjml, "~> 1.5.0"},
{:mjml_eex, "~> 0.9.0"},
{:zappa, github: "skanderm/zappa", branch: "master"}
]
end
Expand Down
Loading

0 comments on commit 83e541d

Please sign in to comment.