From 0ce19527a21b6e54d50041d7dd8e482da451b8b3 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Sat, 10 Aug 2024 17:41:26 -0400 Subject: [PATCH] [scrobbles] Fix mood starting, and clean up code --- .../apps/moods/migrations/0002_mood_image.py | 18 ++++++++ vrobbler/apps/moods/models.py | 30 ++++++++++++- vrobbler/apps/scrobbles/constants.py | 1 + vrobbler/apps/scrobbles/mixins.py | 43 +++++++++++++++++-- vrobbler/apps/scrobbles/models.py | 2 +- vrobbler/apps/scrobbles/views.py | 25 ++++++----- vrobbler/apps/webpages/models.py | 22 ++++++++++ vrobbler/templates/moods/mood_detail.html | 39 +++++++++++++++++ vrobbler/templates/moods/mood_list.html | 42 ++++++++++++++++++ 9 files changed, 205 insertions(+), 17 deletions(-) create mode 100644 vrobbler/apps/moods/migrations/0002_mood_image.py create mode 100644 vrobbler/templates/moods/mood_detail.html create mode 100644 vrobbler/templates/moods/mood_list.html diff --git a/vrobbler/apps/moods/migrations/0002_mood_image.py b/vrobbler/apps/moods/migrations/0002_mood_image.py new file mode 100644 index 0000000..1f8fae6 --- /dev/null +++ b/vrobbler/apps/moods/migrations/0002_mood_image.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.13 on 2024-08-10 20:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("moods", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="mood", + name="image", + field=models.ImageField(blank=True, null=True, upload_to="moods/"), + ), + ] diff --git a/vrobbler/apps/moods/models.py b/vrobbler/apps/moods/models.py index 390c094..831cfcd 100644 --- a/vrobbler/apps/moods/models.py +++ b/vrobbler/apps/moods/models.py @@ -1,10 +1,12 @@ import logging -from uuid import uuid4 from django.contrib.auth import get_user_model from django.db import models from django.urls import reverse +from imagekit.models import ImageSpecField +from imagekit.processors import ResizeToFit from scrobbles.mixins import ScrobblableMixin + from vrobbler.apps.scrobbles.dataclasses import MoodLogData logger = logging.getLogger(__name__) @@ -14,6 +16,19 @@ User = get_user_model() class Mood(ScrobblableMixin): description = models.TextField(**BNULL) + image = models.ImageField(upload_to="moods/", **BNULL) + image_small = ImageSpecField( + source="thumbnail", + processors=[ResizeToFit(100, 100)], + format="JPEG", + options={"quality": 60}, + ) + image_medium = ImageSpecField( + source="thumbnail", + processors=[ResizeToFit(300, 300)], + format="JPEG", + options={"quality": 75}, + ) def __str__(self): if self.title: @@ -23,6 +38,19 @@ class Mood(ScrobblableMixin): def get_absolute_url(self): return reverse("moods:mood-detail", kwargs={"slug": self.uuid}) + def get_start_url(self): + return reverse("scrobbles:start", kwargs={"uuid": self.uuid}) + + @property + def subtitle(self) -> str: + return "" + @property def logdata_cls(self): return MoodLogData + + @property + def primary_image_url(self) -> str: + if self.image: + return self.image.url + return "" diff --git a/vrobbler/apps/scrobbles/constants.py b/vrobbler/apps/scrobbles/constants.py index 8050957..61377b2 100644 --- a/vrobbler/apps/scrobbles/constants.py +++ b/vrobbler/apps/scrobbles/constants.py @@ -12,6 +12,7 @@ PLAY_AGAIN_MEDIA = { "videogames": "VideoGame", "books": "Book", "boardgames": "BoardGame", + "moods": "Mood", } MEDIA_END_PADDING_SECONDS = { diff --git a/vrobbler/apps/scrobbles/mixins.py b/vrobbler/apps/scrobbles/mixins.py index 4a65ddf..a4061af 100644 --- a/vrobbler/apps/scrobbles/mixins.py +++ b/vrobbler/apps/scrobbles/mixins.py @@ -2,12 +2,14 @@ import logging from typing import Optional from uuid import uuid4 +from django.apps import apps from django.db import models from django.urls import reverse +from django.utils import timezone from django_extensions.db.models import TimeStampedModel -from taggit.managers import TaggableManager from scrobbles.utils import get_scrobbles_for_media -from taggit.models import TagBase, GenericTaggedItemBase +from taggit.managers import TaggableManager +from taggit.models import GenericTaggedItemBase, TagBase BNULL = {"blank": True, "null": True} @@ -33,6 +35,7 @@ class ObjectWithGenres(GenericTaggedItemBase): class ScrobblableMixin(TimeStampedModel): SECONDS_TO_STALE = 1600 + COMPLETION_PERCENT = 100 uuid = models.UUIDField(default=uuid4, editable=False, **BNULL) title = models.CharField(max_length=255, **BNULL) @@ -44,21 +47,53 @@ class ScrobblableMixin(TimeStampedModel): class Meta: abstract = True + def basic_scrobble_data(self, user_id) -> dict: + return { + "user_id": user_id, + "timestamp": timezone.now(), + "playback_position_seconds": 0, + "source": "Vrobbler", + } + + def scrobble_for_user(self, user_id): + Scrobble = apps.get_model("scrobbles", "Scrobble") + scrobble_data = self.basic_scrobble_data(user_id) + logger.info( + "[scrobble_for_user] called", + extra={ + "webpage_id": self.id, + "user_id": user_id, + "scrobble_data": scrobble_data, + "media_type": Scrobble.MediaType.WEBPAGE, + }, + ) + return Scrobble.create_or_update(self, user_id, scrobble_data) + @property def primary_image_url(self) -> str: logger.warn(f"Not implemented yet") return "" - def fix_metadata(self): + @property + def logdata_cls(self) -> None: + logger.warn("logdata_cls() not implemented yet") + return None + + @property + def subtitle(self) -> str: + return "" + + def fix_metadata(self) -> None: logger.warn("fix_metadata() not implemented yet") @classmethod - def find_or_create(cls): + def find_or_create(cls) -> None: logger.warn("find_or_create() not implemented yet") def scrobble(self, user_id, **kwargs): """Given a user ID and a dictionary of data, attempts to scrobble it""" from scrobbles.models import Scrobble + Scrobble.create_or_update(self, user_id, **kwargs) diff --git a/vrobbler/apps/scrobbles/models.py b/vrobbler/apps/scrobbles/models.py index d38b34d..8dce87e 100644 --- a/vrobbler/apps/scrobbles/models.py +++ b/vrobbler/apps/scrobbles/models.py @@ -904,7 +904,7 @@ class Scrobble(TimeStampedModel): .order_by("-timestamp") .first() ) - source = scrobble_data.get("source") + source = scrobble_data.get("source", "Vrobbler") mtype = media.__class__.__name__ mopidy_status = scrobble_data.get("mopidy_status", None) diff --git a/vrobbler/apps/scrobbles/views.py b/vrobbler/apps/scrobbles/views.py index 55af9cc..b0cb081 100644 --- a/vrobbler/apps/scrobbles/views.py +++ b/vrobbler/apps/scrobbles/views.py @@ -69,12 +69,13 @@ class ScrobbleableListView(ListView): if not self.request.user.is_anonymous: queryset = queryset.annotate( scrobble_count=Count("scrobble"), - filter=Q(user=self.request.user), + filter=Q(scrobble__user=self.request.user), ).order_by("-scrobble_count") else: queryset = queryset.annotate( scrobble_count=Count("scrobble") ).order_by("-scrobble_count") + return queryset class ScrobbleableDetailView(DetailView): @@ -85,8 +86,8 @@ class ScrobbleableDetailView(DetailView): context_data = super().get_context_data(**kwargs) context_data["scrobbles"] = list() if not self.request.user.is_anonymous: - context_data["scrobbles"] = self.object.scrobbles( - self.request.user + context_data["scrobbles"] = self.object.scrobble_set.filter( + user=self.request.user ) return context_data @@ -429,6 +430,10 @@ def import_audioscrobbler_file(request): @permission_classes([IsAuthenticated]) @api_view(["GET"]) def scrobble_start(request, uuid): + logger.info( + "[scrobble_start] called", + extra={"request": request, "uuid": uuid}, + ) user = request.user success_url = request.META.get("HTTP_REFERER") @@ -443,19 +448,17 @@ def scrobble_start(request, uuid): break if not media_obj: + logger.info( + "[scrobble_start] media object not found", + extra={"uuid": uuid, "user_id": user.id}, + ) + # TODO Log that we couldn't find a media obj to scrobble return scrobble = None user_id = request.user.id if media_obj: - if media_obj.__class__.__name__ == Scrobble.MediaType.BOOK: - scrobble = manual_scrobble_book(media_obj.openlibrary_id, user_id) - if media_obj.__class__.__name__ == Scrobble.MediaType.VIDEO_GAME: - scrobble = manual_scrobble_video_game(media_obj.hltb_id, user_id) - if media_obj.__class__.__name__ == Scrobble.MediaType.BOARD_GAME: - scrobble = manual_scrobble_board_game(media_obj.bggeek_id, user_id) - if media_obj.__class__.__name__ == Scrobble.MediaType.WEBPAGE: - scrobble = manual_scrobble_webpage(media_obj.url, user_id) + media_obj.scrobble_for_user(user_id) if scrobble: messages.add_message( diff --git a/vrobbler/apps/webpages/models.py b/vrobbler/apps/webpages/models.py index d3916cb..60437dc 100644 --- a/vrobbler/apps/webpages/models.py +++ b/vrobbler/apps/webpages/models.py @@ -89,6 +89,28 @@ class WebPage(ScrobblableMixin): def subtitle(self): return self.domain + @property + def primary_image_url(self) -> str: + # TODO Figure out how to add a preview? + return "" + + def scrobble_for_user(self, user_id): + Scrobble = apps.get_model("scrobbles", "Scrobble") + scrobble_data = self.basic_scrobble_data(user_id) + logger.info( + "[scrobble_for_user] called for webpage", + extra={ + "webpage_id": self.id, + "user_id": user_id, + "scrobble_data": scrobble_data, + "media_type": Scrobble.MediaType.WEBPAGE, + }, + ) + scrobble = Scrobble.create_or_update(self, user_id, scrobble_data) + # TODO Possibly make this async? + scrobble.push_to_archivebox() + return scrobble + def scrobbles(self, user): Scrobble = apps.get_model("scrobbles", "Scrobble") return Scrobble.objects.filter(user=user, web_page=self).order_by( diff --git a/vrobbler/templates/moods/mood_detail.html b/vrobbler/templates/moods/mood_detail.html new file mode 100644 index 0000000..91d11cd --- /dev/null +++ b/vrobbler/templates/moods/mood_detail.html @@ -0,0 +1,39 @@ +{% extends "base_list.html" %} +{% load static %} + +{% block title %}{{object.title}}{% endblock %} + +{% block lists %} +
+ + {% if object.extract %} +
+ {{object.extract|linebreaks}} +
+ {% endif %} +
+
+
+
+

Last scrobbles

+
+ + + + + + + + {% for scrobble in object.scrobble_set.all %} + + + + {% endfor %} + +
Date
{{scrobble.timestamp}}
+
+
+
+{% endblock %} diff --git a/vrobbler/templates/moods/mood_list.html b/vrobbler/templates/moods/mood_list.html new file mode 100644 index 0000000..2a8f74c --- /dev/null +++ b/vrobbler/templates/moods/mood_list.html @@ -0,0 +1,42 @@ +{% extends "base_list.html" %} + +{% block title %}Moods{% endblock %} + +{% block head_extra %} + +{% endblock %} + +{% block lists %} +
+ +
+
+ + + + + + + + + + {% for mood in object_list %} + + + {% if request.user.is_authenticated %} + + + {% endif %} + + {% endfor %} + +
TitleScrobblesStart
{{mood}}{{mood.scrobble_count}}Scrobble
+
+
+
+{% endblock %}