From 4977f4631c76a2eb1bedb0c7d03e09e7198384a6 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Thu, 12 Mar 2026 17:23:22 +0000 Subject: [PATCH] Show grouped records in records on this day email --- .../records_on_this_day_email.ex | 62 ++++++++++++++++++- .../records_on_this_day_email_test.exs | 36 +++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/lib/music_library/records_on_this_day_email.ex b/lib/music_library/records_on_this_day_email.ex index 162c5dde..dbbcb992 100644 --- a/lib/music_library/records_on_this_day_email.ex +++ b/lib/music_library/records_on_this_day_email.ex @@ -14,6 +14,7 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do if records == [] do {:ok, :no_records} else + grouped = Collection.group_records_by_release_group(records) conf = config() from_email = Keyword.fetch!(conf, :from_email) to_email = Keyword.fetch!(conf, :to_email) @@ -26,7 +27,7 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do |> to(to_email) |> from({"MusicLibrary", from_email}) |> subject("[MusicLibrary] #{heading}") - |> html_body(build_html(records, date, conf)) + |> html_body(build_html(grouped, date, conf)) case mailer.deliver(email) do {:ok, _} -> @@ -42,12 +43,15 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do # -- Private -- - defp build_html(records, date, conf) do + defp build_html(grouped, date, conf) do base_url = Keyword.fetch!(conf, :base_url) |> String.trim_trailing("/") heading = date |> Calendar.strftime("Records on %-d %B") |> html_escape() records_html = - Enum.map_join(records, "\n", fn record -> record_html(record, date, base_url) end) + Enum.map_join(grouped, "\n", fn + {:single, record} -> record_html(record, date, base_url) + {:group, group} -> grouped_record_html(group, date, base_url) + end) """
@@ -101,6 +105,58 @@ defmodule MusicLibrary.RecordsOnThisDayEmail do """ end + defp grouped_record_html(%{representative: rep, records: records}, date, base_url) do + years = Record.released_how_long_ago?(rep, date) + cover_url = cover_image_url(rep, base_url) + artist_names = rep |> Record.artist_names() |> html_escape() + title = rep.title |> html_escape() + years_label = years_ago_label(years) + {years_color, years_weight} = anniversary_style(years) + + cover_html = + if cover_url do + ~s() + else + ~s(
) + end + + releases_html = + Enum.map_join(records, "\n", fn record -> + record_url = record_detail_url(record, base_url) + format = format_label(record.format) + type = type_label(record.type) + + purchased_label = + if record.purchased_at do + " · Purchased on #{Record.format_as_date(record.purchased_at)}" + else + "" + end + + """ + +

#{format} · #{type}#{purchased_label}

+
+ """ + end) + + """ +
+
+ #{cover_html} +
+

#{artist_names}

+

#{title}

+

+ #{years_label} +

+
+
+ #{releases_html} +
+ """ + end + defp record_detail_url(record, base_url) do "#{base_url}/collection/#{record.id}" end diff --git a/test/music_library/records_on_this_day_email_test.exs b/test/music_library/records_on_this_day_email_test.exs index a8ee5e8a..a780c2d2 100644 --- a/test/music_library/records_on_this_day_email_test.exs +++ b/test/music_library/records_on_this_day_email_test.exs @@ -52,6 +52,42 @@ defmodule MusicLibrary.RecordsOnThisDayEmailTest do refute_email_sent() end + test "groups records with the same musicbrainz_id" do + shared_mbid = Ecto.UUID.generate() + + cd = + Fixtures.Records.record(%{ + release_date: "2020-03-05", + title: "Grouped Album", + musicbrainz_id: shared_mbid, + format: :cd + }) + + vinyl = + Fixtures.Records.record(%{ + release_date: "2020-03-05", + title: "Grouped Album", + musicbrainz_id: shared_mbid, + format: :vinyl + }) + + date = ~D[2025-03-05] + assert {:ok, :sent} = RecordsOnThisDayEmail.send(date) + + assert_email_sent(fn email -> + html = email.html_body + # Title appears once as group header + assert html =~ "Grouped Album" + assert html =~ "5 years ago" + # Both formats appear as sub-items + assert html =~ "CD" + assert html =~ "Vinyl" + # Both records have detail links + assert html =~ "/collection/#{cd.id}" + assert html =~ "/collection/#{vinyl.id}" + end) + end + test "shows anniversary styling for milestone years" do # 10 year anniversary (gold) Fixtures.Records.record(%{