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

This commit is contained in:
2024-02-10 13:18:45 -05:00
parent 40504f83e1
commit 033e3c3b35
3 changed files with 89 additions and 97 deletions

View File

@ -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)

View File

@ -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,40 +705,51 @@ 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}"
)
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
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 (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
updatable = not self.has_moved
return updatable
@property
def loc_diff(self) -> tuple:
if self.media_type != self.MediaType.GEO_LOCATION:
logger.warn("Non-location scrobble, no diff")
return tuple()
if not self.previous_by_media:
logger.warn(f"No previous location scrobble for {self}, no diff")
return tuple()
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):
media_obj = None
@ -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(

View File

@ -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()