Kabila22 commited on
Commit
99e52a8
·
1 Parent(s): ab419e1

backend commit

Browse files
Files changed (1) hide show
  1. app/main.py +138 -9
app/main.py CHANGED
@@ -10,18 +10,15 @@ import json
10
  from groq import Groq
11
  from dotenv import load_dotenv
12
 
13
- # Configure logging
14
  logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger(__name__)
16
 
17
- # Load environment variables
18
  load_dotenv()
19
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
20
  if not GROQ_API_KEY:
21
  logger.error("GROQ_API_KEY not found in environment variables.")
22
  raise Exception("GROQ_API_KEY not found in environment variables.")
23
 
24
- # Log and clear proxy environment variables
25
  logger.info(f"HTTP_PROXY: {os.environ.get('HTTP_PROXY')}")
26
  logger.info(f"HTTPS_PROXY: {os.environ.get('HTTPS_PROXY')}")
27
  os.environ.pop("HTTP_PROXY", None)
@@ -29,7 +26,6 @@ os.environ.pop("HTTPS_PROXY", None)
29
  os.environ.pop("NO_PROXY", None)
30
  logger.info("Proxy environment variables cleared to prevent 'proxies' error.")
31
 
32
- # Initialize Groq client
33
  try:
34
  client = Groq(api_key=GROQ_API_KEY)
35
  logger.info("Groq client initialized successfully.")
@@ -39,7 +35,6 @@ except Exception as e:
39
 
40
  app = FastAPI()
41
 
