Browse Source

feat: add silly listen chart

pull/37/head
Inhji Y. 1 year ago
parent
commit
c73a7de2fd
  1. 39
      apps/listens/lib/listens/report.ex
  2. 53
      apps/listens/lib/listens/sparkline.ex
  3. 1
      apps/listens/mix.exs
  4. 16151
      apps/tomie_web/assets/static/js/chart.js
  5. 2418
      apps/tomie_web/assets/static/js/chartkick.js
  6. 15
      apps/tomie_web/lib/tomie_web/live/listen_live/index.ex
  7. 6
      apps/tomie_web/lib/tomie_web/templates/listen/index.html.leex
  8. 2
      config/config.exs
  9. 3
      mix.lock

39
apps/listens/lib/listens/report.ex

@ -21,6 +21,14 @@ defmodule Listens.Report do
)
end
def sparkline() do
oldest = get_oldest_listen()
newest = get_newest_listen()
per_month = listens_per_month()
Listens.Sparkline.create(per_month, oldest, newest)
end
def count_listens_query(model) when model in [Album, Artist] do
from a in model,
left_join: l in assoc(a, :listens),
@ -29,7 +37,36 @@ defmodule Listens.Report do
select: %{id: a.id, listens: count(a.id)}
end
def offset([{unit, value}]) when unit in [:days, :weeks, :months] do
def offset([{unit, value}]) when unit in [:days, :weeks, :months, :years] do
Timex.shift(DateTime.utc_now(), [{unit, value}])
end
def listens_per_month() do
Db.Repo.all(
from l in listens_per_month_query(),
where: l.listened_at > ^offset(years: -1)
)
end
defp listens_per_month_query do
Listen
|> select([l], [count(l.id), fragment("date_trunc('month', ?)", l.listened_at)])
|> order_by([l], fragment("date_trunc('month', ?)", l.listened_at))
|> group_by([l], fragment("date_trunc('month', ?)", l.listened_at))
end
def get_oldest_listen() do
Listen
|> order_by(asc: :listened_at)
|> where([l], l.listened_at > ^offset(years: -1))
|> limit(1)
|> Db.Repo.one!()
end
def get_newest_listen() do
Listen
|> order_by(desc: :listened_at)
|> limit(1)
|> Db.Repo.one!()
end
end

53
apps/listens/lib/listens/sparkline.ex

@ -0,0 +1,53 @@
defmodule Listens.Sparkline do
def create(listens_per_month, oldest_listen, newest_listen) do
[_, date_first] = List.first(listens_per_month)
[_, date_last] = List.last(listens_per_month)
beginning_interval = get_duration(oldest_listen.listened_at, date_first)
end_interval = get_duration(date_last, newest_listen.listened_at)
fill_list(beginning_interval) ++
fill_gaps(listens_per_month) ++
fill_list(end_interval)
end
defp fill_list(0), do: []
defp fill_list(count), do: Enum.reduce(1..count, [], fn _, acc -> acc ++ [0] end)
defp get_duration(start_date, end_date) do
if Timex.after?(start_date, end_date) do
0
else
Timex.Interval.new(from: start_date, until: end_date, step: [months: 1])
|> Timex.Interval.duration(:months)
end
end
def stupid_diff(from, until) do
from = Timex.to_date(from)
until = Timex.to_date(until)
until.month - from.month + year_diff(from, until) * 12
end
def year_diff(from, until), do: until.year - from.year
def fill_gaps(listens) do
new_list =
for {[count, listen], index} <- Enum.with_index(listens) do
case Enum.at(listens, index + 1) do
nil ->
count
[_, next_listen] ->
duration =
(stupid_diff(listen, next_listen) - 1)
|> fill_list()
[count] ++ duration
end
end
List.flatten(new_list)
end
end

1
apps/listens/mix.exs

@ -26,6 +26,7 @@ defmodule Listens.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:chartkick, "~> 0.4.0"},
{:cachex, "~> 3.2"},
{:waffle, "~> 1.0.1"},
{:waffle_ecto, "~> 0.0.8"},

16151
apps/tomie_web/assets/static/js/chart.js
File diff suppressed because it is too large
View File

2418
apps/tomie_web/assets/static/js/chartkick.js
File diff suppressed because it is too large
View File

15
apps/tomie_web/lib/tomie_web/live/listen_live/index.ex

