From c89d03f9cfeb6593f0060847b50a1da6d6397405 Mon Sep 17 00:00:00 2001 From: Max Rumpf Date: Sun, 1 Dec 2024 03:02:53 +0100 Subject: [PATCH 1/9] Add back `inserted_at` field to export --- listenbrainz/background/export.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/listenbrainz/background/export.py b/listenbrainz/background/export.py index 9e04923e74..b4b1e07be2 100644 --- a/listenbrainz/background/export.py +++ b/listenbrainz/background/export.py @@ -80,6 +80,8 @@ def export_listens_for_time_range(ts_conn, file_path, user_id: int, start_time: SELECT jsonb_build_object( 'listened_at' , extract(epoch from listened_at) + , 'inserted_at' + , extract(epoch from inserted_at) , 'track_metadata' , jsonb_set(data, '{recording_msid}'::text[], to_jsonb(recording_msid::text)) )::text as line From 35f272ef9d69c07ba685c8814afce9ae7468cd18 Mon Sep 17 00:00:00 2001 From: Monkey Do Date: Mon, 13 Jan 2025 19:06:18 +0100 Subject: [PATCH 2/9] Test inserted_at in exports --- listenbrainz/tests/integration/test_export.py | 1 + 1 file changed, 1 insertion(+) diff --git a/listenbrainz/tests/integration/test_export.py b/listenbrainz/tests/integration/test_export.py index a1f026f4ca..a73dbd53e8 100644 --- a/listenbrainz/tests/integration/test_export.py +++ b/listenbrainz/tests/integration/test_export.py @@ -235,6 +235,7 @@ def test_export(self): self.assertEqual(expected["track_metadata"]["artist_name"], received["track_metadata"]["artist_name"]) self.assertEqual(expected["track_metadata"]["release_name"], received["track_metadata"]["release_name"]) self.assertEqual(expected["listened_at"], received["listened_at"]) + self.assertEqual(expected["inserted_at"], received["inserted_at"]) if received["track_metadata"]["track_name"] == "Sister": self.assertEqual({ "caa_id": self.recording["release_data"]["caa_id"], From 70c1a37db56e6e897466f4338e6132c2bbf1e97f Mon Sep 17 00:00:00 2001 From: Aerozol Date: Tue, 14 Jan 2025 14:57:53 +1300 Subject: [PATCH 3/9] Update data-update-intervals.rst Added "Link listens" to the update table --- docs/general/data-update-intervals.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/general/data-update-intervals.rst b/docs/general/data-update-intervals.rst index bb9006df99..94b2f836e3 100644 --- a/docs/general/data-update-intervals.rst +++ b/docs/general/data-update-intervals.rst @@ -13,6 +13,7 @@ Removing deleted listens from stats On the 2nd and 16th of each month Full dumps 1st and 15th of each month Incremental dumps Daily Weekly playlists Monday morning, based on the users timezone setting +Link listens Monday morning, based on the users timezone setting Daily playlists [#f3]_ Every morning, based on the users timezone setting =============================================== ========================================= From 6c8210ec6378812555ae17c0f7bf6afc7c9640a5 Mon Sep 17 00:00:00 2001 From: Aerozol Date: Tue, 14 Jan 2025 14:58:15 +1300 Subject: [PATCH 4/9] Update data-update-intervals.rst fix table order --- docs/general/data-update-intervals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/general/data-update-intervals.rst b/docs/general/data-update-intervals.rst index 94b2f836e3..10b4a2565e 100644 --- a/docs/general/data-update-intervals.rst +++ b/docs/general/data-update-intervals.rst @@ -12,8 +12,8 @@ Updating statistics for new listens Daily [#f2]_ Removing deleted listens from stats On the 2nd and 16th of each month Full dumps 1st and 15th of each month Incremental dumps Daily -Weekly playlists Monday morning, based on the users timezone setting Link listens Monday morning, based on the users timezone setting +Weekly playlists Monday morning, based on the users timezone setting Daily playlists [#f3]_ Every morning, based on the users timezone setting =============================================== ========================================= From 2f967e92823dce10238edb0f35e08c638a18cecf Mon Sep 17 00:00:00 2001 From: Aerozol Date: Tue, 14 Jan 2025 16:03:51 +1300 Subject: [PATCH 5/9] Update LinkListens.tsx Tightened and improved the text, see ticket LB-1724 for more details. --- .../src/settings/link-listens/LinkListens.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/frontend/js/src/settings/link-listens/LinkListens.tsx b/frontend/js/src/settings/link-listens/LinkListens.tsx index c3ccded310..6a8034e4bb 100644 --- a/frontend/js/src/settings/link-listens/LinkListens.tsx +++ b/frontend/js/src/settings/link-listens/LinkListens.tsx @@ -249,8 +249,7 @@ export default function LinkListensPage() { first.

- You will find below your top 1000 listens (grouped by album) that - have  + Your top 1,000 listens (grouped by album) that have  not been automatically linked -   to a MusicBrainz recording. Link them below or  - - submit new data to MusicBrainz - - . +  to a MusicBrainz recording.

MusicBrainz is the open-source - music encyclopedia that ListenBrainz uses to display more information - about your music. + music encyclopedia that ListenBrainz uses to display information about + your music.  + + Submit missing data to MusicBrainz + + .

{!isNil(lastUpdated) && ( -

Last updated {new Date(lastUpdated).toLocaleDateString()}

+

Updates Mondays, based on listen data. Last updated {new Date(lastUpdated).toLocaleDateString()}

)}
From 63c894a20a7f8a992abfc15c9ee1ea942676441c Mon Sep 17 00:00:00 2001 From: Monkey Do Date: Tue, 14 Jan 2025 12:36:32 +0100 Subject: [PATCH 6/9] Select inserted_at for export --- listenbrainz/background/export.py | 1 + 1 file changed, 1 insertion(+) diff --git a/listenbrainz/background/export.py b/listenbrainz/background/export.py index 533a4520f0..770b222b06 100644 --- a/listenbrainz/background/export.py +++ b/listenbrainz/background/export.py @@ -79,6 +79,7 @@ def export_listens_for_time_range(ts_conn, file_path, user_id: int, start_time: query = """ WITH selected_listens AS ( SELECT l.listened_at + , l.inserted_at , l.data , l.recording_msid , COALESCE((data->'additional_info'->>'recording_mbid')::uuid, user_mm.recording_mbid, mm.recording_mbid, other_mm.recording_mbid) AS recording_mbid From f277d18f1a871046c2ba73a677e51061c39467b1 Mon Sep 17 00:00:00 2001 From: Monkey Do Date: Tue, 14 Jan 2025 16:19:11 +0100 Subject: [PATCH 7/9] Fix inserted_at column access and tests The column is named 'created' in the sql table, not 'inserted_at'. Modified the test to expect inserted_at in the exported data, but not in the test data submitted --- listenbrainz/background/export.py | 3 ++- listenbrainz/tests/integration/test_export.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/listenbrainz/background/export.py b/listenbrainz/background/export.py index 770b222b06..02a79443e0 100644 --- a/listenbrainz/background/export.py +++ b/listenbrainz/background/export.py @@ -79,7 +79,7 @@ def export_listens_for_time_range(ts_conn, file_path, user_id: int, start_time: query = """ WITH selected_listens AS ( SELECT l.listened_at - , l.inserted_at + , l.created as inserted_at , l.data , l.recording_msid , COALESCE((data->'additional_info'->>'recording_mbid')::uuid, user_mm.recording_mbid, mm.recording_mbid, other_mm.recording_mbid) AS recording_mbid @@ -143,6 +143,7 @@ def export_listens_for_time_range(ts_conn, file_path, user_id: int, start_time: LEFT JOIN LATERAL jsonb_array_elements(artist_data->'artists') WITH ORDINALITY artists(artist, position) ON TRUE GROUP BY sl.listened_at + , sl.inserted_at , sl.recording_msid , sl.data , mbc.recording_mbid diff --git a/listenbrainz/tests/integration/test_export.py b/listenbrainz/tests/integration/test_export.py index a73dbd53e8..e3e8265881 100644 --- a/listenbrainz/tests/integration/test_export.py +++ b/listenbrainz/tests/integration/test_export.py @@ -235,7 +235,10 @@ def test_export(self): self.assertEqual(expected["track_metadata"]["artist_name"], received["track_metadata"]["artist_name"]) self.assertEqual(expected["track_metadata"]["release_name"], received["track_metadata"]["release_name"]) self.assertEqual(expected["listened_at"], received["listened_at"]) - self.assertEqual(expected["inserted_at"], received["inserted_at"]) + # The test data used in send_listens cannot have an inserted_at prop as that is not a valid listen format + self.assertNotIn("inserted_at",expected) + # However inserted_at should be part of the exported listen data + self.assertIn("inserted_at",received) if received["track_metadata"]["track_name"] == "Sister": self.assertEqual({ "caa_id": self.recording["release_data"]["caa_id"], From c0ce031b60e4dc51b30911d425d6f660c8a45dd4 Mon Sep 17 00:00:00 2001 From: Monkey Do Date: Tue, 14 Jan 2025 16:25:52 +0100 Subject: [PATCH 8/9] Linting --- listenbrainz/tests/integration/test_export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/listenbrainz/tests/integration/test_export.py b/listenbrainz/tests/integration/test_export.py index e3e8265881..cb62e54102 100644 --- a/listenbrainz/tests/integration/test_export.py +++ b/listenbrainz/tests/integration/test_export.py @@ -236,9 +236,9 @@ def test_export(self): self.assertEqual(expected["track_metadata"]["release_name"], received["track_metadata"]["release_name"]) self.assertEqual(expected["listened_at"], received["listened_at"]) # The test data used in send_listens cannot have an inserted_at prop as that is not a valid listen format - self.assertNotIn("inserted_at",expected) + self.assertNotIn("inserted_at", expected) # However inserted_at should be part of the exported listen data - self.assertIn("inserted_at",received) + self.assertIn("inserted_at", received) if received["track_metadata"]["track_name"] == "Sister": self.assertEqual({ "caa_id": self.recording["release_data"]["caa_id"], From d8f11e27d5296ba3dafcb92d434cb47f6c043afd Mon Sep 17 00:00:00 2001 From: Monkey Do Date: Tue, 14 Jan 2025 17:34:37 +0100 Subject: [PATCH 9/9] Tweak link listens docs Modified docs to say Monday 2AM instead of monday morning user-time + format datetime of last run a bit better in LinkListens page --- docs/general/data-update-intervals.rst | 12 ++++++------ .../js/src/settings/link-listens/LinkListens.tsx | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/general/data-update-intervals.rst b/docs/general/data-update-intervals.rst index 10b4a2565e..91b90bab10 100644 --- a/docs/general/data-update-intervals.rst +++ b/docs/general/data-update-intervals.rst @@ -7,14 +7,14 @@ Expected schedule: System Update schedule =============================================== ========================================= Receiving listens, updating listen counts Immediate [#f1]_ -Deleting listens Removed at the top of the next hour (UTC) +Deleting listens Removed at the top of the next hour (UTC) Updating statistics for new listens Daily [#f2]_ Removing deleted listens from stats On the 2nd and 16th of each month -Full dumps 1st and 15th of each month -Incremental dumps Daily -Link listens Monday morning, based on the users timezone setting -Weekly playlists Monday morning, based on the users timezone setting -Daily playlists [#f3]_ Every morning, based on the users timezone setting +Full dumps 1st and 15th of each month +Incremental dumps Daily +Link listens Monday morning at 2AM (UTC) +Weekly playlists Monday morning, based on the user's timezone setting +Daily playlists [#f3]_ Every morning, based on the user's timezone setting =============================================== ========================================= Situations will occasionally arise where these take longer. If you have been a very patient user, and diff --git a/frontend/js/src/settings/link-listens/LinkListens.tsx b/frontend/js/src/settings/link-listens/LinkListens.tsx index 6a8034e4bb..de5e2b0341 100644 --- a/frontend/js/src/settings/link-listens/LinkListens.tsx +++ b/frontend/js/src/settings/link-listens/LinkListens.tsx @@ -13,7 +13,7 @@ import { Helmet } from "react-helmet"; import NiceModal from "@ebay/nice-modal-react"; -import { groupBy, isNil, isNull, pick, size, sortBy } from "lodash"; +import { groupBy, isNil, isNull, isString, pick, size, sortBy } from "lodash"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useQuery } from "@tanstack/react-query"; import ReactTooltip from "react-tooltip"; @@ -86,6 +86,15 @@ export default function LinkListensPage() { const [searchParams, setSearchParams] = useSearchParams(); const pageSearchParam = searchParams.get("page"); + const lastUpdatedHumanReadable = isString(lastUpdated) + ? new Date(lastUpdated).toLocaleString(undefined, { + day: "2-digit", + month: "short", + hour: "numeric", + minute: "numeric", + hour12: true, + }) + : "—"; // State const [deletedListens, setDeletedListens] = React.useState>([]); const [unlinkedListens, setUnlinkedListens] = React.useState< @@ -269,7 +278,10 @@ export default function LinkListensPage() { .

{!isNil(lastUpdated) && ( -

Updates Mondays, based on listen data. Last updated {new Date(lastUpdated).toLocaleDateString()}

+

+ Updates every Monday at 2AM (UTC). Last updated{" "} + {lastUpdatedHumanReadable} +

)}