This commit has no recorded session.
diff
commit d78529efe274a869db0ce78c4ffc8191f07cf505Author: Colin Powell <colin@unbl.ink>Date: Sat Jul 4 11:41:24 2026 -0400 [boardgames] Add genres and categories@@ -619,7 +619,6 @@ The Edit log form should have from top to bottom: - Expansion ids (which should a multi-select widget of expansions for this game) - Location (which should be a drop down of BoardGameLocations for this user) -** TODO Board games should have genres extracted from family data :boardgames:metadata: ** TODO Fix bug in fetching expansions for board games :boardgames: :PROPERTIES: :ID: 17995312-e76e-4a50-b591-0eab78cb59ab@@ -643,6 +642,11 @@ The Edit log form should have from top to bottom: boardgamegeek.exceptions.BGGApiError: invalid data for game id: 242117 #+end_src +** TODO Board games should have genres extracted from family data :boardgames:metadata:+:PROPERTIES:+:ID: 7214b270-dccc-4b98-ac58-ff4f76c8cda9+:END:+ ** DONE Exclude some board games from auto-expansion imports :boardgames: :PROPERTIES: :ID: 51ffdf20-e732-4774-b781-c3501d26d46f@@ -0,0 +1,81 @@+import logging+import time++from django.core.management.base import BaseCommand++logger = logging.getLogger(__name__)+++class Command(BaseCommand):+ help = "Refresh board game metadata from BGG (categories→genres, families→tags)"++ def add_arguments(self, parser):+ parser.add_argument(+ "--commit",+ action="store_true",+ help="Persist changes to the database",+ )+ parser.add_argument(+ "--force",+ action="store_true",+ help="Update all games even if they already have a published_date",+ )+ parser.add_argument(+ "--batch-size",+ type=int,+ default=50,+ help="Number of games to process per batch (default: 50)",+ )+ parser.add_argument(+ "--sleep",+ type=float,+ default=1.0,+ help="Seconds to sleep between API calls (default: 1.0)",+ )++ def handle(self, *args, **options):+ from boardgames.models import BoardGame++ commit = options["commit"]+ force = options["force"]+ batch_size = options["batch_size"]+ sleep_secs = options["sleep"]++ qs = BoardGame.objects.exclude(bggeek_id__isnull=True).exclude(bggeek_id="")+ total = qs.count()+ self.stdout.write(f"Found {total} board games with BGG IDs")++ if not commit:+ self.stdout.write(+ "Dry run — no API calls will be made. Use --commit to run lookups."+ )+ return++ enriched = 0+ skipped = 0++ for batch_num, offset in enumerate(range(0, total, batch_size)):+ batch = qs[offset : offset + batch_size]+ for game in batch:+ try:+ game.fix_metadata(force_update=force)+ enriched += 1+ except Exception as e:+ self.stdout.write(+ self.style.WARNING(+ f" [SKIPPED] {game.title} (BGG {game.bggeek_id}): {e}"+ )+ )+ skipped += 1+ time.sleep(sleep_secs)++ self.stdout.write(+ f" Batch {batch_num + 1}: {offset + len(batch)}/{total} — "+ f"enriched: {enriched}, skipped: {skipped}"+ )++ self.stdout.write(+ f"\nResults:\n"+ f" Games enriched: {enriched}\n"+ f" Games skipped: {skipped}"+ )@@ -351,8 +351,9 @@ class BoardGame(ScrobblableMixin): data.pop("max_players") # Pop extra BGG metadata that isn't a model field+ categories = data.pop("categories", [])+ families = data.pop("families", []) data.pop("mechanics", None)- data.pop("categories", None) data.pop("designers", None) data.pop("publishers", None) data.pop("publisher", None)@@ -368,6 +369,11 @@ class BoardGame(ScrobblableMixin): ) = BoardGamePublisher.objects.get_or_create(name=publisher_name) self.save() + for cat in categories:+ self.genre.add(cat.strip())+ for fam in families:+ self.tags.add(fam.strip())+ # Go get cover image if the URL is present if cover_url and not self.cover: self.save_image_from_url(cover_url)@@ -417,6 +423,7 @@ class BoardGame(ScrobblableMixin): mechanics = bgg_data.pop("mechanics", []) designers = bgg_data.pop("designers", []) categories = bgg_data.pop("categories", [])+ families = bgg_data.pop("families", []) publishers = bgg_data.pop("publishers", []) publisher = bgg_data.pop("publisher", []) cover_url = bgg_data.pop("cover_url") or ""@@ -459,6 +466,11 @@ class BoardGame(ScrobblableMixin): publisher, _ = BoardGamePublisher.objects.get_or_create(name=name) game.publishers.add(publisher) + for cat in categories:+ game.genre.add(cat.strip())+ for fam in families:+ game.tags.add(fam.strip())+ if expansions and not game.skip_expansions: if defer_expansions: from boardgames.tasks import fetch_board_game_expansions@@ -32,6 +32,7 @@ def lookup_boardgame_from_bgg( ) game_dict["mechanics"] = game.mechanics game_dict["categories"] = game.categories+ game_dict["families"] = game.families game_dict["designers"] = game.designers game_dict["publishers"] = game.publishers if game.publishers:@@ -83,6 +83,7 @@ def _mock_bgg_game(bggeek_id, title, expansions=None): playingtime = 60 mechanics = [] categories = []+ families = [] designers = [] publishers = [] @@ -46,6 +46,20 @@ <p> <a href="{{object.start_url}}">Play again</a> </p>+ {% if object.genre.all %}+ <p>Genres:+ {% for tag in object.genre.all %}+ <a href="{% url 'boardgames:boardgame_list' %}?genre={{ tag.name|urlencode }}" class="badge bg-secondary text-decoration-none">{{ tag.name }}</a>+ {% endfor %}+ </p>+ {% endif %}+ {% if object.tags.all %}+ <p>Tags:+ {% for tag in object.tags.all %}+ <a href="{% url 'boardgames:boardgame_list' %}?tag={{ tag.name|urlencode }}" class="badge bg-secondary text-decoration-none">{{ tag.name }}</a>+ {% endfor %}+ </p>+ {% endif %} </div> {% if charts %} <div class="row">