Split ScrobbleActivity into focused modules
Move track CRUD/listing to ListeningStats and diagnostics to Maintenance, leaving ScrobbleActivity for scrobbling only. Closes #111
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
defmodule MusicLibrary.Maintenance do
|
||||
@moduledoc """
|
||||
Context for database maintenance operations and background job monitoring.
|
||||
Context for database maintenance operations, background job monitoring,
|
||||
and scrobble data quality diagnostics.
|
||||
"""
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias LastFm.Track
|
||||
alias MusicLibrary.BackgroundRepo
|
||||
alias MusicLibrary.Repo
|
||||
|
||||
@@ -39,4 +41,96 @@ defmodule MusicLibrary.Maintenance do
|
||||
def optimize do
|
||||
Repo.optimize()
|
||||
end
|
||||
|
||||
# Scrobble data quality diagnostics
|
||||
|
||||
@spec count_tracks_missing_artist_musicbrainz_id() :: non_neg_integer()
|
||||
def count_tracks_missing_artist_musicbrainz_id do
|
||||
query =
|
||||
from t in Track,
|
||||
where:
|
||||
fragment("json_extract(?, '$.musicbrainz_id') IS NULL", t.artist) or
|
||||
fragment("json_extract(?, '$.musicbrainz_id') = ''", t.artist),
|
||||
select: count(t.scrobbled_at_uts)
|
||||
|
||||
Repo.one(query) || 0
|
||||
end
|
||||
|
||||
@spec count_tracks_missing_album_musicbrainz_id() :: non_neg_integer()
|
||||
def count_tracks_missing_album_musicbrainz_id do
|
||||
query =
|
||||
from t in Track,
|
||||
where:
|
||||
fragment("json_extract(?, '$.musicbrainz_id') IS NULL", t.album) or
|
||||
fragment("json_extract(?, '$.musicbrainz_id') = ''", t.album),
|
||||
select: count(t.scrobbled_at_uts)
|
||||
|
||||
Repo.one(query) || 0
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets artists with missing MusicBrainz IDs, grouped by artist name.
|
||||
|
||||
Returns a list of maps with artist name and track count.
|
||||
"""
|
||||
@spec get_artists_missing_musicbrainz_id(keyword()) :: [map()]
|
||||
def get_artists_missing_musicbrainz_id(opts \\ []) do
|
||||
limit = Keyword.get(opts, :limit)
|
||||
|
||||
query =
|
||||
from t in Track,
|
||||
where:
|
||||
fragment("json_extract(?, '$.musicbrainz_id') IS NULL", t.artist) or
|
||||
fragment("json_extract(?, '$.musicbrainz_id') = ''", t.artist),
|
||||
select: %{
|
||||
artist_name: fragment("json_extract(?, '$.name')", t.artist),
|
||||
track_count: count(t.scrobbled_at_uts)
|
||||
},
|
||||
group_by: fragment("json_extract(?, '$.name')", t.artist),
|
||||
order_by: [desc: count(t.scrobbled_at_uts)]
|
||||
|
||||
query =
|
||||
if limit do
|
||||
from q in query, limit: ^limit
|
||||
else
|
||||
query
|
||||
end
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets albums with missing MusicBrainz IDs, grouped by album title and artist.
|
||||
|
||||
Returns a list of maps with album title, artist name, and track count.
|
||||
"""
|
||||
@spec get_albums_missing_musicbrainz_id(keyword()) :: [map()]
|
||||
def get_albums_missing_musicbrainz_id(opts \\ []) do
|
||||
limit = Keyword.get(opts, :limit)
|
||||
|
||||
query =
|
||||
from t in Track,
|
||||
where:
|
||||
fragment("json_extract(?, '$.musicbrainz_id') IS NULL", t.album) or
|
||||
fragment("json_extract(?, '$.musicbrainz_id') = ''", t.album),
|
||||
select: %{
|
||||
album_title: fragment("json_extract(?, '$.title')", t.album),
|
||||
artist_name: fragment("json_extract(?, '$.name')", t.artist),
|
||||
track_count: count(t.scrobbled_at_uts)
|
||||
},
|
||||
group_by: [
|
||||
fragment("json_extract(?, '$.title')", t.album),
|
||||
fragment("json_extract(?, '$.name')", t.artist)
|
||||
],
|
||||
order_by: [desc: count(t.scrobbled_at_uts)]
|
||||
|
||||
query =
|
||||
if limit do
|
||||
from q in query, limit: ^limit
|
||||
else
|
||||
query
|
||||
end
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user