Skip to content

Commit 2f0782f

Browse files
lawikjoshk
andauthored
Add devices online/offline to org and product views (#2402)
This is primarily to help people doing development or with few devices but a couple of orgs set up. It helps for figuring out where your active devices actually are. I've had this issue a hilarious number of times. The loading is deferred so it won't stop someone from navigating if it becomes slow. There are only two queries made so it shouldn't be particularly impactful in performance and the information isn't live-updated. It might also indicate when something is really wrong or weird I suppose. But mostly it helps indicate which orgs and products are actually in use. Especially since the development seed produces a bunch of them. --------- Co-authored-by: Josh Kalderimis <[email protected]>
1 parent 279ab4a commit 2f0782f

File tree

5 files changed

+132
-12
lines changed

5 files changed

+132
-12
lines changed

lib/nerves_hub/devices/connections.ex

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,76 @@ defmodule NervesHub.Devices.Connections do
2020
|> Repo.all()
2121
end
2222

23+
@doc """
24+
Get device connection information for Orgs.
25+
"""
26+
@spec get_connection_status_by_orgs(org_ids :: [non_neg_integer()]) :: %{
27+
non_neg_integer() => %{online: non_neg_integer(), offline: non_neg_integer()}
28+
}
29+
def get_connection_status_by_orgs(org_ids) when is_list(org_ids) do
30+
q =
31+
DeviceConnection
32+
|> join(:inner, [d], p in assoc(d, :product))
33+
|> select([d, p], [p.org_id, count(d.id)])
34+
|> where([_, p], p.org_id in ^org_ids)
35+
|> group_by([_, p], p.org_id)
36+
37+
online =
38+
q
39+
|> where([d], d.status == :connected)
40+
|> Repo.all()
41+
42+
offline =
43+
q
44+
|> where([d], d.status != :connected)
45+
|> Repo.all()
46+
47+
for org_id <- org_ids, into: %{} do
48+
{org_id, %{online: 0, offline: 0}}
49+
end
50+
|> to_connection_status(online, :online)
51+
|> to_connection_status(offline, :offline)
52+
end
53+
54+
@doc """
55+
Get device connection information for Products.
56+
"""
57+
@spec get_connection_status_by_products(product_ids :: [non_neg_integer()]) :: %{
58+
non_neg_integer() => %{online: non_neg_integer(), offline: non_neg_integer()}
59+
}
60+
def get_connection_status_by_products(product_ids) when is_list(product_ids) do
61+
q =
62+
DeviceConnection
63+
|> join(:inner, [d], p in assoc(d, :product))
64+
|> select([d, p], [p.id, count(d.id)])
65+
|> where([_, p], p.id in ^product_ids)
66+
|> group_by([_, p], p.id)
67+
68+
online =
69+
q
70+
|> where([d], d.status == :connected)
71+
|> Repo.all()
72+
73+
offline =
74+
q
75+
|> where([d], d.status != :connected)
76+
|> Repo.all()
77+
78+
for product_id <- product_ids, into: %{} do
79+
{product_id, %{online: 0, offline: 0}}
80+
end
81+
|> to_connection_status(online, :online)
82+
|> to_connection_status(offline, :offline)
83+
end
84+
85+
defp to_connection_status(start, counts, status) do
86+
counts
87+
|> Enum.reduce(start, fn [id, count], acc ->
88+
current = Map.get(acc, id, %{})
89+
Map.put(acc, id, Map.put(current, status, count))
90+
end)
91+
end
92+
2393
@doc """
2494
Get latest inserted connection for a device.
2595
"""

lib/nerves_hub_web/live/org/show.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
defmodule NervesHubWeb.Live.Org.Show do
22
use NervesHubWeb, :updated_live_view
33

4+
alias NervesHub.Devices.Connections
45
alias NervesHub.Products
56

67
@impl Phoenix.LiveView
@@ -11,10 +12,21 @@ defmodule NervesHubWeb.Live.Org.Show do
1112
socket
1213
|> page_title("Products - #{socket.assigns.org.name}")
1314
|> assign(:products, products)
15+
|> assign(:device_info, %{})
16+
17+
if connected?(socket), do: send(self(), :load_extras)
1418

1519
{:ok, socket}
1620
end
1721

22+
@impl Phoenix.LiveView
23+
def handle_info(:load_extras, socket) do
24+
statuses =
25+
Connections.get_connection_status_by_products(Enum.map(socket.assigns.products, & &1.id))
26+
27+
{:noreply, assign(socket, :device_info, statuses)}
28+
end
29+
1830
def fade_in(selector) do
1931
JS.show(
2032
to: selector,

lib/nerves_hub_web/live/org/show.html.heex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@
2222
<%= for product <- @products do %>
2323
<.link navigate={~p"/org/#{@org}/#{product}/devices"} class="grid-item">
2424
<h3>{product.name}</h3>
25+
<div class="flex-row">
26+
<div class="flex-grow-1">
27+
<div class="help-text mt-2">Devices</div>
28+
<div class="flex flex-row mt-1 align-items-center justify-content-between">
29+
<div>
30+
<p>{get_in(@device_info, [product.id, :online]) || "-"} online</p>
31+
</div>
32+
<div>
33+
<p>{get_in(@device_info, [product.id, :offline]) || "-"} offline</p>
34+
</div>
35+
</div>
36+
</div>
37+
</div>
2538
</.link>
2639
<% end %>
2740
</div>

lib/nerves_hub_web/live/orgs/index.ex

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule NervesHubWeb.Live.Orgs.Index do
22
use NervesHubWeb, :updated_live_view
33

44
alias NervesHub.Devices
5+
alias NervesHub.Devices.Connections
56
alias NervesHub.Tracker
67
# alias NervesHubWeb.Components.PinnedDevices
78

@@ -10,6 +11,7 @@ defmodule NervesHubWeb.Live.Orgs.Index do
1011

1112
@pinned_devices_limit 5
1213

14+
@impl Phoenix.LiveView
1315
def mount(_params, _session, %{assigns: %{user: user}} = socket) do
1416
pinned_devices = Devices.get_pinned_devices(user.id)
1517

@@ -18,22 +20,35 @@ defmodule NervesHubWeb.Live.Orgs.Index do
1820
{device.identifier, Tracker.connection_status(device)}
1921
end)
2022

21-
socket
22-
|> assign(:page_title, "Organizations")
23-
|> assign(:show_all_pinned?, false)
24-
|> assign(:pinned_devices, Devices.get_pinned_devices(user.id))
25-
|> assign(:device_statuses, statuses)
26-
|> assign(:device_limit, @pinned_devices_limit)
27-
|> subscribe()
28-
|> ok()
23+
socket =
24+
socket
25+
|> assign(:page_title, "Organizations")
26+
|> assign(:show_all_pinned?, false)
27+
|> assign(:device_info, %{})
28+
|> assign(:pinned_devices, Devices.get_pinned_devices(user.id))
29+
|> assign(:device_statuses, statuses)
30+
|> assign(:device_limit, @pinned_devices_limit)
31+
|> subscribe()
32+
33+
if connected?(socket), do: send(self(), :load_extras)
34+
{:ok, socket}
2935
end
3036

37+
@impl Phoenix.LiveView
3138
def handle_event("toggle-expand-devices", _, %{assigns: %{show_all_pinned?: show_all?}} = socket) do
3239
socket
3340
|> assign(:show_all_pinned?, !show_all?)
3441
|> noreply()
3542
end
3643

44+
@impl true
45+
def handle_info(:load_extras, socket) do
46+
statuses =
47+
Connections.get_connection_status_by_orgs(Enum.map(socket.assigns.user.orgs, & &1.id))
48+
49+
{:noreply, assign(socket, :device_info, statuses)}
50+
end
51+
3752
def handle_info(%Broadcast{event: "connection:status", payload: payload}, socket) do
3853
update_device_statuses(socket, payload)
3954
end

lib/nerves_hub_web/live/orgs/index.html.heex

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,21 @@
1616
<.link navigate={~p"/org/#{org}"} class="grid-item">
1717
<h3>{org.name}</h3>
1818
<div class="flex-row">
19-
<div>
20-
<div class="help-text">Total Products</div>
21-
<div class="flex-row mt-1 align-items-center">
19+
<div class="flex-grow-1 mt-1">
20+
<div class="help-text">Products</div>
21+
<div class="mt-1">
2222
<img src="/images/icons/product.svg" alt="products" class="mr-1" style="margin-top: -1px" />
23-
<p>{Enum.count(org.products)}</p>
23+
<span>{Enum.count(org.products)}</span>
24+
</div>
25+
26+
<div class="help-text mt-2">Devices</div>
27+
<div class="flex flex-row mt-1 align-items-center justify-content-between">
28+
<div>
29+
<p>{get_in(@device_info, [org.id, :online]) || "-"} online</p>
30+
</div>
31+
<div>
32+
<p>{get_in(@device_info, [org.id, :offline]) || "-"} offline</p>
33+
</div>
2434
</div>
2535
</div>
2636
</div>

0 commit comments

Comments
 (0)