diff --git a/PROJECT.org b/PROJECT.org index be48a39..db1e494 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 @@ -594,6 +594,17 @@ named constants for maintainability. - ~vrobbler/apps/webpages/models.py~ (line 290) -- ="url"= - ~vrobbler/apps/scrobbles/importers/tsv.py~ (line 55) -- ="S"= completion status + +** DONE [#C] Cleaner =GeoLocationLogData= deserialization :models:refactoring: +:PROPERTIES: +:ID: 85465dbf-69b3-48cb-9df0-cd076c4470ab +:END: + +*** Description +Currently special-cases =GeoLocationLogData= by reaching into a nested ="movement_detection"= key. Should be handled at the LogData dataclass level. + +File: ~vrobbler/apps/scrobbles/models.py~ (line 977) + ** DONE [#B] Webpage scrobbles should diff existing webpages content :webpages:metadata: :PROPERTIES: :ID: 25576197-258f-48d6-bfe9-e4172a0a1898 diff --git a/vrobbler/apps/locations/models.py b/vrobbler/apps/locations/models.py index 12efd3a..75dc496 100644 --- a/vrobbler/apps/locations/models.py +++ b/vrobbler/apps/locations/models.py @@ -28,6 +28,14 @@ class GeoLocationLogData(BaseLogData, WithPeopleLogData): activity: str = "" detected_at: str = "" + @classmethod + def from_log_dict(cls, log_dict: dict) -> dict: + instance_data = log_dict.get("movement_detection", {}).copy() + for field_name in ["description", "notes", "with_people_ids"]: + if field_name in log_dict: + instance_data[field_name] = log_dict[field_name] + return instance_data + class GeoLocation(ScrobblableMixin): COMPLETION_PERCENT = getattr(settings, "LOCATION_COMPLETION_PERCENT", 100) diff --git a/vrobbler/apps/scrobbles/dataclasses.py b/vrobbler/apps/scrobbles/dataclasses.py index c606a37..67c201c 100644 --- a/vrobbler/apps/scrobbles/dataclasses.py +++ b/vrobbler/apps/scrobbles/dataclasses.py @@ -50,6 +50,18 @@ class BaseLogData(JSONDataclass): def override_fields(cls) -> dict: return {} + @classmethod + def from_log_dict(cls, log_dict: dict) -> dict: + """Extract LogData keyword arguments from a stored log dict. + + Override in subclasses to handle custom nesting/structure. + """ + return { + k: v + for k, v in log_dict.items() + if k in cls.__dataclass_fields__ + } + def notes_as_str(self, separator: str = " | ") -> str: import html import re diff --git a/vrobbler/apps/scrobbles/models.py b/vrobbler/apps/scrobbles/models.py index bc22ef0..452c801 100644 --- a/vrobbler/apps/scrobbles/models.py +++ b/vrobbler/apps/scrobbles/models.py @@ -973,24 +973,8 @@ class Scrobble(TimeStampedModel): if not log_dict: log_dict = {} - # Special handling for GeoLocationLogData - data is nested under 'movement_detection' - # TODO there's a better way to fix this this at the LogData level - if logdata_cls.__name__ == "GeoLocationLogData": - instance_data = log_dict.get("movement_detection", {}).copy() - # Add top-level fields that GeoLocationLogData expects from BaseLogData/WithPeopleLogData - for field_name in ["description", "notes", "with_people_ids"]: - if field_name in log_dict: - instance_data[field_name] = log_dict[field_name] - try: - return logdata_cls(**instance_data) - except Exception as e: - logger.warning("Log data could not be loaded", e) - return logdata_cls() - - # Strip log-only keys (stored in JSONField but not part of LogData dataclass) - logdata_kwargs = { - k: v for k, v in log_dict.items() if k in logdata_cls().__dataclass_fields__ - } + # Use LogData's from_log_dict to handle any custom nesting/structure + logdata_kwargs = logdata_cls.from_log_dict(log_dict) try: return logdata_cls(**logdata_kwargs)