diff --git a/PROJECT.org b/PROJECT.org index cb335ae..c00b881 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [2/22] :vrobbler:project:personal: +* Backlog [3/23] :vrobbler:project:personal: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles: :PROPERTIES: :ID: 702462cf-d54b-48c6-8a7c-78b8de751deb @@ -590,6 +590,10 @@ We should rename `email_scrobble_board_game` to reflect the fact that it's just a helper method to create board game scrobbles given a json blob. It's independent of the email flow it was originally creatdd for +** DONE [#B] Use pk ID for scrobble detail view, not uuid :scrobbles: +:PROPERTIES: +:ID: 9cc3b285-e478-041e-394b-3d550aefbe1d +:END: ** DONE [#B] Display videogame screenshots on scrobble detail if they exist :videogames:templates: :PROPERTIES: :ID: 0406d082-20f6-0d12-76e2-f281c4801468 diff --git a/tests/scrobbles_tests/test_views.py b/tests/scrobbles_tests/test_views.py index be26a75..69299f9 100644 --- a/tests/scrobbles_tests/test_views.py +++ b/tests/scrobbles_tests/test_views.py @@ -519,7 +519,7 @@ def test_scrobble_detail_view_with_notes_as_flat_list(client): "description": "Test description", }, ) - url = reverse("scrobbles:detail", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:detail", kwargs={"pk": scrobble.id}) response = client.get(url) assert response.status_code == 200 assert "First note" in response.content.decode() @@ -545,7 +545,7 @@ def test_scrobble_detail_view_with_notes_as_dict_timestamps(client): "description": "Test description", }, ) - url = reverse("scrobbles:detail", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:detail", kwargs={"pk": scrobble.id}) response = client.get(url) assert response.status_code == 200 content = response.content.decode() @@ -574,7 +574,7 @@ def test_scrobble_detail_view_with_notes_and_labels(client): "description": "Test description", }, ) - url = reverse("scrobbles:detail", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:detail", kwargs={"pk": scrobble.id}) response = client.get(url) assert response.status_code == 200 content = response.content.decode() @@ -597,7 +597,7 @@ def test_scrobble_detail_view_post_updates_log(client): "description": "Original description", }, ) - url = reverse("scrobbles:detail", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:detail", kwargs={"pk": scrobble.id}) client.force_login(user) response = client.post( @@ -896,7 +896,7 @@ def test_change_visibility_owner_can_change(client): ) client.force_login(user) - url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:change-visibility", kwargs={"pk": scrobble.id}) response = client.post(url, {"visibility": "shared"}) assert response.status_code == 302 @@ -919,7 +919,7 @@ def test_change_visibility_non_owner_gets_404(client): ) client.force_login(other) - url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:change-visibility", kwargs={"pk": scrobble.id}) response = client.post(url, {"visibility": "shared"}) assert response.status_code == 404 @@ -937,7 +937,7 @@ def test_change_visibility_anonymous_redirects_to_login(client): task=task, media_type="Task", user=user, visibility="private", timestamp=timezone.now(), ) - url = reverse("scrobbles:change-visibility", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:change-visibility", kwargs={"pk": scrobble.id}) response = client.post(url, {"visibility": "shared"}) assert response.status_code == 302 assert "/login/" in response.url @@ -956,7 +956,7 @@ def test_regenerate_share_token_invalidates_old_sqid(client): old_sqid = encode_scrobble_share(scrobble.id, scrobble.share_token_version) client.force_login(user) - url = reverse("scrobbles:regenerate-share-token", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:regenerate-share-token", kwargs={"pk": scrobble.id}) response = client.post(url) assert response.status_code == 302 @@ -985,7 +985,7 @@ def test_share_analytics_owner_can_view(client): ) client.force_login(user) - url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:share-analytics", kwargs={"pk": scrobble.id}) response = client.get(url) assert response.status_code == 200 @@ -1005,7 +1005,7 @@ def test_share_analytics_non_owner_gets_404(client): ) client.force_login(other) - url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:share-analytics", kwargs={"pk": scrobble.id}) response = client.get(url) assert response.status_code == 404 @@ -1027,7 +1027,7 @@ def test_share_analytics_shows_view_logs(client): client.get(share_url) client.force_login(user) - url = reverse("scrobbles:share-analytics", kwargs={"uuid": scrobble.uuid}) + url = reverse("scrobbles:share-analytics", kwargs={"pk": scrobble.id}) response = client.get(url) assert response.status_code == 200 content = response.content.decode() diff --git a/vrobbler/apps/books/models.py b/vrobbler/apps/books/models.py index 943659b..f2f13ed 100644 --- a/vrobbler/apps/books/models.py +++ b/vrobbler/apps/books/models.py @@ -225,7 +225,7 @@ class Book(LongPlayScrobblableMixin): @property def resume_start_url(self): - return reverse("scrobbles:start", kwargs={"uuid": self.uuid}) + "?resume=1" + return reverse("scrobbles:start", kwargs={"media_uuid": self.uuid}) + "?resume=1" @classmethod def get_from_comicvine( diff --git a/vrobbler/apps/scrobbles/mixins.py b/vrobbler/apps/scrobbles/mixins.py index fbb909d..42afd87 100644 --- a/vrobbler/apps/scrobbles/mixins.py +++ b/vrobbler/apps/scrobbles/mixins.py @@ -114,7 +114,7 @@ class ScrobblableMixin(TimeStampedModel): @property def start_url(self): - return reverse("scrobbles:start", kwargs={"uuid": self.uuid}) + return reverse("scrobbles:start", kwargs={"media_uuid": self.uuid}) @property def strings(self) -> ScrobblableConstants: @@ -162,7 +162,7 @@ class LongPlayScrobblableMixin(ScrobblableMixin): return False def get_longplay_finish_url(self): - return reverse("scrobbles:longplay-finish", kwargs={"uuid": self.uuid}) + return reverse("scrobbles:longplay-finish", kwargs={"media_uuid": self.uuid}) def first_long_play_scrobble_for_user(self, user) -> Optional["Scrobble"]: last = self.last_long_play_scrobble_for_user(user) diff --git a/vrobbler/apps/scrobbles/models.py b/vrobbler/apps/scrobbles/models.py index fcad691..1fe9989 100644 --- a/vrobbler/apps/scrobbles/models.py +++ b/vrobbler/apps/scrobbles/models.py @@ -906,7 +906,7 @@ class Scrobble(TimeStampedModel): @property def finish_url(self) -> str: - return reverse("scrobbles:finish", kwargs={"uuid": self.uuid}) + return reverse("scrobbles:finish", kwargs={"pk": self.pk}) def save(self, *args, **kwargs): class_name = self.media_obj.__class__.__name__ @@ -948,10 +948,7 @@ class Scrobble(TimeStampedModel): return super(Scrobble, self).save(*args, **kwargs) def get_absolute_url(self): - if not self.uuid: - self.uuid = uuid4() - self.save(update_fields=["uuid"]) - return reverse("scrobbles:detail", kwargs={"uuid": self.uuid}) + return reverse("scrobbles:detail", kwargs={"pk": self.pk}) def get_share_url(self): if self.visibility == Visibility.PRIVATE: diff --git a/vrobbler/apps/scrobbles/urls.py b/vrobbler/apps/scrobbles/urls.py index 6bc7ee3..b36ce73 100644 --- a/vrobbler/apps/scrobbles/urls.py +++ b/vrobbler/apps/scrobbles/urls.py @@ -44,7 +44,7 @@ urlpatterns = [ name="lookup-manual-scrobble", ), path( - "long-play-finish//", + "long-play-finish//", views.scrobble_longplay_finish, name="longplay-finish", ), @@ -160,38 +160,38 @@ urlpatterns = [ name="shared-detail", ), path( - "scrobbles//", + "scrobbles//", views.ScrobbleDetailView.as_view(), name="detail", ), path( - "scrobbles//regenerate-share-token/", + "scrobbles//regenerate-share-token/", views.RegenerateShareTokenView.as_view(), name="regenerate-share-token", ), path( - "scrobbles//change-visibility/", + "scrobbles//change-visibility/", views.ChangeVisibilityView.as_view(), name="change-visibility", ), path( - "scrobbles//share-analytics/", + "scrobbles//share-analytics/", views.ScrobbleShareAnalyticsView.as_view(), name="share-analytics", ), path( - "scrobbles//add-to-mopidy-queue/", + "scrobbles//add-to-mopidy-queue/", views.add_to_mopidy_queue, name="add-to-mopidy-queue", ), path( - "scrobbles//add-to-mopidy-monthly-playlist/", + "scrobbles//add-to-mopidy-monthly-playlist/", views.add_to_mopidy_monthly_playlist, name="add-to-mopidy-monthly-playlist", ), - path("scrobbles//start/", views.scrobble_start, name="start"), - path("scrobbles//finish/", views.scrobble_finish, name="finish"), - path("scrobbles//cancel/", views.scrobble_cancel, name="cancel"), + path("scrobbles//start/", views.scrobble_start, name="start"), + path("scrobbles//finish/", views.scrobble_finish, name="finish"), + path("scrobbles//cancel/", views.scrobble_cancel, name="cancel"), path( "favorite///toggle/", views.toggle_favorite, diff --git a/vrobbler/apps/scrobbles/views.py b/vrobbler/apps/scrobbles/views.py index 92fb3bc..394b1b6 100644 --- a/vrobbler/apps/scrobbles/views.py +++ b/vrobbler/apps/scrobbles/views.py @@ -839,10 +839,10 @@ def import_audioscrobbler_file(request): @api_view(["GET"]) @permission_classes([IsAuthenticated]) -def scrobble_start(request, uuid): +def scrobble_start(request, media_uuid): logger.info( "[scrobble_start] called", - extra={"request": request, "uuid": uuid}, + extra={"request": request, "media_uuid": media_uuid}, ) user = request.user success_url = request.META.get("HTTP_REFERER") @@ -853,14 +853,14 @@ def scrobble_start(request, uuid): media_obj = None for app, model in PLAY_AGAIN_MEDIA.items(): media_model = apps.get_model(app_label=app, model_name=model) - media_obj = media_model.objects.filter(uuid=uuid).first() + media_obj = media_model.objects.filter(uuid=media_uuid).first() if media_obj: break if not media_obj: logger.info( "[scrobble_start] media object not found", - extra={"uuid": uuid, "user_id": user.id}, + extra={"media_uuid": media_uuid, "user_id": user.id}, ) raise Exception("No media object provided to scrobble") @@ -897,7 +897,7 @@ def scrobble_start(request, uuid): ) else: messages.add_message( - request, messages.ERROR, f"Media with uuid {uuid} not found." + request, messages.ERROR, f"Media with uuid {media_uuid} not found." ) if ( @@ -915,7 +915,7 @@ def scrobble_start(request, uuid): @api_view(["GET"]) -def scrobble_longplay_finish(request, uuid): +def scrobble_longplay_finish(request, media_uuid): user = request.user success_url = request.META.get("HTTP_REFERER") @@ -923,7 +923,7 @@ def scrobble_longplay_finish(request, uuid): return HttpResponseRedirect(success_url) # Try scrobble UUID first - scrobble = Scrobble.objects.filter(uuid=uuid, user=user).first() + scrobble = Scrobble.objects.filter(uuid=media_uuid, user=user).first() if scrobble: if scrobble.long_play_complete == True: scrobble.long_play_complete = None @@ -947,13 +947,13 @@ def scrobble_longplay_finish(request, uuid): media_obj = None for app, model in LONG_PLAY_MEDIA.items(): media_model = apps.get_model(app_label=app, model_name=model) - media_obj = media_model.objects.filter(uuid=uuid).first() + media_obj = media_model.objects.filter(uuid=media_uuid).first() if media_obj: break if not media_obj: messages.add_message( - request, messages.ERROR, f"Media with uuid {uuid} not found." + request, messages.ERROR, f"Media with uuid {media_uuid} not found." ) return HttpResponseRedirect(success_url) @@ -976,14 +976,14 @@ def scrobble_longplay_finish(request, uuid): ) else: messages.add_message( - request, messages.ERROR, f"Media with uuid {uuid} not found." + request, messages.ERROR, f"Media with uuid {media_uuid} not found." ) return HttpResponseRedirect(success_url) @api_view(["GET"]) @permission_classes([IsAuthenticated]) -def scrobble_finish(request, uuid): +def scrobble_finish(request, pk): user = request.user success_url = request.META.get("HTTP_REFERER") if not success_url: @@ -992,7 +992,7 @@ def scrobble_finish(request, uuid): if not user.is_authenticated: return HttpResponseRedirect(success_url) - scrobble = Scrobble.objects.filter(user=user, uuid=uuid).first() + scrobble = Scrobble.objects.filter(user=user, pk=pk).first() if scrobble: scrobble.stop(force_finish=True) messages.add_message( @@ -1007,14 +1007,14 @@ def scrobble_finish(request, uuid): @api_view(["GET"]) @permission_classes([IsAuthenticated]) -def scrobble_cancel(request, uuid): +def scrobble_cancel(request, pk): user = request.user success_url = reverse_lazy("vrobbler-home") if not user.is_authenticated: return HttpResponseRedirect(success_url) - scrobble = Scrobble.objects.filter(user=user, uuid=uuid).first() + scrobble = Scrobble.objects.filter(user=user, pk=pk).first() if scrobble: scrobble.cancel() messages.add_message( @@ -1028,11 +1028,11 @@ def scrobble_cancel(request, uuid): @require_POST -def add_to_mopidy_queue(request, uuid): +def add_to_mopidy_queue(request, pk): if not request.user.is_authenticated: - return redirect("scrobbles:detail", uuid=uuid) + return redirect("scrobbles:detail", pk=pk) - scrobble = get_object_or_404(Scrobble, uuid=uuid, user=request.user) + scrobble = get_object_or_404(Scrobble, pk=pk, user=request.user) mopidy_url = request.user.profile.mopidy_api_url if not mopidy_url: @@ -1041,22 +1041,22 @@ def add_to_mopidy_queue(request, uuid): messages.ERROR, "Mopidy API URL not configured in your profile settings.", ) - return redirect("scrobbles:detail", uuid=uuid) + return redirect("scrobbles:detail", pk=pk) from scrobbles.tasks import add_scrobble_to_mopidy_queue as task task.delay(scrobble.id) msg = f'Adding "{scrobble.media_obj}" to Mopidy queue.' messages.add_message(request, messages.SUCCESS, msg) - return redirect("scrobbles:detail", uuid=uuid) + return redirect("scrobbles:detail", pk=pk) @require_POST -def add_to_mopidy_monthly_playlist(request, uuid): +def add_to_mopidy_monthly_playlist(request, pk): if not request.user.is_authenticated: - return redirect("scrobbles:detail", uuid=uuid) + return redirect("scrobbles:detail", pk=pk) - scrobble = get_object_or_404(Scrobble, uuid=uuid, user=request.user) + scrobble = get_object_or_404(Scrobble, pk=pk, user=request.user) profile = request.user.profile pattern = profile.monthly_mopidy_playlist_pattern @@ -1066,7 +1066,7 @@ def add_to_mopidy_monthly_playlist(request, uuid): messages.ERROR, "Monthly playlist pattern or Mopidy API URL not configured in your profile.", ) - return redirect("scrobbles:detail", uuid=uuid) + return redirect("scrobbles:detail", pk=pk) now = now_user_timezone(profile) playlist_name = DateFormat(now).format(pattern) @@ -1079,7 +1079,7 @@ def add_to_mopidy_monthly_playlist(request, uuid): messages.SUCCESS, f'Adding "{scrobble.media_obj}" to monthly playlist "{playlist_name}".', ) - return redirect("scrobbles:detail", uuid=uuid) + return redirect("scrobbles:detail", pk=pk) @require_POST @@ -1184,8 +1184,6 @@ class ScrobbleStatusView(LoginRequiredMixin, TemplateView): class ScrobbleDetailView(DetailView): model = Scrobble - slug_field = "uuid" - slug_url_kwarg = "uuid" paginate_by = 100 def get_object(self, queryset=None): @@ -1385,15 +1383,15 @@ class ScrobbleExploreView(ListView): class RegenerateShareTokenView(LoginRequiredMixin, View): - def post(self, request, uuid): - scrobble = get_object_or_404(Scrobble, uuid=uuid, user=request.user) + def post(self, request, pk): + scrobble = get_object_or_404(Scrobble, pk=pk, user=request.user) scrobble.regenerate_share_token() return redirect(scrobble.get_absolute_url()) class ChangeVisibilityView(LoginRequiredMixin, View): - def post(self, request, uuid): - scrobble = get_object_or_404(Scrobble, uuid=uuid, user=request.user) + def post(self, request, pk): + scrobble = get_object_or_404(Scrobble, pk=pk, user=request.user) visibility = request.POST.get("visibility") if visibility not in (Visibility.PUBLIC, Visibility.SHARED, Visibility.PRIVATE): return redirect(scrobble.get_absolute_url()) @@ -1404,8 +1402,6 @@ class ChangeVisibilityView(LoginRequiredMixin, View): class ScrobbleShareAnalyticsView(LoginRequiredMixin, DetailView): model = Scrobble - slug_field = "uuid" - slug_url_kwarg = "uuid" template_name = "scrobbles/scrobble_share_analytics.html" def get_queryset(self): diff --git a/vrobbler/templates/base.html b/vrobbler/templates/base.html index 0d7a622..af964f5 100644 --- a/vrobbler/templates/base.html +++ b/vrobbler/templates/base.html @@ -322,8 +322,8 @@

- Cancel - Finish + Cancel + Finish

{% if not forloop.last %}
{% endif %} diff --git a/vrobbler/templates/books/book_detail.html b/vrobbler/templates/books/book_detail.html index 1dc5dd7..39b402b 100644 --- a/vrobbler/templates/books/book_detail.html +++ b/vrobbler/templates/books/book_detail.html @@ -65,7 +65,7 @@ {% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %} {{scrobble.local_timestamp}} - {% if scrobble.long_play_complete == True %}Yes (not complete?){% else %}No{% endif %} + {% if scrobble.long_play_complete == True %}Yes (not complete?){% else %}No{% endif %} {% if scrobble.in_progress %}Now reading{% else %}{{scrobble.session_pages_read}}{% endif %} {% for author in scrobble.book.authors.all %}{{author}}{% if not forloop.last %}, {% endif %}{% endfor %} diff --git a/vrobbler/templates/bricksets/brickset_detail.html b/vrobbler/templates/bricksets/brickset_detail.html index 814fe67..f463439 100644 --- a/vrobbler/templates/bricksets/brickset_detail.html +++ b/vrobbler/templates/bricksets/brickset_detail.html @@ -48,7 +48,7 @@ {% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %} {{scrobble.local_timestamp}} - {% if scrobble.long_play_complete == True %}Yes (not complete?){% else %}No{% endif %} + {% if scrobble.long_play_complete == True %}Yes (not complete?){% else %}No{% endif %} {% if scrobble.in_progress %}Now reading{% else %}{{scrobble.session_pages_read}}{% endif %} {% endfor %} diff --git a/vrobbler/templates/scrobbles/_row.html b/vrobbler/templates/scrobbles/_row.html index 7d6208d..20bbe57 100644 --- a/vrobbler/templates/scrobbles/_row.html +++ b/vrobbler/templates/scrobbles/_row.html @@ -1,7 +1,7 @@ {% load humanize %} {% load naturalduration %} - {% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | Finish{% else %}{{scrobble.local_timestamp|naturaltime}}{% endif %} + {% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | Finish{% else %}{{scrobble.local_timestamp|naturaltime}}{% endif %} {% if scrobble.media_type in "Task" %}

{{scrobble.media_obj.title|truncatechars_html:45}} - {% if scrobble.logdata %}{% if scrobble.logdata.title %}{{scrobble.logdata.title}}{% endif %}{% endif %}

diff --git a/vrobbler/templates/scrobbles/calendar.html b/vrobbler/templates/scrobbles/calendar.html index 4580651..6d4b567 100644 --- a/vrobbler/templates/scrobbles/calendar.html +++ b/vrobbler/templates/scrobbles/calendar.html @@ -205,7 +205,7 @@ header.navbar { display: none !important; } style="background:{% if not cd.is_today %}{{ cd.color }}{% endif %};">
{{ cd.day }}{% if cd.total_count > 0 %} {{ cd.total_count }}{% endif %}
{% for s in cd.scrobbles %} - {{ s.emoji }} {{ s.title }} {% endfor %} diff --git a/vrobbler/templates/scrobbles/scrobble_detail.html b/vrobbler/templates/scrobbles/scrobble_detail.html index 2467a7d..b69b8af 100644 --- a/vrobbler/templates/scrobbles/scrobble_detail.html +++ b/vrobbler/templates/scrobbles/scrobble_detail.html @@ -88,7 +88,7 @@ {% endif %}"> {{ object.get_visibility_display }} -
+ {% csrf_token %}