diff --git a/PROJECT.org b/PROJECT.org index fb2b971..04fad1b 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [1/24] :vrobbler:project:personal: +* Backlog [2/25] :vrobbler:project:personal: ** TODO [#C] After transition to linux add curl_cffi as webpage scrapper again :webpages:metadata: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles: :PROPERTIES: @@ -605,6 +605,10 @@ independent of the email flow it was originally creatdd for ** TODO [#B] Is there way to create unique slugs for media instances :media_types: +** DONE [#B] Allow people all trends or individual trends :trends:profiles: +:PROPERTIES: +:ID: 1d081152-abd1-73c2-a625-903565a10c6c +:END: ** DONE [#A] Fix a bug in board game scorelog data :boardgames:logdata: :PROPERTIES: :ID: 014bab30-13bf-fae7-e678-4666a8d38ae4 diff --git a/vrobbler/apps/profiles/forms.py b/vrobbler/apps/profiles/forms.py index 35e10d1..966a747 100644 --- a/vrobbler/apps/profiles/forms.py +++ b/vrobbler/apps/profiles/forms.py @@ -42,6 +42,7 @@ class UserProfileForm(forms.ModelForm): "home_scrobble_limit", "live_now_playing", "weigh_in_units", + "trends_disabled", ] widgets = { "lastfm_password": forms.PasswordInput(render_value=True), diff --git a/vrobbler/apps/profiles/migrations/0040_userprofile_trends_disabled.py b/vrobbler/apps/profiles/migrations/0040_userprofile_trends_disabled.py new file mode 100644 index 0000000..af30c34 --- /dev/null +++ b/vrobbler/apps/profiles/migrations/0040_userprofile_trends_disabled.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.29 on 2026-06-25 20:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("profiles", "0039_userprofile_live_now_playing"), + ] + + operations = [ + migrations.AddField( + model_name="userprofile", + name="disabled_trends", + field=models.JSONField( + blank=True, + default=list, + help_text="List of trend slugs the user has disabled", + ), + ), + migrations.AddField( + model_name="userprofile", + name="trends_disabled", + field=models.BooleanField(default=False), + ), + ] diff --git a/vrobbler/apps/profiles/models.py b/vrobbler/apps/profiles/models.py index 4dc467b..0c982b3 100644 --- a/vrobbler/apps/profiles/models.py +++ b/vrobbler/apps/profiles/models.py @@ -106,6 +106,14 @@ class UserProfile(TimeStampedModel): default=WeighUnit.METRIC, ) + trends_disabled = models.BooleanField(default=False) + + disabled_trends = models.JSONField( + default=list, + blank=True, + help_text="List of trend slugs the user has disabled", + ) + def __str__(self): return f"User profile for {self.user}" diff --git a/vrobbler/apps/trends/management/commands/compute_trends.py b/vrobbler/apps/trends/management/commands/compute_trends.py index 76e2027..30088a7 100644 --- a/vrobbler/apps/trends/management/commands/compute_trends.py +++ b/vrobbler/apps/trends/management/commands/compute_trends.py @@ -38,15 +38,36 @@ class Command(BaseCommand): overall_start = timezone.now() ok_count = 0 fail_count = 0 + skipped_count = 0 for user in users: - total_trends = len(TREND_REGISTRY) - self.stdout.write(f" {user} ({user.id}): {total_trends} trends...") + try: + profile = user.profile + if profile.trends_disabled: + self.stdout.write( + self.style.WARNING( + f" {user} ({user.id}): trends disabled globally, skipping" + ) + ) + skipped_count += len(TREND_REGISTRY) + continue + disabled_trends = set(profile.disabled_trends or []) + except Exception: + disabled_trends = set() + + active_slugs = [ + s for s in TREND_REGISTRY if s not in disabled_trends + ] + total_trends = len(active_slugs) + self.stdout.write( + f" {user} ({user.id}): {total_trends} trends (" + f"{len(disabled_trends & set(TREND_REGISTRY.keys()))} disabled)..." + ) user_start = timezone.now() user_ok = 0 user_fail = 0 - for idx, (slug, _) in enumerate(TREND_REGISTRY.items(), start=1): + for idx, slug in enumerate(active_slugs, start=1): periods = get_supported_periods(slug) self.stdout.write(f" [{idx}/{total_trends}] {slug}...\n") for period in periods: @@ -76,7 +97,7 @@ class Command(BaseCommand): overall_elapsed = (timezone.now() - overall_start).total_seconds() self.stdout.write( self.style.SUCCESS( - f"Done! {ok_count} OK, {fail_count} failed " + f"Done! {ok_count} OK, {fail_count} failed, {skipped_count} skipped " f"({overall_elapsed:.1f}s across {total_users} user(s))" ) ) diff --git a/vrobbler/apps/trends/tasks.py b/vrobbler/apps/trends/tasks.py index d7e3fb7..260250d 100644 --- a/vrobbler/apps/trends/tasks.py +++ b/vrobbler/apps/trends/tasks.py @@ -26,7 +26,17 @@ def compute_user_trends(user_id): logger.warning("User %s not found, skipping trends", user_id) return - total = len(TREND_REGISTRY) + try: + profile = user.profile + if profile.trends_disabled: + logger.info("User %s (%d) has trends disabled, skipping", user, user_id) + return + disabled = set(profile.disabled_trends or []) + except Exception: + disabled = set() + + active_slugs = [s for s in TREND_REGISTRY if s not in disabled] + total = len(active_slugs) logger.info( "Computing %d trends for user %s (%d)", total, @@ -34,7 +44,7 @@ def compute_user_trends(user_id): user_id, ) - for idx, (slug, _) in enumerate(TREND_REGISTRY.items(), start=1): + for idx, slug in enumerate(active_slugs, start=1): compute_single_trend.delay(user_id, slug) logger.info("Dispatched all %d trends for user %s (%d)", total, user, user_id) @@ -52,6 +62,21 @@ def compute_single_trend(user_id, slug): logger.warning("Unknown trend slug '%s' for user %d", slug, user_id) return + try: + profile = user.profile + if profile.trends_disabled: + logger.info( + "User %d has trends disabled, skipping '%s'", user_id, slug + ) + return + if slug in (profile.disabled_trends or []): + logger.info( + "User %d has trend '%s' disabled, skipping", user_id, slug + ) + return + except Exception: + pass + periods = get_supported_periods(slug) for period in periods: diff --git a/vrobbler/apps/trends/templates/trends/trend_detail.html b/vrobbler/apps/trends/templates/trends/trend_detail.html index 3884243..20b028d 100644 --- a/vrobbler/apps/trends/templates/trends/trend_detail.html +++ b/vrobbler/apps/trends/templates/trends/trend_detail.html @@ -3,6 +3,10 @@ {% block title %}{{ trend.title }}{% endblock %} {% block lists %} +{% if trend_not_found %} +
{{ trend.description }}
- {% if trend.computed_at %} - Last computed: {{ trend.computed_at|date:"M j, Y H:i" }} - {% else %} - Pending - {% endif %} ++ {{ field }} + {{ field.label_tag }} +
{% elif field.name == "home_scrobble_limit" %}{{ field.label_tag }} diff --git a/vrobbler/templates/scrobbles/scrobble_list.html b/vrobbler/templates/scrobbles/scrobble_list.html index 80fe0f2..a7ed0fe 100644 --- a/vrobbler/templates/scrobbles/scrobble_list.html +++ b/vrobbler/templates/scrobbles/scrobble_list.html @@ -76,9 +76,11 @@
+ {% if not user.profile.trends_disabled %} + {% endif %}