diff --git a/lib/music_library/collection.ex b/lib/music_library/collection.ex
index 1de5d634..239e8be4 100644
--- a/lib/music_library/collection.ex
+++ b/lib/music_library/collection.ex
@@ -161,6 +161,23 @@ defmodule MusicLibrary.Collection do
Repo.all(q)
end
+ @spec count_records_by_release_year(keyword()) :: [{String.t(), non_neg_integer()}]
+ def count_records_by_release_year(opts \\ []) do
+ limit = Keyword.get(opts, :limit, @pagination[:stats_limit])
+
+ q =
+ from r in Record,
+ where: not is_nil(r.purchased_at),
+ where: not is_nil(r.release_date),
+ where: r.release_date != "",
+ group_by: fragment("substr(?, 1, 4)", r.release_date),
+ order_by: [desc: fragment("count(1)")],
+ select: {fragment("substr(?, 1, 4)", r.release_date), fragment("count(1)")},
+ limit: ^limit
+
+ Repo.all(q)
+ end
+
@spec collected_releases_query() :: Ecto.Query.t()
def collected_releases_query do
from rr in RecordRelease,
diff --git a/lib/music_library_web/live/stats_live/index.ex b/lib/music_library_web/live/stats_live/index.ex
index 930a8557..95319441 100644
--- a/lib/music_library_web/live/stats_live/index.ex
+++ b/lib/music_library_web/live/stats_live/index.ex
@@ -292,7 +292,7 @@ defmodule MusicLibraryWeb.StatsLive.Index do
-
+
{gettext("Top %{n} Collection Artists", %{n: length(@records_by_artist)})}
@@ -336,6 +336,23 @@ defmodule MusicLibraryWeb.StatsLive.Index do
/>
+
+
+
+ {gettext("Top 20 Release Years")}
+
+
+ <.vertical_bar_chart
+ data={@records_by_release_year}
+ width={600}
+ height={26 * length(@records_by_release_year)}
+ color_class="fill-red-500"
+ label_fn={fn {year, _count} -> year end}
+ value_fn={fn {_year, count} -> count end}
+ class="w-full"
+ />
+
+
"""
@@ -347,6 +364,7 @@ defmodule MusicLibraryWeb.StatsLive.Index do
latest_record = Collection.get_latest_record()
records_by_artists = Collection.count_records_by_artist(limit: 20)
records_by_genre = Collection.count_records_by_genre(limit: 20)
+ records_by_release_year = Collection.count_records_by_release_year(limit: 20)
records_on_this_day =
current_date
@@ -375,6 +393,7 @@ defmodule MusicLibraryWeb.StatsLive.Index do
current_section: :stats,
records_by_artist: records_by_artists,
records_by_genre: records_by_genre,
+ records_by_release_year: records_by_release_year,
records_on_this_day: records_on_this_day
)}
end
diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot
index ad45939b..89a72f9c 100644
--- a/priv/gettext/default.pot
+++ b/priv/gettext/default.pot
@@ -2336,3 +2336,8 @@ msgstr ""
#, elixir-autogen, elixir-format
msgid "Track ID"
msgstr ""
+
+#: lib/music_library_web/live/stats_live/index.ex
+#, elixir-autogen, elixir-format
+msgid "Top 20 Release Years"
+msgstr ""
diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po
index 0b8df7e4..6c960e2f 100644
--- a/priv/gettext/en/LC_MESSAGES/default.po
+++ b/priv/gettext/en/LC_MESSAGES/default.po
@@ -2336,3 +2336,8 @@ msgstr ""
#, elixir-autogen, elixir-format, fuzzy
msgid "Track ID"
msgstr ""
+
+#: lib/music_library_web/live/stats_live/index.ex
+#, elixir-autogen, elixir-format
+msgid "Top 20 Release Years"
+msgstr ""
diff --git a/test/music_library/collection_test.exs b/test/music_library/collection_test.exs
index 0fac8526..43d6441b 100644
--- a/test/music_library/collection_test.exs
+++ b/test/music_library/collection_test.exs
@@ -156,6 +156,79 @@ defmodule MusicLibrary.CollectionTest do
end
end
+ describe "count_records_by_release_year/1" do
+ test "returns counts grouped by release year, ordered by count descending" do
+ record_with_artist("Artist A", %{
+ release_date: "1994-09-13",
+ purchased_at: ~U[2024-12-27 16:50:57Z]
+ })
+
+ record_with_artist("Artist B", %{
+ release_date: "1994-03-01",
+ purchased_at: ~U[2024-12-28 16:50:57Z]
+ })
+
+ record_with_artist("Artist C", %{
+ release_date: "2020",
+ purchased_at: ~U[2024-12-29 16:50:57Z]
+ })
+
+ assert [{"1994", 2}, {"2020", 1}] = Collection.count_records_by_release_year()
+ end
+
+ test "excludes wishlisted records" do
+ record_with_artist("Artist A", %{
+ release_date: "2020",
+ purchased_at: ~U[2024-12-27 16:50:57Z]
+ })
+
+ record_with_artist("Artist B", %{
+ release_date: "2020",
+ purchased_at: nil
+ })
+
+ assert [{"2020", 1}] = Collection.count_records_by_release_year()
+ end
+
+ test "excludes records without a release date" do
+ record_with_artist("Artist A", %{
+ release_date: "2020",
+ purchased_at: ~U[2024-12-27 16:50:57Z]
+ })
+
+ record_with_artist("Artist B", %{
+ release_date: nil,
+ purchased_at: ~U[2024-12-28 16:50:57Z]
+ })
+
+ record_with_artist("Artist C", %{
+ release_date: "",
+ purchased_at: ~U[2024-12-29 16:50:57Z]
+ })
+
+ assert [{"2020", 1}] = Collection.count_records_by_release_year()
+ end
+
+ test "respects limit" do
+ record_with_artist("Artist A", %{
+ release_date: "1994",
+ purchased_at: ~U[2024-12-27 16:50:57Z]
+ })
+
+ record_with_artist("Artist B", %{
+ release_date: "1994",
+ purchased_at: ~U[2024-12-28 16:50:57Z]
+ })
+
+ record_with_artist("Artist C", %{
+ release_date: "2020",
+ purchased_at: ~U[2024-12-29 16:50:57Z]
+ })
+
+ assert [{"1994", 2}] = Collection.count_records_by_release_year(limit: 1)
+ end
+ end
+
describe "collected_artist_ids/0" do
setup [:fill_collection]