Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
"test"
],
"words": [
"AEXAMPLE",
"BUCKETPOLICY",
"FABBEXAMPLE",
"Mjml",
"Opps",
"Spex",
"Ueberauth",
"acreateserial",
"anotherdummytoken",
"behaviours",
Expand Down Expand Up @@ -110,6 +117,7 @@
"progressbar",
"psql",
"rebar",
"reconnections",
"registered",
"rgba",
"sendfile",
Expand Down
2 changes: 2 additions & 0 deletions assets/ui-rework/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import TimeAgo from "javascript-time-ago"
import en from "javascript-time-ago/locale/en"

import Chart from "./hooks/chart.js"
import ConnectedDevicesAnalytics from "./hooks/connectedDevicesAnalytics.js"
import Console from "./hooks/console.js"
import DeviceLocationMap from "./hooks/deviceLocationMap.js"
import DeviceLocationMapWithGeocoder from "./hooks/deviceLocationMapWithGeocoder.js"
Expand All @@ -33,6 +34,7 @@ let liveSocket = new LiveSocket("/live", Socket, {
params: { _csrf_token: csrfToken },
hooks: {
Chart,
ConnectedDevicesAnalytics,
Console,
DeviceLocationMap,
DeviceLocationMapWithGeocoder,
Expand Down
131 changes: 131 additions & 0 deletions assets/ui-rework/hooks/connectedDevicesAnalytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import Chart from "chart.js/auto"

export default {
mounted() {
let unit = this.el.dataset.unit

let label_singual = this.el.dataset.labelSingual
let label_plural = this.el.dataset.labelPlural

let dataset = JSON.parse(this.el.dataset.metrics)

const ctx = this.el

var data = []
for (let i = 0; i < dataset.length; i++) {
data.push({
x: dataset[i].timestamp,
y: dataset[i].count
})
}

var gradient = ctx.getContext("2d").createLinearGradient(0, 0, 0, 400)
gradient.addColorStop(0, "rgba(99, 102, 241, 1)")
gradient.addColorStop(1, "rgba(99, 102, 241, 0)")

const barChartDataset = {
type: "bar",
data: {
datasets: [
{
backgroundColor: gradient,
hoverBackgroundColor: "#7f9cf5",
barPercentage: 0.75,
minBarLength: 2,
data: data
}
]
},
options: {
plugins: {
title: {
display: false
},
legend: {
display: false,
labels: {
display: false
}
},
tooltip: {
callbacks: {
title: function(context) {
date = new Date(context[0].parsed.x)
return date.toLocaleTimeString("en-NZ")
},
label: function(context) {
if (context.raw.y === 1) {
unit = label_singual
} else {
unit = label_plural
}
return " " + context.formattedValue + " " + unit
}
}
}
},
scales: {
x: {
display: false,
// grid: {
// display: false,
// drawOnChartArea: false,
// drawTicks: false
// },
type: "time",
time: {
unit: unit,
displayFormats: {
millisecond: "HH:mm:ss.SSS",
second: "HH:mm:ss",
minute: "HH:mm",
hour: "HH:mm"
}
},
ticks: {
display: false
}
},
y: {
offset: false,
grid: {
display: false
},
type: "linear",
min: 0,
// max: max
ticks: {
display: false
}
}
},
responsive: true,
maintainAspectRatio: false,
layout: {
autoPadding: false,
padding: {
top: 0,
right: 10,
bottom: 0,
left: 5
}
}
}
}

const chart = new Chart(ctx, barChartDataset)
this.el.chart = chart

this.handleEvent("update-charts", function(payload) {
if (payload.type == type) {
chart.data.datasets[0].data = payload.data
chart.update()
}
})

this.handleEvent("update-time-unit", function(payload) {
chart.options.scales.x.time.unit = payload.unit
chart.update()
})
}
}
1 change: 1 addition & 0 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ config :nerves_hub,
String.to_integer(System.get_env("DEPLOYMENT_CALCULATOR_INTERVAL_SECONDS", "3600")),
mapbox_access_token: System.get_env("MAPBOX_ACCESS_TOKEN"),
dashboard_enabled: System.get_env("DASHBOARD_ENABLED", "false") == "true",
insights_enabled: System.get_env("INSIGHTS_ENABLED", "false") == "true",
extension_config: [
geo: [
# No interval, fetch geo on device connection by default
Expand Down
20 changes: 20 additions & 0 deletions lib/nerves_hub/devices/filtering.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule NervesHub.Devices.Filtering do
import Ecto.Query

alias NervesHub.Devices.Alarms
alias NervesHub.Devices.Device
alias NervesHub.Devices.DeviceMetric
alias NervesHub.Types.Tag

Expand Down Expand Up @@ -62,6 +63,25 @@ defmodule NervesHub.Devices.Filtering do
)
end

def filter(query, _filters, :filter, "not_recently_seen") do
query
|> where([latest_connection: lc], lc.status == :disconnected)
|> where([latest_connection: lc], lc.disconnected_at < ago(24, "hour"))
end

def filter(query, _filters, :filter, "unstable_connections") do
unstable_connections =
Device
|> select([d], %{id: d.id, count: count()})
|> join(:left, [d], dc in assoc(d, :device_connections))
|> where([_, dc], dc.established_at > ago(24, "hour"))
|> group_by([d], d.id)

join(query, :inner, [d], uc in subquery(unstable_connections),
on: d.id == uc.id and uc.count > 5
)
end

def filter(query, _filters, :firmware_version, value) do
where(query, [d], d.firmware_metadata["version"] == ^value)
end
Expand Down
38 changes: 35 additions & 3 deletions lib/nerves_hub/firmwares.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ defmodule NervesHub.Firmwares do
|> Repo.all()
end

@spec get_installed_firmwares(Product.t(), [String.t()]) :: [Firmware.t()]
def get_installed_firmwares(product, uuids) do
subquery =
Device
|> select([d], %{
firmware_uuid: fragment("? ->> 'uuid'", d.firmware_metadata),
install_count: count(fragment("? ->> 'uuid'", d.firmware_metadata), :distinct)
})
|> where([d], not is_nil(d.firmware_metadata))
|> where([d], not is_nil(fragment("? ->> 'uuid'", d.firmware_metadata)))
|> Repo.exclude_deleted()
|> group_by([d], fragment("? ->> 'uuid'", d.firmware_metadata))

Firmware
|> join(:left, [f], d in subquery(subquery), on: d.firmware_uuid == f.uuid)
|> where([f], f.product_id == ^product.id)
|> where([f], f.uuid in ^uuids)
|> select_merge([_f, d], %{install_count: d.install_count})
|> order_by([_f, d], d.install_count)
|> Repo.all()
end

@spec filter(Product.t(), map()) :: {[Product.t()], Flop.Meta.t()}
def filter(product, opts \\ %{}) do
opts = Map.reject(opts, fn {_key, val} -> is_nil(val) end)
Expand All @@ -94,15 +116,25 @@ defmodule NervesHub.Firmwares do
|> group_by([d], fragment("? ->> 'uuid'", d.firmware_metadata))

Firmware
|> join(:left, [f], d in subquery(subquery), on: d.firmware_uuid == f.uuid)
|> join(:left, [f], d in subquery(subquery),
as: :install_count,
on: d.firmware_uuid == f.uuid
)
|> where([f], f.product_id == ^product.id)
|> filter_selection(opts[:filter])
|> sort_firmware(sort_opts)
|> select_merge([_f, d], %{install_count: d.install_count})
|> select_merge([_f, install_count: ic], %{install_count: ic.install_count})
|> Flop.run(flop)
end

defp filter_selection(query, "active_firmware") do
where(query, [_f, install_count: ic], ic.install_count > 0)
end

defp filter_selection(query, _filter), do: query

defp sort_firmware(query, {direction, :install_count}) do
order_by(query, [_f, d], {^direction, d.install_count})
order_by(query, [_f, install_count: ic], {^direction, ic.install_count})
end

defp sort_firmware(query, sort), do: order_by(query, ^sort)
Expand Down
Loading
Loading