diff --git a/PROJECT.org b/PROJECT.org index 030060a..6493d67 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -92,7 +92,7 @@ fetching and simple saving. :LOGBOOK: CLOCK: [2025-07-09 Wed 09:55]--[2025-07-09 Wed 10:15] => 0:20 :END: -* Backlog [16/35] +* Backlog [17/37] ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :vrobbler:personal:bug:music:scrobbles: ** TODO [#C] Move to using more robust mopidy-webhooks pacakge form pypi :utility:improvement: :PROPERTIES: @@ -431,11 +431,14 @@ https://app.todoist.com/app/task/add-a-csv-endpoint-for-users-book-reads-that-li ** TODO [#A] When creating org-mode tasks, don't copy comments :vrobbler:bug:scrobbles:tasks: ** TODO Add sentiment parsing for Scrobbles with notes :vrobbler:project:scrobbles:sentiment: -** DONE Fix genearting chart records :vrobbler:bug:personal:project:chartrecords: +** STRT Fix display of notes so they look like stickies :vrobbler:project:personal:notes:scrobbles: :PROPERTIES: -:ID: e749d762-44ac-dd7e-017e-2423016737ff +:ID: 0ace8814-e96e-fa54-28d1-c57dcb508f1e +:END: +** DONE Add searching to scrobbles :vrobbler:project:personal:search:scrobbles: +:PROPERTIES: +:ID: b4690c76-d741-4320-20e8-c003251fe035 :END: - ** DONE Fix uniqueness of imdb_id messing up youtube videos :vrobbler:project:bug:videos: :PROPERTIES: :ID: 610b8c4c-abf1-8630-a4fd-08d9939da9f5 @@ -445,6 +448,11 @@ Turns out you can't make imdb_id unique by itself or you never get to create youtube videos. Rather, we should make imdb_id and youtube_id unique together so imdb_id can be empty for every youtube_id and vice versa. +** DONE Fix genearting chart records :vrobbler:bug:personal:project:chartrecords: +:PROPERTIES: +:ID: e749d762-44ac-dd7e-017e-2423016737ff +:END: + ** DONE [#A] Save raw scrobble request data to every scrobble log :vrobbler:personal:feature:scrobbles: :PROPERTIES: :ID: b4686db4-1319-399c-9b84-f0b1036c6815 diff --git a/vrobbler/apps/scrobbles/views.py b/vrobbler/apps/scrobbles/views.py index d3df06c..c76ef84 100644 --- a/vrobbler/apps/scrobbles/views.py +++ b/vrobbler/apps/scrobbles/views.py @@ -792,6 +792,7 @@ class ScrobbleDetailView(DetailView): model = Scrobble slug_field = "uuid" slug_url_kwarg = "uuid" + paginate_by = 100 def get_form_class(self): return self.object.media_obj.logdata_cls().form() @@ -830,10 +831,55 @@ class ScrobbleDetailView(DetailView): context = self.get_context_data(log_form=form) return self.render_to_response(context) + MEDIA_FK_MAP = { + "Video": "video", + "Track": "track", + "PodcastEpisode": "podcast_episode", + "Book": "book", + "Paper": "paper", + "VideoGame": "video_game", + "BoardGame": "board_game", + "Beer": "beer", + "Food": "food", + "Puzzle": "puzzle", + "Trail": "trail", + "GeoLocation": "geo_location", + "Task": "task", + "WebPage": "web_page", + "LifeEvent": "life_event", + "Mood": "mood", + "BrickSet": "brick_set", + } + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if "log_form" not in context: context["log_form"] = self.get_form() + + media_obj = self.object.media_obj + user = self.object.user + media_type = self.object.media_type + + fk_field = self.MEDIA_FK_MAP.get(media_type) + if fk_field: + related_scrobbles = ( + Scrobble.objects.filter(user=user) + .filter(**{fk_field: media_obj}) + .exclude(id=self.object.id) + .order_by("-timestamp") + ) + else: + related_scrobbles = Scrobble.objects.none() + + page = self.request.GET.get("page") + paginator = Paginator(related_scrobbles, self.paginate_by) + try: + context["related_scrobbles"] = paginator.page(page) + except PageNotAnInteger: + context["related_scrobbles"] = paginator.page(1) + except EmptyPage: + context["related_scrobbles"] = paginator.page(paginator.num_pages) + return context diff --git a/vrobbler/apps/tasks/models.py b/vrobbler/apps/tasks/models.py index c35c0c8..0282c4d 100644 --- a/vrobbler/apps/tasks/models.py +++ b/vrobbler/apps/tasks/models.py @@ -1,3 +1,4 @@ +import json from dataclasses import dataclass from typing import Optional @@ -45,15 +46,20 @@ class TaskLogData(BaseLogData): lines = [] if self.notes: - for note in self.notes: + notes = self.notes + if isinstance(notes, str): + notes = [notes] + + for note in notes: if isinstance(note, dict): timestamp, note_text = next(iter(note.items())) # Flatten newlines and clean whitespace note_text = " ".join(note_text.strip().split()) lines.append(f"{timestamp}: {note_text} [{labels_str}]") + if isinstance(note, list): + lines += note if isinstance(note, str): lines.append(note) - return "\n".join(lines).encode("utf-8").decode("unicode_escape") diff --git a/vrobbler/templates/scrobbles/scrobble_detail.html b/vrobbler/templates/scrobbles/scrobble_detail.html index 541147d..667e32f 100644 --- a/vrobbler/templates/scrobbles/scrobble_detail.html +++ b/vrobbler/templates/scrobbles/scrobble_detail.html @@ -1,6 +1,7 @@ {% extends "base_list.html" %} {% load form_tags %} {% load mathfilters %} +{% load naturalduration %} {% load static %} {% block title %}{{object.name}}{% endblock %} @@ -11,40 +12,95 @@

{{ object.media_obj }} - {{object.media_type}}

-{% if object.notes %} -
- Notes ({{ object.notes|length }}) - -
+{% with notes_string=object.logdata.notes_as_str %} +{% if notes_string %} +
+
Notes
+
{{ notes_string }}
+
{% endif %} +{% endwith %} - {% if object.logdata.avg_seconds_per_page %}

Rate: {{object.logdata.avg_seconds_per_page}}s per page

{% endif %} -

Edit Log

-
- {% csrf_token %} - {% for field in log_form %} -
- - {{ field|add_class:"form-control" }} - {% if field.help_text %} -
{{ field.help_text }}
- {% endif %} - {% for error in field.errors %} -
{{ error }}
- {% endfor %} -
- {% endfor %} +
+ Edit Log + + {% csrf_token %} + {% for field in log_form %} +
+ + {{ field|add_class:"form-control" }} + {% if field.help_text %} +
{{ field.help_text }}
+ {% endif %} + {% for error in field.errors %} +
{{ error }}
+ {% endfor %} +
+ {% endfor %} - - + + +
+ +

Other Scrobbles of This Media

+{% if related_scrobbles %} +

{{ related_scrobbles.paginator.count }} scrobble{{ related_scrobbles.paginator.count|pluralize }}

+ + + + + + + + + + {% for scrobble in related_scrobbles %} + + + + + + {% endfor %} + +
DateTitleTime
{{ scrobble.timestamp|date:"M d, Y" }} + {% if scrobble.media_type == "Task" and scrobble.logdata.title %}{{ scrobble.media_obj.title }}: {{ scrobble.logdata.title }}{% else %}{{ scrobble.media_obj.title|default:scrobble.media_obj }}{% endif %} + + {% if scrobble.playback_position_seconds %}{{ scrobble.playback_position_seconds|natural_duration }}{% endif %} +
+ +{% if related_scrobbles.has_other_pages %} + +{% endif %} + +{% else %} +

No other scrobbles of this media.

+{% endif %}