From 146b3332f4978f622357f6a80907864cf825db4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Hells=C3=A9n?= Date: Tue, 6 Jan 2026 23:31:24 +0100 Subject: [PATCH] Fix game data not updating --- .../commands/better_import_drops.py | 41 ++++++++++--- twitch/tests/test_better_import_drops.py | 59 +++++++++++++++++++ 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/twitch/management/commands/better_import_drops.py b/twitch/management/commands/better_import_drops.py index 7712078..48ce75d 100644 --- a/twitch/management/commands/better_import_drops.py +++ b/twitch/management/commands/better_import_drops.py @@ -446,22 +446,45 @@ class Command(BaseCommand): if game_data.twitch_id in self.game_cache: game_obj: Game = self.game_cache[game_data.twitch_id] - # Maintenance: Ensure the existing game is linked to the - # correct owner (Sometimes games are imported without owner - # data first). Use owner_id to avoid triggering a query. - # Correct stale owner linkage that may exist from earlier - # partial imports. - if game_obj.owner_id != org_obj.pk: # type: ignore[attr-defined] # Django adds _id suffix for FK fields + update_fields: list[str] = [] + + # Ensure owner is correct without triggering a read + if game_obj.owner_id != org_obj.pk: # type: ignore[attr-defined] game_obj.owner = org_obj - game_obj.save(update_fields=["owner"]) + update_fields.append("owner") + + # Persist normalized display name when provided + if game_data.display_name and game_obj.display_name != game_data.display_name: + game_obj.display_name = game_data.display_name + update_fields.append("display_name") + + # Persist canonical name when provided (Inventory format) + if game_data.name and game_obj.name != game_data.name: + game_obj.name = game_data.name + update_fields.append("name") + + # Persist slug when provided by API (Inventory and DropCampaignDetails) + if game_data.slug is not None and game_obj.slug != (game_data.slug or ""): + game_obj.slug = game_data.slug or "" + update_fields.append("slug") + + # Persist box art URL when provided + if game_data.box_art_url is not None and game_obj.box_art != (game_data.box_art_url or ""): + game_obj.box_art = game_data.box_art_url or "" + update_fields.append("box_art") + + if update_fields: + game_obj.save(update_fields=update_fields) return game_obj game_obj, created = Game.objects.update_or_create( twitch_id=game_data.twitch_id, defaults={ - "display_name": game_data.display_name, - "box_art": game_data.box_art_url, + "display_name": game_data.display_name or (game_data.name or ""), + "name": game_data.name or "", + "slug": game_data.slug or "", + "box_art": game_data.box_art_url or "", "owner": org_obj, }, ) diff --git a/twitch/tests/test_better_import_drops.py b/twitch/tests/test_better_import_drops.py index ea8fdf3..0be6036 100644 --- a/twitch/tests/test_better_import_drops.py +++ b/twitch/tests/test_better_import_drops.py @@ -8,6 +8,7 @@ from django.test import TestCase from twitch.management.commands.better_import_drops import Command from twitch.models import Channel from twitch.models import DropCampaign +from twitch.models import Game from twitch.schemas import DropBenefitSchema if TYPE_CHECKING: @@ -481,3 +482,61 @@ class OperationNameFilteringTests(TestCase): # Cross-check: Inventory campaign should not be in viewer campaigns assert not viewer_campaigns.filter(twitch_id="inventory-campaign-1").exists() assert not inventory_campaigns.filter(twitch_id="viewer-campaign-1").exists() + + +class GameImportTests(TestCase): + """Tests for importing and persisting Game fields from campaign data.""" + + def test_imports_game_slug_from_campaign(self) -> None: + """Ensure Game.slug is imported from DropCampaign game data when provided.""" + command = Command() + command.pre_fill_cache() + + payload: dict[str, object] = { + "data": { + "user": { + "id": "17658559", + "dropCampaign": { + "id": "campaign-with-slug", + "name": "Slug Campaign", + "description": "", + "startAt": "2025-01-01T00:00:00Z", + "endAt": "2025-12-31T23:59:59Z", + "accountLinkURL": "https://example.com/link", + "detailsURL": "https://example.com/details", + "imageURL": "", + "status": "ACTIVE", + "self": {"isAccountConnected": True, "__typename": "DropCampaignSelfEdge"}, + "game": { + "id": "497057", + "slug": "destiny-2", + "displayName": "Destiny 2", + "boxArtURL": "https://example.com/boxart.png", + "__typename": "Game", + }, + "owner": { + "id": "bungie-org", + "name": "Bungie", + "__typename": "Organization", + }, + "timeBasedDrops": [], + "__typename": "DropCampaign", + }, + "__typename": "User", + }, + }, + "extensions": {"operationName": "DropCampaignDetails"}, + } + + success, broken_dir = command.process_responses( + responses=[payload], + file_path=Path("slug.json"), + options={}, + ) + + assert success is True + assert broken_dir is None + + game = Game.objects.get(twitch_id="497057") + assert game.slug == "destiny-2" + assert game.display_name == "Destiny 2"