Skip to content

Commit

Permalink
Add device count and estimated device count for Deployment views (#1517)
Browse files Browse the repository at this point in the history
This allows seeing how big a deployment is in device count in listings
and detail view.

Beyond that the Edit form for Deployments will estimate the matching
devices based on the conditions set.
  • Loading branch information
lawik authored Sep 25, 2024
1 parent 90a6573 commit 4f01a66
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 7 deletions.
18 changes: 16 additions & 2 deletions assets/css/_layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,21 @@ html {
}
}

.x5-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-column-gap: 2rem;
grid-row-gap: 2rem;

@media (max-width: 860px) {
grid-template-columns: 1fr 1fr;
}

@media (max-width: 600px) {
grid-template-columns: 1fr;
}
}

.x2-grid {
display: grid;
grid-template-columns: 1fr 1fr;
Expand Down Expand Up @@ -411,5 +426,4 @@ html {
.gr-2 {
grid-row: 2;
}
}

}
38 changes: 38 additions & 0 deletions lib/nerves_hub/deployments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ defmodule NervesHub.Deployments do
|> Repo.all()
end

@spec get_deployment_device_counts_by_product(integer()) :: %{integer() => integer()}
def get_deployment_device_counts_by_product(product_id) do
Device
|> select([d], {d.deployment_id, count(d.id)})
|> where([d], d.product_id == ^product_id)
|> group_by([d], d.id)
|> Repo.all()
|> Map.new()
end

@spec get_deployment_device_count(integer()) :: %{integer() => integer()}
def get_deployment_device_count(deployment_id) do
Device
|> select([d], count(d.id))
|> where([d], d.deployment_id == ^deployment_id)
|> Repo.one()
end

@spec get_deployments_by_firmware(integer()) :: [Deployment.t()]
def get_deployments_by_firmware(firmware_id) do
from(d in Deployment, where: d.firmware_id == ^firmware_id)
Expand Down Expand Up @@ -328,6 +346,11 @@ defmodule NervesHub.Deployments do
:ok
end

@spec change_deployment(Deployment.t(), map()) :: Changeset.t()
def change_deployment(deployment, params) do
Deployment.changeset(deployment, params)
end

@spec create_deployment(map) :: {:ok, Deployment.t()} | {:error, Changeset.t()}
def create_deployment(params) do
changeset = Deployment.creation_changeset(%Deployment{}, params)
Expand Down Expand Up @@ -405,6 +428,21 @@ defmodule NervesHub.Deployments do
)
end

@doc """
Find all potential devices for a deployment
Based on the product, firmware platform, firmware architecture, and device tags
"""
def estimate_devices_matched_by_conditions(product_id, platform, conditions) do
Device
|> where([dev], dev.product_id == ^product_id)
|> where([dev], fragment("d0.firmware_metadata ->> 'platform'") == ^platform)
|> where([dev], fragment("?::jsonb->'tags' <@ to_jsonb(?::text[])", ^conditions, dev.tags))
|> Repo.all()
|> Enum.filter(&version_match?(&1, %{conditions: conditions}))
|> Enum.count()
end

@doc """
Check that a device version matches for a deployment's conditions
Expand Down
35 changes: 34 additions & 1 deletion lib/nerves_hub_web/live/deployments/edit.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,56 @@ defmodule NervesHubWeb.Live.Deployments.Edit do
%{"name" => name} = params
%{product: product} = socket.assigns

deployment = Deployments.get_by_product_and_name!(product, name)
deployment =
Deployments.get_by_product_and_name!(product, name) |> NervesHub.Repo.preload(:firmware)

current_device_count = Deployments.get_deployment_device_count(deployment.id)

archives = Archives.all_by_product(deployment.product)
firmwares = Firmwares.get_firmwares_for_deployment(deployment)

changeset = Deployment.changeset(deployment, %{}) |> tags_to_string()

estimate_count =
Deployments.estimate_devices_matched_by_conditions(
deployment.product_id,
deployment.firmware.platform,
deployment.conditions
)

socket
|> assign(:archives, archives)
|> assign(:deployment, deployment)
|> assign(:current_device_count, current_device_count)
|> assign(:estimate_count, estimate_count)
|> assign(:firmware, deployment.firmware)
|> assign(:firmwares, firmwares)
|> assign(:form, to_form(changeset))
|> ok()
end

@impl Phoenix.LiveView
def handle_event("recalculate", %{"deployment" => params}, socket) do
params = inject_conditions_map(params)

try do
count =
Deployments.estimate_devices_matched_by_conditions(
socket.assigns.deployment.product_id,
socket.assigns.deployment.firmware.platform,
params["conditions"]
)

changeset = Deployments.change_deployment(socket.assigns.deployment, params)

{:noreply, assign(socket, estimate_count: count, form: to_form(tags_to_string(changeset)))}
rescue
_ ->
# Ignore version parsing errors
{:noreply, socket}
end
end

def handle_event("update-deployment", %{"deployment" => params}, socket) do
%{org_user: org_user, org: org, product: product, user: user, deployment: deployment} =
socket.assigns
Expand Down
13 changes: 9 additions & 4 deletions lib/nerves_hub_web/live/deployments/edit.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<h1>Edit Deployment</h1>

<h5 class="mb-2">Firmware version details</h5>
<div class="x4-grid">
<div class="x5-grid">
<div>
<div class="help-text mb-1">Product</div>
<p><%= @product.name %></p>
Expand All @@ -26,11 +26,15 @@
<div class="help-text mb-1">Architecture</div>
<p><%= @firmware.architecture %></p>
</div>
<div>
<div class="help-text mb-1">Device count</div>
<p><%= @current_device_count %></p>
</div>
</div>

<div class="divider"></div>

<.form :let={f} for={@form} phx-submit="update-deployment">
<.form :let={f} for={@form} phx-change="recalculate" phx-submit="update-deployment">
<%= hidden_input(f, :firmware_id, value: @firmware.id) %>

<div class="form-group">
Expand Down Expand Up @@ -64,6 +68,7 @@
<h3 class="mb-2">Conditions</h3>

<p>Changing any conditions will reset any attached devices.</p>
<p>Estimated affected devices: <%= @estimate_count %></p>

<div class="form-group">
<div class="help-text tooltip-label help-tooltip">
Expand All @@ -78,7 +83,7 @@
id: "tags_input",
value:
@form.source
|> Ecto.Changeset.get_change(:conditions, %{})
|> Ecto.Changeset.get_field(:conditions, %{})
|> Map.get("tags", "")
) %>
<div class="has-error"><%= error_tag(f, :tags) %></div>
Expand All @@ -97,7 +102,7 @@
id: "version_requirement",
value:
@form.source
|> Ecto.Changeset.get_change(:conditions, %{})
|> Ecto.Changeset.get_field(:conditions, %{})
|> Map.get("version", "")
) %>
<div class="has-error"><%= error_tag(f, :version) %></div>
Expand Down
2 changes: 2 additions & 0 deletions lib/nerves_hub_web/live/deployments/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule NervesHubWeb.Live.Deployments.Index do
@impl Phoenix.LiveView
def mount(_params, _session, %{assigns: %{product: product}} = socket) do
deployments = Deployments.get_deployments_by_product(product.id)
counts = Deployments.get_deployment_device_counts_by_product(product.id)

deployments =
deployments
Expand All @@ -19,6 +20,7 @@ defmodule NervesHubWeb.Live.Deployments.Index do
socket
|> page_title("Deployments - #{product.name}")
|> assign(:deployments, deployments)
|> assign(:counts, counts)
|> ok()
end

Expand Down
7 changes: 7 additions & 0 deletions lib/nerves_hub_web/live/deployments/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<tr>
<th>Name</th>
<th>State</th>
<th>Devices</th>
<th>Firmware version</th>
<th>Distributed to</th>
<th>Version requirement</th>
Expand All @@ -44,6 +45,12 @@
<%= if deployment.is_active, do: "On", else: "Off" %>
</div>
</td>
<td>
<div class="mobile-label help-text">Devices</div>
<div>
<%= @counts[deployment.id] %>
</div>
</td>
<td>
<div class="mobile-label help-text">Firmware version</div>
<div>
Expand Down
2 changes: 2 additions & 0 deletions lib/nerves_hub_web/live/deployments/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ defmodule NervesHubWeb.Live.Deployments.Show do
|> Map.put(:anchor, "latest-activity")

inflight_updates = Devices.inflight_updates_for(deployment)
current_device_count = Deployments.get_deployment_device_count(deployment.id)

socket
|> page_title("Deployment - #{deployment.name} - #{product.name}")
|> assign(:deployment, deployment)
|> assign(:audit_logs, logs)
|> assign(:inflight_updates, inflight_updates)
|> assign(:firmware, deployment.firmware)
|> assign(:current_device_count, current_device_count)
|> schedule_inflight_updates_updater()
|> ok()
end
Expand Down
4 changes: 4 additions & 0 deletions lib/nerves_hub_web/live/deployments/show.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
<div class="help-text mb-1">Version requirement</div>
<p><%= version(@deployment) %></p>
</div>
<div>
<div class="help-text mb-1">Current device count</div>
<p><%= @current_device_count %></p>
</div>
<div class="row">
<div class="col-lg-6 mb-1">
<div class="help-text mb-1 tooltip-label">
Expand Down

0 comments on commit 4f01a66

Please sign in to comment.