mrSpectrum commited on
Commit
2577d67
·
verified ·
1 Parent(s): c4019d2

Upload 2 files

Browse files
utils/__pycache__/bgg.cpython-313.pyc ADDED
Binary file (1.18 kB). View file
 
utils/bgg.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from xml.etree import ElementTree as ET
3
+
4
+ BGG_BASE_URL = 'https://boardgamegeek.com/xmlapi2'
5
+
6
+ def search(query):
7
+ """
8
+ Search for board games on BoardGameGeek by title.
9
+
10
+ Args:
11
+ query (str): The name (or partial name) of the board game to search for.
12
+
13
+ Returns:
14
+ List[dict]: A list of dictionaries, each representing a matching game with the following fields:
15
+ - 'id' (str): The unique BGG ID of the game.
16
+ - 'title' (str): The official title of the game.
17
+ - 'year' (str): The year the game was published (or 'Unknown' if not available).
18
+
19
+ Example:
20
+ >>> search_game("Catan")
21
+ [
22
+ {"id": "13", "title": "Catan", "year": "1995"},
23
+ ...
24
+ ]
25
+ """
26
+
27
+ response = requests.get(f"{BGG_BASE_URL}/search?query={query}&exact=0&type=boardgame")
28
+ root = ET.fromstring(response.content)
29
+
30
+ results = []
31
+ for item in root.findall("item"):
32
+ game_id = item.get("id")
33
+ title = item.find("name").get("value")
34
+ year = item.find("yearpublished").get("value") if item.find("yearpublished") is not None else "Unknown"
35
+ results.append({"id": game_id, "title": title, "year": year})
36
+
37
+ return results
38
+
39
+ def get_game_details(game_ids):
40
+ """
41
+ Get detailed information for one or more board games.
42
+
43
+ Args:
44
+ game_ids (str or list): Single game ID or comma-separated list of IDs
45
+
46
+ Returns:
47
+ List[dict]: Detailed game information including mechanics, categories, ratings, etc.
48
+ """
49
+ if isinstance(game_ids, list):
50
+ game_ids = ",".join(game_ids)
51
+
52
+ response = requests.get(f"{BGG_BASE_URL}/thing?id={game_ids}&stats=1")
53
+ root = ET.fromstring(response.content)
54
+
55
+ games = []
56
+ for item in root.findall("item"):
57
+ game = {
58
+ "id": item.get("id"),
59
+ "title": item.find("name[@type='primary']").get("value"),
60
+ "description": item.find("description").text if item.find("description") is not None else "No description available",
61
+ "year": item.find("yearpublished").get("value") if item.find("yearpublished") is not None else "Unknown",
62
+ "min_players": item.find("minplayers").get("value") if item.find("minplayers") is not None else "Unknown",
63
+ "max_players": item.find("maxplayers").get("value") if item.find("maxplayers") is not None else "Unknown",
64
+ "playing_time": item.find("playingtime").get("value") if item.find("playingtime") is not None else "Unknown",
65
+ "complexity": None,
66
+ "rating": None,
67
+ "categories": [],
68
+ "mechanics": []
69
+ }
70
+
71
+ # Extract ratings and complexity
72
+ ratings = item.find("statistics/ratings")
73
+ if ratings is not None:
74
+ avg_rating = ratings.find("average")
75
+ if avg_rating is not None:
76
+ game["rating"] = avg_rating.get("value")
77
+
78
+ complexity_elem = ratings.find("averageweight")
79
+ if complexity_elem is not None:
80
+ game["complexity"] = complexity_elem.get("value")
81
+
82
+ # Extract categories and mechanics
83
+ for link in item.findall("link"):
84
+ link_type = link.get("type")
85
+ if link_type == "boardgamecategory":
86
+ game["categories"].append(link.get("value"))
87
+ elif link_type == "boardgamemechanic":
88
+ game["mechanics"].append(link.get("value"))
89
+
90
+ games.append(game)
91
+
92
+ return games
93
+
94
+ def get_hot_games():
95
+ """
96
+ Get a list of the Top 50 trending games today from BoardGameGeek.
97
+
98
+ Returns:
99
+ List[dict]: A list of dictionaries, each representing a popular game with the following fields:
100
+ - 'id' (str): The unique BGG ID of the game.
101
+ - 'title' (str): The official title of the game.
102
+ - 'rank' (str): The current hotness rank of the game on BGG.
103
+ - 'year' (str): The year the game was published (or 'Unknown' if not available).
104
+ - 'url' (str): URL to the similar game's page on BoardGameGeek.
105
+ """
106
+ response = requests.get(f"{BGG_BASE_URL}/hot?type=boardgame")
107
+ root = ET.fromstring(response.content)
108
+
109
+ results = []
110
+ for item in root.findall("item"):
111
+ game_id = item.get("id")
112
+ rank = item.get("rank")
113
+ title = item.find("name").get("value")
114
+ year = item.find("yearpublished").get("value") if item.find("yearpublished") is not None else "Unknown"
115
+ url = f"https://boardgamegeek.com/boardgame/{game_id}"
116
+ results.append({"id": game_id, "title": title, "rank": rank, "year": year, "url": url})
117
+
118
+ return results
119
+
120
+ def get_similar_games(game_id, limit=5):
121
+ """
122
+ Get a list of games similar to the specified game from the recommend games API.
123
+
124
+ Args:
125
+ game_id (str): The unique BGG ID of the game to find similar games for.
126
+ limit (int): The number of BGG IDs to retrieve.
127
+
128
+ Returns:
129
+ List[dict]: A list of dictionaries, each representing a similar game with the following fields:
130
+ - 'id' (str): The unique BGG ID of the similar game.
131
+ - 'title' (str): The official title of the similar game.
132
+ - 'year' (str): The year the similar game was published (or 'Unknown' if not available).
133
+ - 'description' (str): A brief description of the similar game.
134
+ - 'url' (str): URL to the similar game's page on BoardGameGeek.
135
+ """
136
+ recommended_games = []
137
+ api_url = f"https://recommend.games/api/games/{game_id}/similar.json"
138
+ try:
139
+ response = requests.get(api_url)
140
+ response.raise_for_status()
141
+ api_data = response.json()
142
+ results = api_data['results']
143
+ # Filter out all games with less than 30 votes (num_votes)
144
+ results = [game for game in results if game.get('num_votes', 0) >= 30]
145
+ # Filter out all games with a rec_rating of 0
146
+ results = [game for game in results if game.get('rec_rating', 0) > 0]
147
+ # Sort results by rec_rating, bayes_rating and avg_rating
148
+ results.sort(key=lambda x: (x.get('rec_rating', 0), x.get('bayes_rating', 0), x.get('avg_rating', 0)), reverse=True)
149
+
150
+ for game_data in results[:limit]:
151
+ bgg_id = game_data.get('bgg_id')
152
+ title = game_data.get('name')
153
+ year = game_data.get('year')
154
+ description = game_data.get('description', 'No description available')
155
+ url = game_data.get('url')
156
+
157
+ if bgg_id and title:
158
+ formatted_game = {
159
+ 'id': str(bgg_id),
160
+ 'title': str(title),
161
+ 'year': str(year) if year is not None else 'Unknown',
162
+ 'description': str(description),
163
+ 'url': str(url),
164
+ }
165
+ recommended_games.append(formatted_game)
166
+
167
+ return recommended_games
168
+ except requests.RequestException as e:
169
+ print(f"Error fetching similar games: {e}")
170
+ return []
171
+
172
+ def get_similar_games_v2(game_id, limit=5, start=0, end=25, noblock=False):
173
+ """
174
+ Retrieves a list of games similar to a specified board game from the RecommendGames API.
175
+
176
+ Args:
177
+ game_id (str): The unique BGG ID of the game to find similar games for.
178
+ limit (int): The number of similar games to retrieve.
179
+ start (int, optional): The starting index for the desired range of results. Defaults to 0.
180
+ end (int, optional): The ending index for the desired range of results. Defaults to 25.
181
+ noblock (bool, optional): If True, the request will not timeout. Defaults to False, which sets a 10-second timeout.
182
+
183
+ Returns:
184
+ List[dict]: A list of dictionaries, each representing a similar game with the following fields:
185
+ - 'id' (str): The unique BGG ID of the similar game.
186
+ - 'title' (str): The official title of the similar game.
187
+ - 'year' (str): The year the similar game was published (or 'Unknown' if not available).
188
+ - 'description' (str): A brief description of the similar game.
189
+ - 'url' (str): URL to the similar game's page on BoardGameGeek.
190
+ Returns an empty list if there are any errors during the API request or data processing.
191
+ """
192
+
193
+ api_url = f"https://recommend.games/api/games/{game_id}/similar.json"
194
+ params = {
195
+ 'num_votes__gte': 30,
196
+ 'ordering': '-rec_rating,-bayes_rating,-avg_rating'
197
+ }
198
+
199
+ all_games = []
200
+
201
+ try:
202
+ for i in range(start+1, end+1):
203
+ params['page'] = i
204
+ response = requests.get(api_url, params=params, timeout=None if noblock else 10)
205
+ response.raise_for_status()
206
+ api_data = response.json()
207
+
208
+ games = api_data.get('results', [])
209
+ if not games:
210
+ break
211
+
212
+ processed_games = [
213
+ {
214
+ 'id': str(game.get('bgg_id', '')),
215
+ 'title': str(game.get('name', '')),
216
+ 'year': str(game.get('year', 'Unknown')),
217
+ 'description': str(game.get('description', 'No description available')),
218
+ 'url': str(game.get('url', '')),
219
+ }
220
+ for game in games
221
+ if game.get('num_votes', 0) >= 30 and game.get('rec_rating', 0) > 0.001
222
+ ]
223
+
224
+ all_games.extend(processed_games)
225
+
226
+ # Check if we have enough results
227
+ if len(all_games) >= end or not api_data.get('next'):
228
+ break
229
+
230
+ return all_games[:limit] if limit > 0 else all_games
231
+
232
+ except requests.RequestException as e:
233
+ print(f"Error fetching similar games: {e}")
234
+ return []
235
+ except ValueError as e:
236
+ print(f"Error: {e}")
237
+ return []