Bläddra i källkod

[locations] Try another tack for whether we've moved or not

Colin Powell 1 år sedan
förälder
incheckning
033e3c3b35
3 ändrade filer med 87 tillägg och 95 borttagningar
  1. 0 15
      vrobbler/apps/locations/models.py
  2. 74 79
      vrobbler/apps/scrobbles/models.py
  3. 13 1
      vrobbler/apps/scrobbles/utils.py

+ 0 - 15
vrobbler/apps/locations/models.py

@@ -51,21 +51,6 @@ class GeoLocation(ScrobblableMixin):
             logger.error("No lat or lon keys in data dict")
             return
 
-        int_lat, r_lat = str(data_dict.get("lat", "")).split(".")
-        int_lon, r_lon = str(data_dict.get("lon", "")).split(".")
-
-        try:
-            trunc_lat = r_lat[0:GEOLOC_ACCURACY]
-        except IndexError:
-            trunc_lat = r_lat
-        try:
-            trunc_lon = r_lon[0:GEOLOC_ACCURACY]
-        except IndexError:
-            trunc_lon = r_lon
-
-        data_dict["lat"] = float(f"{int_lat}.{trunc_lat}")
-        data_dict["lon"] = float(f"{int_lon}.{trunc_lon}")
-
         int_alt, r_alt = str(data_dict.get("alt", "")).split(".")
 
         data_dict["altitude"] = float(int_alt)

+ 74 - 79
vrobbler/apps/scrobbles/models.py

@@ -33,6 +33,7 @@ from scrobbles.stats import build_charts
 from scrobbles.utils import (
     check_long_play_for_finish,
     check_scrobble_for_finish,
+    media_class_to_foreign_key,
 )
 from sports.models import SportEvent
 from videogames import retroarch
@@ -476,6 +477,15 @@ class Scrobble(TimeStampedModel):
         GEO_LOCATION = "GeoLocation", "GeoLocation"
         WEBPAGE = "WebPage", "Web Page"
 
+    media_ids = {
+        "Track": "track_id",
+        "Video": "video_id",
+        "Episode": "episode_id",
+        "SportEvent": "spoort_event_id",
+        "Track": "track_id",
+        "Track": "track_id",
+    }
+
     uuid = models.UUIDField(editable=False, **BNULL)
     video = models.ForeignKey(Video, on_delete=models.DO_NOTHING, **BNULL)
     track = models.ForeignKey(Track, on_delete=models.DO_NOTHING, **BNULL)
@@ -551,10 +561,16 @@ class Scrobble(TimeStampedModel):
         if not self.uuid:
             self.uuid = uuid4()
 
+        # Microseconds mess up Django's filtering, and we don't need be that specific
+        self.timestamp = self.timestamp.replace(microsecond=0)
         self.media_type = self.MediaType(self.media_obj.__class__.__name__)
 
         return super(Scrobble, self).save(*args, **kwargs)
 
+    @property
+    def scrobble_media_key(self) -> str:
+        return media_class_to_foreign_key(self.media_type) + "_id"
+
     @property
     def status(self) -> str:
         if self.is_paused:
@@ -592,25 +608,31 @@ class Scrobble(TimeStampedModel):
         )
 
     @property
-    def previous_all(self) -> "Scrobble":
+    def previous_by_media(self) -> "Scrobble":
         return (
-            Scrobble.objects.filter(media_type=self.media_type)
+            Scrobble.objects.filter(
+                media_type=self.media_type,
+                user=self.user,
+                timestamp__lt=self.timestamp,
+            )
             .order_by("-timestamp")
-            .filter(timestamp__lt=self.timestamp)
             .first()
         )
 
     @property
-    def next_all(self) -> "Scrobble":
+    def next_by_media(self) -> "Scrobble":
         return (
-            Scrobble.objects.filter(media_type=self.media_type)
+            Scrobble.objects.filter(
+                media_type=self.media_type,
+                user=self.user,
+                timestamp__gt=self.timestamp,
+            )
             .order_by("-timestamp")
-            .filter(timestamp__gt=self.timestamp)
             .first()
         )
 
     @property
-    def previous_all_media(self) -> "Scrobble":
+    def previous_by_user(self) -> "Scrobble":
         return (
             Scrobble.objects.order_by("-timestamp")
             .filter(timestamp__lt=self.timestamp)
@@ -618,7 +640,7 @@ class Scrobble(TimeStampedModel):
         )
 
     @property