@ -5,16 +5,25 @@ defmodule TomieWeb.ListenLive.Index do
def mount(_args, _session, socket) do
Phoenix.PubSub.subscribe(TomieWeb.PubSub, "Listens.Workers:ALL")
{:ok, socket |> assign(listens: fetch())}
{:ok, socket |> assign(fetch())}
end
def handle_info(%{event: :updated}, socket) do
{:noreply, socket |> assign(listens: fetch())}
{:noreply, socket |> assign(fetch())}
end
def handle_params(_params, _url, socket) do
{:noreply, socket}
end
def fetch(), do: Listens.Listens.list_listens()
def fetch() do
[
listens: Listens.Listens.list_listens(),
sparkline:
Listens.Report.sparkline()
|> Enum.with_index(-12)
|> Enum.map(fn {k, v} -> [v, k] end)
|> IO.inspect()
]
end
end

6
apps/tomie_web/lib/tomie_web/templates/listen/index.html.leex

@ -2,6 +2,12 @@
<%= live_component @socket, TomieWeb.ListenLive.SidebarComponent %>
<div class="col-span-4">
<div class="chart" phx-update="ignore">
<script src=" <%= Routes.static_path(TomieWeb.Endpoint, "/js/chartkick.js") %>"></script>
<script src=" <%= Routes.static_path(TomieWeb.Endpoint, "/js/chart.js") %>"></script>
<%= raw Chartkick.line_chart Jason.encode!(@sparkline) %>
</div>
<%= for listen <- @listens do %>
<div class="card">
<div class="flex">

2
config/config.exs

@ -54,6 +54,8 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
config :chartkick, json_serializer: Jason
config :waffle,
storage: Waffle.Storage.Local,
storage_dir_prefix: "uploads"

3
mix.lock

@ -2,6 +2,7 @@
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"cachex": {:hex, :cachex, "3.2.0", "a596476c781b0646e6cb5cd9751af2e2974c3e0d5498a8cab71807618b74fe2f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "aef93694067a43697ae0531727e097754a9e992a1e7946296f5969d6dd9ac986"},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
"chartkick": {:hex, :chartkick, "0.4.0", "d490135346cc726bc0f971ef657cdb6504bc8c8bb01f760ad5dfaa86f6c67f5f", [:mix], [{:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "6c3493a5326066dd0eecd4ff5775916067c70aa67d1dc8f502724dd4d37f1319"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
@ -53,6 +54,7 @@
"plug": {:hex, :plug, "1.10.0", "6508295cbeb4c654860845fb95260737e4a8838d34d115ad76cd487584e2fc4d", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "422a9727e667be1bf5ab1de03be6fa0ad67b775b2d84ed908f3264415ef29d4a"},
"plug_cowboy": {:hex, :plug_cowboy, "2.2.1", "fcf58aa33227a4322a050e4783ee99c63c031a2e7f9a2eb7340d55505e17f30f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3b43de24460d87c0971887286e7a20d40462e48eb7235954681a20cee25ddeb6"},
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
"postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4737ce62a31747b4c63c12b20c62307e51bb4fcd730ca0c32c280991e0606c90"},
"pow": {:hex, :pow, "1.0.20", "b99993811af5233681bfc521e81ca706d25a56f2be54bad6424db327ce840ab9", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.3.0 and < 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "4b6bd271399ccb353abbdbdc316199fe7fd7ae36bbf47059d53e366831c34fc8"},
"que": {:hex, :que, "0.10.1", "788ed0ec92ed69bdf9cfb29bf41a94ca6355b8d44959bd0669cf706e557ac891", [:mix], [{:ex_utils, "~> 0.1.6", [hex: :ex_utils, repo: "hexpm", optional: false]}, {:memento, "~> 0.3.0", [hex: :memento, repo: "hexpm", optional: false]}], "hexpm", "a737b365253e75dbd24b2d51acc1d851049e87baae08cd0c94e2bc5cd65088d5"},
@ -67,6 +69,7 @@
"tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
"uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm", "c790593b4c3b601f5dc2378baae7efaf5b3d73c4c6456ba85759905be792f2ac"},
"waffle": {:hex, :waffle, "1.0.1", "2eb6d14866b07717fc1450ee2e7a4a3583660fc62a7ee1984b8e75c642c09d2a", [:mix], [{:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:ex_aws_s3, "~> 2.0", [hex: :ex_aws_s3, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "36a7303fd6014dfce83c1b732219a51607a7469c4b4d6b47a47e0600dc7c01dc"},
"waffle_ecto": {:hex, :waffle_ecto, "0.0.8", "a89273d09b27cfd534c0d99265cbc8dd14e6f8d74e568a0a353f29b0c79be926", [:mix], [{:ecto, ">= 2.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:waffle, "~> 1.0.0", [hex: :waffle, repo: "hexpm", optional: false]}], "hexpm", "dcc6170b3f75c9c9db1b009dff80bd4157503f1638d20331c1814595515008d5"},
}
Loading…
Cancel
Save