42
- # Enable CORS
43
  app.add_middleware(
44
  CORSMiddleware,
45
  allow_origins=["*"],
@@ -48,7 +43,6 @@ app.add_middleware(
48
  allow_headers=["*"],
49
  )
50
 
51
- # Load datasets and country codes
52
  try:
53
  matches_df = pd.read_csv('data/results.csv')
54
  goals_df = pd.read_csv('data/goalscorers.csv')
@@ -75,10 +69,17 @@ goals_df['y_coord'] = np.random.uniform(20, 80, len(goals_df)).round()
75
  teams = set(matches_df['home_team'].unique()).union(set(matches_df['away_team'].unique()))
76
  players = sorted([str(scorer) for scorer in goals_df['scorer'].dropna().unique() if pd.notna(scorer)])
77
 
78
- # Skip model loading for now
79
  logger.warning("Model loading skipped due to compatibility issues. Prediction endpoint disabled.")
80
 
81
  def summarize_with_groq(text):
 
 
 
 
 
 
 
 
82
  try:
83
  chat_completion = client.chat.completions.create(
84
  messages=[
@@ -94,6 +95,15 @@ def summarize_with_groq(text):
94
  return "Summary unavailable due to an error."
95
 
96
  def get_team_stats(team_name):
 
 
 
 
 
 
 
 
 
97
  home_matches = matches_df[matches_df['home_team'] == team_name]
98
  away_matches = matches_df[matches_df['away_team'] == team_name]
99
 
@@ -113,7 +123,7 @@ def get_team_stats(team_name):
113
  wins = len(home_matches[home_matches['home_score'] > home_matches['away_score']]) + \
114
  len(away_matches[away_matches['away_score'] > away_matches['home_score']])
115
  losses = len(home_matches[home_matches['home_score'] < home_matches['away_score']]) + \
116
- len(away_matches[away_matches['away_score'] < home_matches['home_score']])
117
  draws = len(home_matches[home_matches['home_score'] == home_matches['away_score']]) + \
118
  len(away_matches[away_matches['away_score'] == away_matches['home_score']])
119
 
@@ -152,18 +162,38 @@ def get_team_stats(team_name):
152
  }
153
 
154
  def get_match_goalscorers(date, home_team, away_team):
 
 
 
 
 
 
 
 
 
 
155
  match_goals = goals_df[(goals_df['date'] == date) &
156
  (goals_df['home_team'] == home_team) &
157
  (goals_df['away_team'] == away_team)]
158
  return match_goals[['scorer', 'minute', 'team', 'own_goal', 'penalty']].to_dict('records')
159
 
160
  def get_head_to_head_stats(team1, team2, num_matches=5):
 
 
 
 
 
 
 
 
 
 
161
  matches = matches_df[((matches_df['home_team'] == team1) & (matches_df['away_team'] == team2)) |
162
  ((matches_df['home_team'] == team2) & (matches_df['away_team'] == team1))]
163
 
164
  if matches.empty:
165
  return {"total_matches": 0, f"{team1}_wins": 0, f"{team2}_wins": 0, "draws": 0,
166
- f"{team1}_goals": 0, f"{team2}_wins": 0, "goal_difference": "Even",
167
  "last_matches": [], "chart": None}
168
 
169
  total_matches = len(matches)
@@ -214,6 +244,17 @@ def get_head_to_head_stats(team1, team2, num_matches=5):
214
  }
215
 
216
  def get_player_stats(player_name):
 
 
 
 
 
 
 
 
 
 
 
217
  player_goals = goals_df[goals_df['scorer'] == player_name]
218
  if player_goals.empty:
219
  raise HTTPException(status_code=404, detail="Player not found")
@@ -222,10 +263,24 @@ def get_player_stats(player_name):
222
  return {"player_name": player_name, "country": player_team, "total_goals": total_goals}
223
 
224
  def predict_match_outcome(team1, team2):
 
 
 
 
 
 
 
 
 
225
  raise HTTPException(status_code=503, detail="Prediction functionality is temporarily disabled due to model loading issues.")
226
 
227
  @app.get("/")
228
  async def home():
 
 
 
 
 
229
  return {
230
  "message": "Welcome to Football Prediction API",
231
  "description": "This API provides football statistics, match predictions, and data visualizations. Note: Prediction endpoint is currently disabled.",
@@ -243,18 +298,45 @@ async def home():
243
 
244
  @app.get("/teams")
245
  async def get_teams():
 
 
 
 
 
246
  return {"teams": sorted(list(teams))}
247
 
248
  @app.get("/players")
249
  async def get_players():
 
 
 
 
 
250
  return {"players": players}
251
 
252
  @app.get("/country-codes")
253
  async def get_country_codes():
 
 
 
 
 
254
  return COUNTRY_CODE_MAP
255
 
256
  @app.get("/team/{team_name}")
257
  async def get_team_statistics(team_name: str, summarize: bool = False):
 
 
 
 
 
 
 
 
 
 
 
 
258
  if team_name not in teams:
259
  raise HTTPException(status_code=404, detail=f"Team {team_name} not found")
260
  try:
@@ -283,6 +365,20 @@ async def get_team_statistics(team_name: str, summarize: bool = False):
283
 
284
  @app.get("/head-to-head/{team1}/{team2}")
285
  async def get_head_to_head(team1: str, team2: str, num_matches: int = 5, summarize: bool = False):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  if team1 not in teams or team2 not in teams:
287
  raise HTTPException(status_code=404, detail="One or both teams not found")
288
  if num_matches < 0:
@@ -299,6 +395,15 @@ async def get_head_to_head(team1: str, team2: str, num_matches: int = 5, summari
299
 
300
  @app.get("/player/{player_name}")
301
  async def get_player_statistics(player_name: str, summarize: bool = False):
 
 
 
 
 
 
 
 
 
302
  stats = get_player_stats(player_name)
303
  response = stats
304
  if summarize:
@@ -309,10 +414,34 @@ async def get_player_statistics(player_name: str, summarize: bool = False):
309
 
310
  @app.get("/predict/{team1}/{team2}")
311
  async def predict_match(team1: str, team2: str, summarize: bool = False):
 
 
 
 
 
 
 
 
 
 
312
  raise HTTPException(status_code=503, detail="Prediction functionality is temporarily disabled due to model loading issues.")
313
 
314
  @app.get("/goal-spatial-heatmap/{team}")
315
  async def get_goal_spatial_heatmap(team: str, start_year: int = 2000, end_year: int = 2023, summarize: bool = False):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  if team not in teams:
317
  raise HTTPException(status_code=404, detail=f"Team {team} not found")
318
 
 
10
  from groq import Groq
11
  from dotenv import load_dotenv
12
 
 
13
  logging.basicConfig(level=logging.INFO)
14
  logger = logging.getLogger(__name__)
15
 
 
16
  load_dotenv()
17
  GROQ_API_KEY = os.getenv("GROQ_API_KEY")
18
  if not GROQ_API_KEY:
19
  logger.error("GROQ_API_KEY not found in environment variables.")
20
  raise Exception("GROQ_API_KEY not found in environment variables.")
21
 
 
22
  logger.info(f"HTTP_PROXY: {os.environ.get('HTTP_PROXY')}")
23
  logger.info(f"HTTPS_PROXY: {os.environ.get('HTTPS_PROXY')}")
24
  os.environ.pop("HTTP_PROXY", None)
 
26
  os.environ.pop("NO_PROXY", None)
27
  logger.info("Proxy environment variables cleared to prevent 'proxies' error.")
28
 
 
29
  try:
30
  client = Groq(api_key=GROQ_API_KEY)
31
  logger.info("Groq client initialized successfully.")
 
35
 
36
  app = FastAPI()
37
 
 
38
  app.add_middleware(
39
  CORSMiddleware,
40
  allow_origins=["*"],
 
43
  allow_headers=["*"],
44
  )
45
 
 
46
  try:
47
  matches_df = pd.read_csv('data/results.csv')
48
  goals_df = pd.read_csv('data/goalscorers.csv')
 
69
  teams = set(matches_df['home_team'].unique()).union(set(matches_df['away_team'].unique()))
70
  players = sorted([str(scorer) for scorer in goals_df['scorer'].dropna().unique() if pd.notna(scorer)])
71
 
 
72
  logger.warning("Model loading skipped due to compatibility issues. Prediction endpoint disabled.")
73
 
74
  def summarize_with_groq(text):
75
+ """Generate a concise summary of the provided text using the Groq API.
76
+
77
+ Args:
78
+ text (str): The text to summarize.
79
+
80
+ Returns:
81
+ str: A summary of the text or an error message if summarization fails.
82
+ """
83
  try:
84
  chat_completion = client.chat.completions.create(
85
  messages=[
 
95
  return "Summary unavailable due to an error."
96
 
97
  def get_team_stats(team_name):
98
+ """Calculate comprehensive statistics for a specified football team.
99
+
100
+ Args:
101
+ team_name (str): The name of the team to analyze.
102
+
103
+ Returns:
104
+ dict: A dictionary containing team statistics including total matches, wins, losses, draws,
105
+ home/away matches played, tournament performance, and country code.
106
+ """
107
  home_matches = matches_df[matches_df['home_team'] == team_name]
108
  away_matches = matches_df[matches_df['away_team'] == team_name]
109
 
 
123
  wins = len(home_matches[home_matches['home_score'] > home_matches['away_score']]) + \
124
  len(away_matches[away_matches['away_score'] > away_matches['home_score']])
125
  losses = len(home_matches[home_matches['home_score'] < home_matches['away_score']]) + \
126
+ len(away_matches[away_matches['away_score'] < away_matches['home_score']])
127
  draws = len(home_matches[home_matches['home_score'] == home_matches['away_score']]) + \
128
  len(away_matches[away_matches['away_score'] == away_matches['home_score']])
129
 
 
162
  }
163
 
164
  def get_match_goalscorers(date, home_team, away_team):
165
+ """Retrieve goalscorers for a specific match.
166
+
167
+ Args:
168
+ date (str): The date of the match.
169
+ home_team (str): The home team name.
170
+ away_team (str): The away team name.
171
+
172
+ Returns:
173
+ list: A list of dictionaries containing goalscorer details for the match.
174
+ """
175
  match_goals = goals_df[(goals_df['date'] == date) &
176
  (goals_df['home_team'] == home_team) &
177
  (goals_df['away_team'] == away_team)]
178
  return match_goals[['scorer', 'minute', 'team', 'own_goal', 'penalty']].to_dict('records')
179
 
180
  def get_head_to_head_stats(team1, team2, num_matches=5):
181
+ """Calculate head-to-head statistics between two teams.
182
+
183
+ Args:
184
+ team1 (str): The first team name.
185
+ team2 (str): The second team name.
186
+ num_matches (int, optional): Number of recent matches to include. Defaults to 5.
187
+
188
+ Returns:
189
+ dict: A dictionary containing head-to-head stats including wins, goals, last matches, and a chart.
190
+ """
191
  matches = matches_df[((matches_df['home_team'] == team1) & (matches_df['away_team'] == team2)) |
192
  ((matches_df['home_team'] == team2) & (matches_df['away_team'] == team1))]
193
 
194
  if matches.empty:
195
  return {"total_matches": 0, f"{team1}_wins": 0, f"{team2}_wins": 0, "draws": 0,
196
+ f"{team1}_goals": 0, f"{team2}_goals": 0, "goal_difference": "Even",
197
  "last_matches": [], "chart": None}
198
 
199
  total_matches = len(matches)
 
244
  }
245
 
246
  def get_player_stats(player_name):
247
+ """Retrieve statistics for a specific player.
248
+
249
+ Args:
250
+ player_name (str): The name of the player.
251
+
252
+ Returns:
253
+ dict: A dictionary containing the player's name, country, and total goals.
254
+
255
+ Raises:
256
+ HTTPException: If the player is not found in the dataset.
257
+ """
258
  player_goals = goals_df[goals_df['scorer'] == player_name]
259
  if player_goals.empty:
260
  raise HTTPException(status_code=404, detail="Player not found")
 
263
  return {"player_name": player_name, "country": player_team, "total_goals": total_goals}
264
 
265
  def predict_match_outcome(team1, team2):
266
+ """Predict the outcome of a match between two teams.
267
+
268
+ Args:
269
+ team1 (str): The first team name.
270
+ team2 (str): The second team name.
271
+
272
+ Raises:
273
+ HTTPException: Always raises an exception as prediction is currently disabled.
274
+ """
275
  raise HTTPException(status_code=503, detail="Prediction functionality is temporarily disabled due to model loading issues.")
276
 
277
  @app.get("/")
278
  async def home():
279
+ """Return a welcome message and API description.
280
+
281
+ Returns:
282
+ dict: A dictionary containing welcome message, description, and available endpoints.
283
+ """
284
  return {
285
  "message": "Welcome to Football Prediction API",
286
  "description": "This API provides football statistics, match predictions, and data visualizations. Note: Prediction endpoint is currently disabled.",
 
298
 
299
  @app.get("/teams")
300
  async def get_teams():
301
+ """Retrieve a list of all unique teams.
302
+
303
+ Returns:
304
+ dict: A dictionary containing a sorted list of team names.
305
+ """
306
  return {"teams": sorted(list(teams))}
307
 
308
  @app.get("/players")
309
  async def get_players():
310
+ """Retrieve a list of all unique players.
311
+
312
+ Returns:
313
+ dict: A dictionary containing a sorted list of player names.
314
+ """
315
  return {"players": players}
316
 
317
  @app.get("/country-codes")
318
  async def get_country_codes():
319
+ """Retrieve the country code mapping.
320
+
321
+ Returns:
322
+ dict: A dictionary mapping team names to their country codes.
323
+ """
324
  return COUNTRY_CODE_MAP
325
 
326
  @app.get("/team/{team_name}")
327
  async def get_team_statistics(team_name: str, summarize: bool = False):
328
+ """Get detailed statistics for a specified team.
329
+
330
+ Args:
331
+ team_name (str): The name of the team.
332
+ summarize (bool, optional): Whether to include a summary. Defaults to False.
333
+
334
+ Returns:
335
+ dict: A dictionary containing team statistics and optionally a summary.
336
+
337
+ Raises:
338
+ HTTPException: If the team is not found or stats calculation fails.
339
+ """
340
  if team_name not in teams:
341
  raise HTTPException(status_code=404, detail=f"Team {team_name} not found")
342
  try:
 
365
 
366
  @app.get("/head-to-head/{team1}/{team2}")
367
  async def get_head_to_head(team1: str, team2: str, num_matches: int = 5, summarize: bool = False):
368
+ """Get head-to-head statistics between two teams.
369
+
370
+ Args:
371
+ team1 (str): The first team name.
372
+ team2 (str): The second team name.
373
+ num_matches (int, optional): Number of recent matches to include. Defaults to 5.
374
+ summarize (bool, optional): Whether to include a summary. Defaults to False.
375
+
376
+ Returns:
377
+ dict: A dictionary containing head-to-head statistics and optionally a summary.
378
+
379
+ Raises:
380
+ HTTPException: If teams are not found or num_matches is negative.
381
+ """
382
  if team1 not in teams or team2 not in teams:
383
  raise HTTPException(status_code=404, detail="One or both teams not found")
384
  if num_matches < 0:
 
395
 
396
  @app.get("/player/{player_name}")
397
  async def get_player_statistics(player_name: str, summarize: bool = False):
398
+ """Get statistics for a specified player.
399
+
400
+ Args:
401
+ player_name (str): The name of the player.
402
+ summarize (bool, optional): Whether to include a summary. Defaults to False.
403
+
404
+ Returns:
405
+ dict: A dictionary containing player statistics and optionally a summary.
406
+ """
407
  stats = get_player_stats(player_name)
408
  response = stats
409
  if summarize:
 
414
 
415
  @app.get("/predict/{team1}/{team2}")
416
  async def predict_match(team1: str, team2: str, summarize: bool = False):
417
+ """Predict the outcome of a match between two teams (currently disabled).
418
+
419
+ Args:
420
+ team1 (str): The first team name.
421
+ team2 (str): The second team name.
422
+ summarize (bool, optional): Whether to include a summary. Defaults to False.
423
+
424
+ Raises:
425
+ HTTPException: Always raises an exception as prediction is disabled.
426
+ """
427
  raise HTTPException(status_code=503, detail="Prediction functionality is temporarily disabled due to model loading issues.")
428
 
429
  @app.get("/goal-spatial-heatmap/{team}")
430
  async def get_goal_spatial_heatmap(team: str, start_year: int = 2000, end_year: int = 2023, summarize: bool = False):
431
+ """Generate a spatial heatmap of goal distribution for a team.
432
+
433
+ Args:
434
+ team (str): The team name.
435
+ start_year (int, optional): The starting year for analysis. Defaults to 2000.
436
+ end_year (int, optional): The ending year for analysis. Defaults to 2023.
437
+ summarize (bool, optional): Whether to include a summary. Defaults to False.
438
+
439
+ Returns:
440
+ dict: A dictionary containing the heatmap, total goals, and average goals per match.
441
+
442
+ Raises:
443
+ HTTPException: If team not found, years invalid, or no goal data exists.
444
+ """
445
  if team not in teams:
446
  raise HTTPException(status_code=404, detail=f"Team {team} not found")
447