-    def next_all_media(self) -> "Scrobble":
+    def next_by_user(self) -> "Scrobble":
         return (
             Scrobble.objects.order_by("-timestamp")
             .filter(timestamp__gt=self.timestamp)
@@ -683,39 +705,50 @@ class Scrobble(TimeStampedModel):
             logger.info(f"No - stale - {self.id} - {self.source}")
             updatable = False
         if self.media_obj.__class__.__name__ in ["GeoLocation"]:
-            if self.previous_all:
-                last_location = self.previous_all.media_obj
-                logger.info(
-                    f"Calculate proximity to last scrobble: {last_location}"
-                )
+            updatable = not self.has_moved
+        return updatable
 
-                same_lat = last_location.lat == self.media_obj.lat
-                same_lon = last_location.lon == self.media_obj.lon
-                same_title = False
-                if self.media_obj.title:
-                    same_title = last_location.title == self.media_obj.title
+    @property
+    def loc_diff(self) -> tuple:
+        if self.media_type != self.MediaType.GEO_LOCATION:
+            logger.warn("Non-location scrobble, no diff")
+            return tuple()
 
-                logger.info(f"{self.timestamp}")
-                logger.info(
-                    f"Last lat: {last_location.lat}, last long {last_location.lon}, last title {last_location.title}"
-                )
-                logger.info(
-                    f"Our lat {self.media_obj.lat}, Our lon {self.media_obj.lon} or our title {self.media_obj.title}"
-                )
+        if not self.previous_by_media:
+            logger.warn(f"No previous location scrobble for {self},  no diff")
+            return tuple()
 
-                if (same_lat and same_lon) or same_title:
-                    logger.info(
-                        f"Yes - We're in the same place: {self.media_obj}"
-                    )
-                    updatable = True
-                else:
-                    logger.info(
-                        f"No - We've moved, start a new scrobble: {self.media_obj}"
-                    )
-                    # Stop the previous location scrobble
-                    self.previous_all.stop()
-                    updatable = False
-        return updatable
+        if self.media_obj == self.previous_by_media.media_obj:
+            logger.warn("Previous scrobble is same location, no diff")
+            return tuple()
+
+        return (
+            abs(self.media_obj.lat - self.previous_by_media.media_obj.lat),
+            abs(self.media_obj.lon - self.previous_by_media.media_obj.lon),
+        )
+
+    @property
+    def has_moved(self) -> bool:
+        has_moved = False
+
+        if self.media_type != self.MediaType.GEO_LOCATION:
+            logger.warn("Non-location scrobble means nothing")
+            return has_moved
+
+        scrobble = self
+        all_moves = []
+        for i in range(3):
+            loc_diff = self.loc_diff
+            if loc_diff and loc_diff[0] < 0.001 and loc_diff[1] > 0.001:
+                all_moves.append(True)
+            else:
+                all_moves.append(False)
+            scrobble = self.previous_by_media
+
+        if not False in all_moves:
+            has_moved = True
+
+        return has_moved
 
     @property
     def media_obj(self):
@@ -763,47 +796,9 @@ class Scrobble(TimeStampedModel):
     def create_or_update(
         cls, media, user_id: int, scrobble_data: dict, **kwargs
     ) -> "Scrobble":
-
-        media_class = media.__class__.__name__
-        dup = None
-
-        media_query = models.Q(track=media)
-        if media_class == "Track":
-            scrobble_data["track_id"] = media.id
-        if media_class == "Video":
-            media_query = models.Q(video=media)
-            scrobble_data["video_id"] = media.id
-        if media_class == "Episode":
-            media_query = models.Q(podcast_episode=media)
-            scrobble_data["podcast_episode_id"] = media.id
-        if media_class == "SportEvent":
-            media_query = models.Q(sport_event=media)
-            scrobble_data["sport_event_id"] = media.id
-        if media_class == "Book":
-            media_query = models.Q(book=media)
-            scrobble_data["book_id"] = media.id
-        if media_class == "VideoGame":
-            media_query = models.Q(video_game=media)
-            scrobble_data["video_game_id"] = media.id
-        if media_class == "BoardGame":
-            media_query = models.Q(board_game=media)
-            scrobble_data["board_game_id"] = media.id
-        if media_class == "WebPage":
-            media_query = models.Q(webpage=media)
-            scrobble_data["webpage_id"] = media.id
-        if media_class == "GeoLocation":
-            media_query = models.Q(media_type=Scrobble.MediaType.GEO_LOCATION)
-            scrobble_data["geo_location_id"] = media.id
-            dup = cls.objects.filter(
-                media_type=cls.MediaType.GEO_LOCATION,
-                timestamp=scrobble_data.get("timestamp"),
-            ).first()
-
-            if dup:
-                logger.info(
-                    "[scrobbling] scrobble for geo location with identical timestamp found"
-                )
-                return dup
+        key = media_class_to_foreign_key(media.__class__.__name__)
+        media_query = models.Q(**{key: media})
+        scrobble_data[key + "_id"] = media.id
 
         scrobble = (
             cls.objects.filter(

+ 13 - 1
vrobbler/apps/scrobbles/utils.py

@@ -1,5 +1,6 @@
 import logging
 import os
+import re
 from datetime import datetime, timedelta, tzinfo
 from urllib.parse import unquote
 
@@ -20,6 +21,7 @@ User = get_user_model()
 
 PODCAST_DATE_FORMAT = "YYYY-MM-DD"
 
+
 def timestamp_user_tz_to_utc(timestamp: int, user_tz: tzinfo) -> datetime:
     return user_tz.localize(datetime.utcfromtimestamp(timestamp)).astimezone(
         pytz.utc
@@ -42,6 +44,7 @@ def convert_to_seconds(run_time: str) -> int:
         run_time_int = int((((hours * 60) + minutes) * 60) + seconds)
     return run_time_int
 
+
 def parse_mopidy_uri(uri: str) -> dict:
     logger.debug(f"Parsing URI: {uri}")
     parsed_uri = os.path.splitext(unquote(uri))[0].split("/")
@@ -60,7 +63,12 @@ def parse_mopidy_uri(uri: str) -> dict:
 
         try:
             # Beacuse we have epsiode numbers on
-            pub_date = parse(episode_str[episode_num_pad:len(PODCAST_DATE_FORMAT) + episode_num_pad])
+            pub_date = parse(
+                episode_str[
+                    episode_num_pad : len(PODCAST_DATE_FORMAT)
+                    + episode_num_pad
+                ]
+            )
         except ParserError:
             pub_date = ""
 
@@ -264,3 +272,7 @@ def delete_zombie_scrobbles(dry_run=True):
         f"Found {zombies_found} zombie scrobbles to delete, use dry_run=False to proceed"
     )
     return zombies_found
+
+
+def media_class_to_foreign_key(media_class: str) -> str:
+    return re.sub(r"(?<!^)(?=[A-Z])", "_", media_class).lower()