Compare commits

...

5 Commits
18.2 ... 18.3

6 changed files with 95 additions and 47 deletions

View File

@ -7,6 +7,7 @@ from profiles.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin): class UserProfileAdmin(admin.ModelAdmin):
date_hierarchy = "created" date_hierarchy = "created"
ordering = ("-created",) ordering = ("-created",)
readonly_fields = ("timezone_change_log",)
exclude = ( exclude = (
"twitch_token", "twitch_token",
"twitch_client_secret", "twitch_client_secret",

View File

@ -16,6 +16,7 @@ BNULL = {"blank": True, "null": True}
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class UserProfile(TimeStampedModel): class UserProfile(TimeStampedModel):
user = models.OneToOneField( user = models.OneToOneField(
User, on_delete=models.CASCADE, related_name="profile" User, on_delete=models.CASCADE, related_name="profile"
@ -72,7 +73,10 @@ class UserProfile(TimeStampedModel):
old_instance = UserProfile.objects.get(pk=self.pk) old_instance = UserProfile.objects.get(pk=self.pk)
is_timezone_change = self.timezone != old_instance.timezone is_timezone_change = self.timezone != old_instance.timezone
if is_timezone_change: if is_timezone_change:
logger.info("Updating timezone changelog for user", extra={"profile_id": self.id}) logger.info(
"Updating timezone changelog for user",
extra={"profile_id": self.id},
)
previous_changes = old_instance.timezone_change_log previous_changes = old_instance.timezone_change_log
now = timezone.now().replace(microsecond=0) now = timezone.now().replace(microsecond=0)
new_log = f"{self.timezone} - {now}" new_log = f"{self.timezone} - {now}"

View File

@ -62,20 +62,20 @@ def start_of_year(dt, profile) -> datetime:
def one_off_fix_colins_profile(profile): def one_off_fix_colins_profile(profile):
home_tz = "America/New_York" home_tz = "America/New_York"
europe = "2022-10-15" europe = "2022-10-15 06:00:00"
europe_end = "2023-12-16 12:00:00"
europe_tz = "Europe/Paris" europe_tz = "Europe/Paris"
europe_end = "2023-12-15"
washington = "2023-04-28" washington = "2023-04-28 06:00:00"
washington_end = "2023-05-04 12:00:00"
washington_tz = "America/Los_Angeles" washington_tz = "America/Los_Angeles"
washington_end = "2023-05-04"
camp = "2024-08-04" camp = "2024-08-04 17:00:00"
camp_end = "2024-08-10" camp_end = "2024-08-10 12:00:00"
camp_tz = "America/Halifax" camp_tz = "America/Halifax"
summer = "2025-07-09 12:00:00" summer = "2025-07-09 06:00:00"
summer_end = "2025-07-13 20:30:00" summer_end = "2025-07-11 23:30:00"
summer_tz = "America/Los_Angeles" summer_tz = "America/Los_Angeles"
profile.timezone_change_log = "" profile.timezone_change_log = ""

View File

@ -50,20 +50,22 @@ class LastFM:
enrich=True, enrich=True,
) )
timezone = settings.TIME_ZONE timestamp = self.vrobbler_user.profile.get_timestamp_with_tz(
if self.vrobbler_user.profile: lfm_scrobble.get("timestamp")
timezone = self.vrobbler_user.profile.timezone )
stop_timestamp = timestamp + timedelta(
timestamp = lfm_scrobble.get("timestamp") seconds=track.run_time_seconds
)
new_scrobble = Scrobble( new_scrobble = Scrobble(
user=self.vrobbler_user, user=self.vrobbler_user,
timestamp=timestamp, timestamp=timestamp,
stop_timestamp=stop_timestamp,
source=source, source=source,
track=track, track=track,
timezone=timezone,
played_to_completion=True, played_to_completion=True,
in_progress=False, in_progress=False,
media_type=Scrobble.MediaType.TRACK, media_type=Scrobble.MediaType.TRACK,
timezone=timestamp.tzinfo.name,
) )
# Vrobbler scrobbles on finish, LastFM scrobbles on start # Vrobbler scrobbles on finish, LastFM scrobbles on start
seconds_eariler = timestamp - timedelta(seconds=20) seconds_eariler = timestamp - timedelta(seconds=20)

View File

@ -1087,6 +1087,7 @@ class Scrobble(TimeStampedModel):
key = media_class_to_foreign_key(media.__class__.__name__) key = media_class_to_foreign_key(media.__class__.__name__)
media_query = models.Q(**{key: media}) media_query = models.Q(**{key: media})
scrobble_data[key + "_id"] = media.id scrobble_data[key + "_id"] = media.id
skip_in_progress_check = kwargs.get("skip_in_progress_check", False)
# Find our last scrobble of this media item (track, video, etc) # Find our last scrobble of this media item (track, video, etc)
scrobble = ( scrobble = (
@ -1110,6 +1111,7 @@ class Scrobble(TimeStampedModel):
) )
return scrobble return scrobble
if not skip_in_progress_check:
logger.info( logger.info(
f"[create_or_update] check for existing scrobble to update ", f"[create_or_update] check for existing scrobble to update ",
extra={ extra={
@ -1119,7 +1121,9 @@ class Scrobble(TimeStampedModel):
"scrobble_data": scrobble_data, "scrobble_data": scrobble_data,
}, },
) )
scrobble_data["playback_status"] = scrobble_data.pop("status", None) scrobble_data["playback_status"] = scrobble_data.pop(
"status", None
)
# If it's marked as stopped, send it through our update mechanism, which will complete it # If it's marked as stopped, send it through our update mechanism, which will complete it
if scrobble and ( if scrobble and (
scrobble.can_be_updated scrobble.can_be_updated
@ -1131,6 +1135,7 @@ class Scrobble(TimeStampedModel):
# Discard status before creating # Discard status before creating
scrobble_data.pop("playback_status") scrobble_data.pop("playback_status")
logger.info( logger.info(
f"[scrobbling] creating new scrobble", f"[scrobbling] creating new scrobble",
extra={ extra={

View File

@ -2,6 +2,7 @@ import logging
import re import re
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Optional from typing import Any, Optional
from zoneinfo import ZoneInfo
import pendulum import pendulum
import pytz import pytz
@ -18,6 +19,7 @@ from music.models import Track
from people.models import Person from people.models import Person
from podcasts.models import PodcastEpisode from podcasts.models import PodcastEpisode
from podcasts.utils import parse_mopidy_uri from podcasts.utils import parse_mopidy_uri
from profiles.models import UserProfile
from puzzles.models import Puzzle from puzzles.models import Puzzle
from scrobbles.constants import ( from scrobbles.constants import (
JELLYFIN_AUDIO_ITEM_TYPES, JELLYFIN_AUDIO_ITEM_TYPES,
@ -390,10 +392,25 @@ def email_scrobble_board_game(
locations[location_dict.get("id")] = location locations[location_dict.get("id")] = location
scrobbles_created = [] scrobbles_created = []
second = 0
for play_dict in bgstat_data.get("plays", []): for play_dict in bgstat_data.get("plays", []):
hour = None
minute = None
second = None
if "comments" in play_dict.keys(): if "comments" in play_dict.keys():
if "Learning to play" in play_dict.get("comments"): for line in play_dict.get("comments", "").split("\n"):
if "Learning to play" in line:
log_data["learning"] = True log_data["learning"] = True
if "Start time:" in line:
start_time = line.split(": ")[1]
pieces = start_time.split(":")
hour = int(pieces[0])
minute = int(pieces[1])
try:
second = int(pieces[2])
except IndexError:
second = 0
log_data["details"] = play_dict.get("comments") log_data["details"] = play_dict.get("comments")
log_data["expansion_ids"] = [] log_data["expansion_ids"] = []
try: try:
@ -402,7 +419,6 @@ def email_scrobble_board_game(
try: try:
base_game = expansions[play_dict.get("gameRefId")] base_game = expansions[play_dict.get("gameRefId")]
except KeyError: except KeyError:
print(play_dict)
logger.info( logger.info(
"Skipping scrobble of play, can't find game", "Skipping scrobble of play, can't find game",
extra={"play_dict": play_dict}, extra={"play_dict": play_dict},
@ -440,22 +456,39 @@ def email_scrobble_board_game(
} }
) )
start = parse(play_dict.get("playDate")) timestamp = parse(play_dict.get("playDate"))
if hour and minute:
logger.info(f"Scrobble playDate has manual start time {timestamp}")
timestamp = timestamp.replace(
hour=hour, minute=minute, second=second or 0
)
logger.info(f"Update to {timestamp}")
profile = UserProfile.objects.filter(user_id=user_id).first()
timestamp = profile.get_timestamp_with_tz(timestamp)
if play_dict.get("durationMin") > 0: if play_dict.get("durationMin") > 0:
duration_seconds = play_dict.get("durationMin") * 60 duration_seconds = play_dict.get("durationMin") * 60
else: else:
duration_seconds = base_game.run_time_seconds duration_seconds = base_game.run_time_seconds
stop = start + timedelta(seconds=duration_seconds) stop_timestamp = timestamp + timedelta(seconds=duration_seconds)
logger.info(f"Creating scrobble for {base_game} at {timestamp}")
scrobble_dict = { scrobble_dict = {
"user_id": user_id, "user_id": user_id,
"timestamp": start, "timestamp": timestamp,
"playback_position_seconds": duration_seconds, "playback_position_seconds": duration_seconds,
"source": "BG Stats", "source": "BG Stats",
"log": log_data, "log": log_data,
} }
scrobble = None
if timestamp.year > 2023:
logger.info(
"Scrobbles older than 2024 likely have no time associated just create it"
)
scrobble = Scrobble.objects.filter( scrobble = Scrobble.objects.filter(
board_game=base_game, user_id=user_id, timestamp=start board_game=base_game, user_id=user_id, timestamp=timestamp
).first() ).first()
if scrobble: if scrobble:
logger.info( logger.info(
@ -463,8 +496,11 @@ def email_scrobble_board_game(
extra={"scrobble_dict": scrobble_dict, "user_id": user_id}, extra={"scrobble_dict": scrobble_dict, "user_id": user_id},
) )
continue continue
scrobble = Scrobble.create_or_update(base_game, user_id, scrobble_dict) scrobble = Scrobble.create_or_update(
scrobble.stop_timestamp = stop base_game, user_id, scrobble_dict, skip_in_progress_check=True
)
scrobble.timezone = timestamp.tzinfo.name
scrobble.stop_timestamp = stop_timestamp
scrobble.in_progress = False scrobble.in_progress = False
scrobble.played_to_completion = True scrobble.played_to_completion = True
scrobble.save() scrobble.save()