[boardgames] Add genres and categories
This commit is contained in:
@ -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">
|
||||
|
||||
Reference in New Issue
Block a user