-
Notifications
You must be signed in to change notification settings - Fork 0
Games ~ Creating Fixtures Using BoardGameGeek API
Will Cravitz edited this page Nov 25, 2023
·
1 revision
Below is code to call the BGG API for information on games and to create corresponding fixtures. The output of running this script will be a file called games-fixtures-<fixture_size>.json
. This file should be placed in /src/chigame/games/fixtures
.
This code was initially written in issue #98 and updated in issue #283 to parse special XML characters.
import requests
import xml.etree.ElementTree as ET
import xml.sax.saxutils
import json
def bgg_get_game_details(bgg_ids):
BGG_BASE_URL = "https://www.boardgamegeek.com/xmlapi2/"
ids_string = ",".join(map(str, bgg_ids))
details_url = f"{BGG_BASE_URL}thing?id={ids_string}&stats=1"
details_response = requests.get(details_url)
details_text = xml.sax.saxutils.unescape(details_response.text)
try:
details_root = ET.fromstring(details_text)
# Skip the batch if it cannot be parsed after escaping special characters
except:
return []
games_data = []
for item in details_root.findall("item"):
# Extract game details as before, but for each item
name_element = item.find(".//name[@type='primary']")
if name_element is None:
continue # Skip games without a name
# Retrieve the complexity value from the API response, convert it to a float,
# and round it to two decimal places. If the value is not found, default to None.
if item.find(".//averageweight") is not None:
complexity_value = item.find(".//averageweight").get("value")
rounded_complexity = (
round(float(complexity_value), 2) if complexity_value else None
)
else:
rounded_complexity = None
# Construct a dictionary with the game's details, parsing various elements from the API response
game_data = {
"BGG_id": item.get("id"),
"name": item.find(".//name").get("value"),
"image": item.find(".//image").text
if item.find(".//image") is not None
else "/static/images/no_picture_available.png",
"description": item.find(".//description").text,
"year_published": int(item.find(".//yearpublished").get("value"))
if item.find(".//yearpublished") is not None
else None,
"min_players": item.find(".//minplayers").get("value"),
"max_players": item.find(".//maxplayers").get("value"),
"expected_playtime": item.find(".//playingtime").get("value"),
"min_playtime": item.find(".//minplaytime").get("value"),
"max_playtime": item.find(".//maxplaytime").get("value"),
"suggested_age": item.find(".//minage").get("value"),
"complexity": rounded_complexity, # The rounded complexity rating of the game
# Missing fields: category, mechanics
}
games_data.append(game_data)
return games_data
def create_fixture_file(num_entries, filename="fixture_file.json", batch_size=10):
fixtures = []
fetched_games = 0
bgg_id = 1
while fetched_games < num_entries:
batch_ids = list(range(bgg_id, bgg_id + batch_size))
games_data = bgg_get_game_details(batch_ids)
for game_data in games_data:
if fetched_games >= num_entries:
break
fixture_entry = {
"model": "games.game",
"pk": fetched_games + 1,
"fields": {
"name": game_data.get("name", ""),
"description": game_data.get("description", ""),
"year_published": game_data.get("year_published", None),
"image": game_data.get("image", ""),
"rules": "",
"min_players": game_data.get("min_players", ""),
"max_players": game_data.get("max_players", ""),
"suggested_age": game_data.get("suggested_age", ""),
"expected_playtime": game_data.get("expected_playtime", ""),
"min_playtime": game_data.get("min_playtime", ""),
"max_playtime": game_data.get("max_playtime", ""),
"complexity": game_data.get("complexity", None),
"BGG_id": game_data.get("BGG_id", ""),
"categories": [],
"mechanics": [],
},
}
fixtures.append(fixture_entry)
fetched_games += 1
print(f"Added game {fetched_games} of {num_entries}")
bgg_id += batch_size
with open(filename, "w", encoding="utf-8") as f:
json.dump(fixtures, f, ensure_ascii=False, indent=4)
if __name__ == "__main__":
# This will create a fixture with 50 games
fixture_size = 50
create_fixture_file(fixture_size, f"games-fixture-{fixture_size}.json")
-
Tournaments
-
Games
-
Matches
-
API
-
Forums
-
Users & Friending