Browse Source

[books] Move book metadata into the log field

Colin Powell 11 months ago
parent
commit
b626ac583a

+ 8 - 5
vrobbler/apps/books/koreader.py

@@ -293,6 +293,11 @@ def build_scrobbles_from_book_map(
                     logger.info(
                         f"Queueing scrobble for {book_id}, page {cur_page_number}"
                     )
+                    log_data = {
+                        "koreader_hash": book_dict.get("hash"),
+                        "page_data": scrobble_page_data,
+                        "pages_read": cur_page_number,
+                    }
                     scrobbles_to_create.append(
                         Scrobble(
                             book_id=book_id,
@@ -300,11 +305,9 @@ def build_scrobbles_from_book_map(
                             source="KOReader",
                             media_type=Scrobble.MediaType.BOOK,
                             timestamp=timestamp,
+                            log=log_data,
                             stop_timestamp=stop_timestamp,
                             playback_position_seconds=playback_position_seconds,
-                            book_koreader_hash=book_dict.get("hash"),
-                            book_page_data=scrobble_page_data,
-                            book_pages_read=cur_page_number,
                             in_progress=False,
                             played_to_completion=True,
                             long_play_complete=False,
@@ -338,9 +341,9 @@ def fix_long_play_stats_for_scrobbles(scrobbles: list) -> None:
             )
         else:
             scrobble.long_play_seconds = scrobble.playback_position_seconds
-        scrobble.book_pages_read = scrobble.calc_pages_read()
+        scrobble.log["book_pages_read"] = scrobble.calc_pages_read()
 
-        scrobble.save(update_fields=["book_pages_read", "long_play_seconds"])
+        scrobble.save(update_fields=["log", "long_play_seconds"])
 
 
 def process_koreader_sqlite_file(file_path, user_id) -> list:

+ 29 - 0
vrobbler/apps/books/management/commands/migrate_koreader_data_to_log_json.py

@@ -0,0 +1,29 @@
+import logging
+import pytz
+from datetime import datetime, timedelta
+from books.models import Book
+from django.core.management.base import BaseCommand
+from scrobbles.models import Scrobble
+from vrobbler.apps.books.koreader import fix_long_play_stats_for_scrobbles
+from vrobbler.apps.scrobbles.utils import timestamp_user_tz_to_utc
+
+
+logger = logging.getLogger(__name__)
+
+
+class Command(BaseCommand):
+    def handle(self, *args, **options):
+        total_books = Book.objects.all().count()
+        processed_books = 0
+
+        for book in Book.objects.all():
+            for scrobble in book.scrobble_set.all():
+                log_data = {
+                    "koreader_hash": scrobble.book_koreader_hash,
+                    "page_data": scrobble.book_page_data,
+                    "pages_read": scrobble.book_pages_read,
+                }
+                scrobble.log = log_data
+                scrobble.save(update_fields=["log"])
+            processed_books += 1
+            logger.info(f"Processed book {processed_books} of {total_books}")

+ 0 - 3
vrobbler/apps/scrobbles/admin.py

@@ -30,9 +30,6 @@ class ScrobbleInline(admin.TabularInline):
     exclude = (
         "scrobble_log",
         "timezone",
-        "book_koreader_hash",
-        "book_page_data",
-        "book_pages_read",
         "videogame_save_data",
         "videogame_screenshot",
     )

+ 1 - 1
vrobbler/apps/scrobbles/dataclasses.py

@@ -85,7 +85,7 @@ class LifeEventMetadata(JSONMetadata):
     participant_names: Optional[list[str]] = None
     location: Optional[str] = None
     geo_location_id: Optional[int] = None
-    details: Optional[str]
+    details: Optional[str] = None
 
     def participants(self) -> list[str]:
         participants = []

+ 24 - 0
vrobbler/apps/scrobbles/migrations/0057_alter_scrobble_log.py

@@ -0,0 +1,24 @@
+# Generated by Django 4.2.13 on 2024-08-06 15:27
+
+from django.db import migrations, models
+import scrobbles.dataclasses
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("scrobbles", "0056_scrobble_life_event_alter_scrobble_media_type"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="scrobble",
+            name="log",
+            field=models.JSONField(
+                blank=True,
+                decoder=scrobbles.dataclasses.ScrobbleMetadataDecoder,
+                encoder=scrobbles.dataclasses.ScrobbleMetadataEncoder,
+                null=True,
+            ),
+        ),
+    ]

+ 6 - 3
vrobbler/apps/scrobbles/models.py

@@ -40,6 +40,7 @@ from videogames.models import VideoGame
 from videos.models import Series, Video
 from scrobbles.dataclasses import (
     BoardGameMetadata,
+    BookMetadata,
     JSONMetadata,
     LifeEventMetadata,
     ScrobbleMetadataDecoder,
@@ -541,7 +542,7 @@ class Scrobble(TimeStampedModel):
     )
     timezone = models.CharField(max_length=50, **BNULL)
 
-    # Fields for keeping track of book data
+    # Fields for keeping track of book data DEPRECATED, remove after migration
     book_koreader_hash = models.CharField(max_length=50, **BNULL)
     book_pages_read = models.IntegerField(**BNULL)
     book_page_data = models.JSONField(**BNULL)
@@ -615,6 +616,8 @@ class Scrobble(TimeStampedModel):
             metadata_cls = BoardGameMetadata
         if self.media_type == self.MediaType.VIDEO:
             metadata_cls = VideoMetadata
+        if self.media_type == self.MediaType.BOOK:
+            metadata_cls = BookMetadata
 
         if not metadata_cls:
             logger.warn(
@@ -725,9 +728,9 @@ class Scrobble(TimeStampedModel):
 
     @property
     def session_pages_read(self) -> Optional[int]:
-        if not self.book_pages_read:
+        if not self.log.get("pages_read"):
             return 0
-        return self.book_pages_read
+        return self.log.get("pages_read")
 
     @property
     def is_long_play(self) -> bool: