From df62865eea16914d23aab2fbe78fcf19306eb272 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Sun, 5 Mar 2023 17:27:39 -0500 Subject: [PATCH] Fix completion for video games --- vrobbler/apps/scrobbles/admin.py | 18 ++------------- vrobbler/apps/scrobbles/models.py | 18 ++++++++++----- vrobbler/apps/scrobbles/utils.py | 10 ++++++++- vrobbler/apps/videogames/howlongtobeat.py | 4 ++++ vrobbler/apps/videogames/models.py | 27 ++++++++++++++++++++++- vrobbler/apps/videogames/utils.py | 4 ---- 6 files changed, 53 insertions(+), 28 deletions(-) diff --git a/vrobbler/apps/scrobbles/admin.py b/vrobbler/apps/scrobbles/admin.py index 5b4cfa5..92035be 100644 --- a/vrobbler/apps/scrobbles/admin.py +++ b/vrobbler/apps/scrobbles/admin.py @@ -57,14 +57,7 @@ class ChartRecordAdmin(admin.ModelAdmin): ordering = ("-created",) def media_name(self, obj): - if obj.video: - return obj.video - if obj.track: - return obj.track - if obj.podcast_episode: - return obj.podcast_episode - if obj.sport_event: - return obj.sport_event + return obj.media_obj @admin.register(Scrobble) @@ -86,6 +79,7 @@ class ScrobbleAdmin(admin.ModelAdmin): "track", "sport_event", "book", + "video_game", ) list_filter = ("is_paused", "in_progress", "source", "track__artist") ordering = ("-timestamp",) @@ -95,14 +89,6 @@ class ScrobbleAdmin(admin.ModelAdmin): def media_type(self, obj): return obj.media_obj.__class__.__name__ - if obj.video: - return "Video" - if obj.track: - return "Track" - if obj.podcast_episode: - return "Podcast" - if obj.sport_event: - return "Sport Event" def playback_percent(self, obj): return obj.percent_played diff --git a/vrobbler/apps/scrobbles/models.py b/vrobbler/apps/scrobbles/models.py index 54961f7..3ce96c6 100644 --- a/vrobbler/apps/scrobbles/models.py +++ b/vrobbler/apps/scrobbles/models.py @@ -417,7 +417,6 @@ class Scrobble(TimeStampedModel): # Fields for keeping track long content like books and games book_pages_read = models.IntegerField(**BNULL) - videogame_minutes_played = models.IntegerField(**BNULL) long_play_complete = models.BooleanField(**BNULL) def save(self, *args, **kwargs): @@ -490,6 +489,8 @@ class Scrobble(TimeStampedModel): media_obj = self.sport_event if self.book: media_obj = self.book + if self.video_game: + media_obj = self.video_game return media_obj def __str__(self): @@ -501,21 +502,26 @@ class Scrobble(TimeStampedModel): cls, media, user_id: int, scrobble_data: dict ) -> "Scrobble": - if media.__class__.__name__ == "Track": + media_class = media.__class__.__name__ + + if media_class == "Track": media_query = models.Q(track=media) scrobble_data["track_id"] = media.id - if media.__class__.__name__ == "Video": + if media_class == "Video": media_query = models.Q(video=media) scrobble_data["video_id"] = media.id - if media.__class__.__name__ == "Episode": + if media_class == "Episode": media_query = models.Q(podcast_episode=media) scrobble_data["podcast_episode_id"] = media.id - if media.__class__.__name__ == "SportEvent": + if media_class == "SportEvent": media_query = models.Q(sport_event=media) scrobble_data["sport_event_id"] = media.id - if media.__class__.__name__ == "Book": + 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 scrobble = ( cls.objects.filter( diff --git a/vrobbler/apps/scrobbles/utils.py b/vrobbler/apps/scrobbles/utils.py index c42691a..b08af70 100644 --- a/vrobbler/apps/scrobbles/utils.py +++ b/vrobbler/apps/scrobbles/utils.py @@ -112,6 +112,14 @@ def check_scrobble_for_finish( def get_scrobbles_for_media(media_obj, user: User) -> models.QuerySet: Scrobble = apps.get_model(app_label="scrobbles", model_name="Scrobble") - if media_obj.__class__.__name__ == "Book": + media_query = None + media_class = media_obj.__class__.__name__ + if media_class == "Book": media_query = models.Q(book=media_obj) + if media_class == "VideoGame": + media_query = models.Q(video_game=media_obj) + + if not media_query: + logger.warn("Do not know about media {media_class} 🙍") + return [] return Scrobble.objects.filter(media_query, user=user) diff --git a/vrobbler/apps/videogames/howlongtobeat.py b/vrobbler/apps/videogames/howlongtobeat.py index 9424b8f..8beb7ea 100644 --- a/vrobbler/apps/videogames/howlongtobeat.py +++ b/vrobbler/apps/videogames/howlongtobeat.py @@ -6,6 +6,10 @@ from howlongtobeatpy import HowLongToBeat logger = logging.getLogger(__name__) +def hrs_to_secs(hrs: float) -> int: + return int(hrs * 60 * 60) + + def lookup_game_from_hltb(name_or_id: str) -> Optional[dict]: """Lookup game on HowLongToBeat.com via HLtB ID or a name string and return the data in a dictonary mapped to our internal game fields diff --git a/vrobbler/apps/videogames/models.py b/vrobbler/apps/videogames/models.py index 44d9c2d..e8c94d2 100644 --- a/vrobbler/apps/videogames/models.py +++ b/vrobbler/apps/videogames/models.py @@ -1,16 +1,18 @@ import logging -from typing import Optional from uuid import uuid4 from django.conf import settings +from django.contrib.auth import get_user_model from django.db import models from django.urls import reverse from django_extensions.db.models import TimeStampedModel from scrobbles.mixins import ScrobblableMixin +from vrobbler.apps.scrobbles.utils import get_scrobbles_for_media logger = logging.getLogger(__name__) BNULL = {"blank": True, "null": True} +User = get_user_model() class VideoGamePlatform(TimeStampedModel): @@ -93,6 +95,24 @@ class VideoGame(ScrobblableMixin): def hltb_link(self): return f"https://howlongtobeat.com/game/{self.hltb_id}" + @property + def seconds_for_completion(self) -> int: + completion_time = self.run_time_ticks + if not completion_time: + # Default to 10 hours, why not + completion_time = 10 * 60 * 60 + return int(completion_time * (self.COMPLETION_PERCENT / 100)) + + def progress_for_user(self, user_id: int) -> int: + """Used to keep track of whether the game is complete or not""" + user = User.objects.get(id=user_id) + last_scrobble = get_scrobbles_for_media(self, user).last() + if not last_scrobble or not last_scrobble.playback_position: + logger.warn("No total minutes in last scrobble, no progress") + return 0 + sec_played = last_scrobble.playback_position * 60 + return int(sec_played / self.run_time) * 100 + def fix_metadata( self, force_update: bool = False, @@ -107,3 +127,8 @@ class VideoGame(ScrobblableMixin): if self.igdb_id: load_game_data_from_igdb(self.id) + + if (not self.run_time_ticks or force_update) and self.main_story_time: + self.run_time_ticks = self.main_story_time * 1000 # miliseconds + self.run_time = self.main_story_time + self.save(update_fields=["run_time_ticks", "run_time"]) diff --git a/vrobbler/apps/videogames/utils.py b/vrobbler/apps/videogames/utils.py index 1c73d0f..c2782cb 100644 --- a/vrobbler/apps/videogames/utils.py +++ b/vrobbler/apps/videogames/utils.py @@ -11,10 +11,6 @@ from videogames.igdb import lookup_game_from_igdb logger = logging.getLogger(__name__) -def hrs_to_secs(hrs: float) -> int: - return int(hrs * 60 * 60) - - def get_or_create_videogame( name_or_id: str, force_update=False ) -> Optional[VideoGame]: