rairo commited on
Commit
4e4311b
·
verified ·
1 Parent(s): ec03155

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +476 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,478 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import requests
3
+ import pandas as pd
4
+ from bs4 import BeautifulSoup
5
+ import time
6
+ import re
7
+ from datetime import datetime, timezone
8
+
9
+ # ---------- Configuration & Constants ----------
10
+ LEAGUES = {
11
+ 'premier_league': {
12
+ 'player_stats_url': 'https://fbref.com/en/comps/9/stats/Premier-League-Stats',
13
+ 'squad_stats_url': 'https://fbref.com/en/comps/9/Premier-League-Stats',
14
+ 'fixtures_url': 'https://fbref.com/en/comps/9/schedule/Premier-League-Scores-and-Fixtures',
15
+ 'name': 'Premier League'
16
+ },
17
+ 'la_liga': {
18
+ 'player_stats_url': 'https://fbref.com/en/comps/12/stats/La-Liga-Stats',
19
+ 'squad_stats_url': 'https://fbref.com/en/comps/12/La-Liga-Stats',
20
+ 'fixtures_url': 'https://fbref.com/en/comps/12/schedule/La-Liga-Scores-and-Fixtures',
21
+ 'name': 'La Liga'
22
+ },
23
+ 'serie_a': {
24
+ 'player_stats_url': 'https://fbref.com/en/comps/11/stats/Serie-A-Stats',
25
+ 'squad_stats_url': 'https://fbref.com/en/comps/11/Serie-A-Stats',
26
+ 'fixtures_url': 'https://fbref.com/en/comps/11/schedule/Serie-A-Scores-and-Fixtures',
27
+ 'name': 'Serie A'
28
+ },
29
+ 'bundesliga': {
30
+ 'player_stats_url': 'https://fbref.com/en/comps/20/stats/Bundesliga-Stats',
31
+ 'squad_stats_url': 'https://fbref.com/en/comps/20/Bundesliga-Stats',
32
+ 'fixtures_url': 'https://fbref.com/en/comps/20/schedule/Bundesliga-Scores-and-Fixtures',
33
+ 'name': 'Bundesliga'
34
+ },
35
+ 'ligue_1': {
36
+ 'player_stats_url': 'https://fbref.com/en/comps/13/stats/Ligue-1-Stats',
37
+ 'squad_stats_url': 'https://fbref.com/en/comps/13/Ligue-1-Stats',
38
+ 'fixtures_url': 'https://fbref.com/en/comps/13/schedule/Ligue-1-Scores-and-Fixtures',
39
+ 'name': 'Ligue 1'
40
+ }
41
+ }
42
+
43
+ SCRAPE_HEADERS = {
44
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
45
+ }
46
+
47
+ PERPLEXITY_API_URL = 'https://api.perplexity.ai/chat/completions'
48
+
49
+ # Initialize session state for storing data
50
+ if 'player_stats_data' not in st.session_state:
51
+ st.session_state.player_stats_data = {}
52
+ if 'squad_stats_data' not in st.session_state:
53
+ st.session_state.squad_stats_data = {}
54
+ if 'fixtures_data' not in st.session_state:
55
+ st.session_state.fixtures_data = {}
56
+ if 'perplexity_api_key' not in st.session_state:
57
+ st.session_state.perplexity_api_key = ""
58
+
59
+
60
+ # ---------- Helper Functions (from Flask app) ----------
61
+ def clean_fbref_df_columns(df):
62
+ if isinstance(df.columns, pd.MultiIndex):
63
+ df.columns = df.columns.droplevel(0)
64
+ df.columns = ["".join(c if c.isalnum() or c == '%' else "_" for c in str(col)) for col in df.columns]
65
+ df.columns = [col.replace('%', 'Pct') for col in df.columns]
66
+ df = df.rename(columns=lambda x: re.sub(r'_+', '_', x))
67
+ df = df.rename(columns=lambda x: x.strip('_'))
68
+ return df
69
+
70
+ # ---------- Scraping Functions (modified for Streamlit) ----------
71
+ def scrape_player_stats_st(league_keys_to_scrape):
72
+ st.write("### Scraping Player Stats...")
73
+ progress_bar = st.progress(0)
74
+ total_leagues = len(league_keys_to_scrape)
75
+
76
+ for i, key in enumerate(league_keys_to_scrape):
77
+ url = LEAGUES[key]['player_stats_url']
78
+ st.write(f"Fetching player stats for: {LEAGUES[key]['name']}...")
79
+ try:
80
+ r = requests.get(url, headers=SCRAPE_HEADERS, timeout=30)
81
+ r.raise_for_status()
82
+ soup = BeautifulSoup(r.text, 'html.parser')
83
+ table_player_standard = soup.find('table', {'id': 'stats_standard'})
84
+
85
+ if table_player_standard:
86
+ df = pd.read_html(str(table_player_standard))[0]
87
+ df = clean_fbref_df_columns(df)
88
+ df = df[df['Player'].notna() & (df['Player'] != 'Player')]
89
+ df = df[df['Rk'].notna() & (df['Rk'] != 'Rk')]
90
+
91
+ for col in df.columns:
92
+ if col not in ['Player', 'Nation', 'Pos', 'Squad', 'Comp', 'Matches']:
93
+ try:
94
+ df[col] = pd.to_numeric(df[col], errors='coerce')
95
+ except Exception:
96
+ pass
97
+ df = df.fillna(0)
98
+
99
+ st.session_state.player_stats_data[key] = df
100
+ st.success(f"Successfully scraped player stats for {LEAGUES[key]['name']}.")
101
+ else:
102
+ st.error(f"Could not find player stats table for {LEAGUES[key]['name']}.")
103
+ time.sleep(3)
104
+ except Exception as e:
105
+ st.error(f"Error scraping player stats for {LEAGUES[key]['name']}: {e}")
106
+ progress_bar.progress((i + 1) / total_leagues)
107
+ st.write("Player stats scraping complete.")
108
+
109
+ def scrape_squad_stats_st(league_keys_to_scrape):
110
+ st.write("### Scraping Squad Stats (League Tables)...")
111
+ progress_bar = st.progress(0)
112
+ total_leagues = len(league_keys_to_scrape)
113
+
114
+ for i, key in enumerate(league_keys_to_scrape):
115
+ url = LEAGUES[key]['squad_stats_url']
116
+ st.write(f"Fetching squad stats for: {LEAGUES[key]['name']}...")
117
+ try:
118
+ r = requests.get(url, headers=SCRAPE_HEADERS, timeout=30)
119
+ r.raise_for_status()
120
+ soup = BeautifulSoup(r.text, 'html.parser')
121
+
122
+ league_table = None
123
+ all_captions = soup.find_all('caption')
124
+ for caption_tag in all_captions:
125
+ if "table" in caption_tag.get_text().lower() and "squad" not in caption_tag.get_text().lower() and "standard stats" not in caption_tag.get_text().lower():
126
+ parent_table = caption_tag.find_parent('table')
127
+ temp_df_check = pd.read_html(str(parent_table))[0]
128
+ temp_cols = temp_df_check.columns
129
+ if isinstance(temp_cols, pd.MultiIndex): temp_cols = temp_cols.droplevel(0)
130
+ if all(col in temp_cols for col in ['Squad', 'MP', 'W', 'D', 'L', 'Pts']):
131
+ league_table = parent_table
132
+ break
133
+
134
+ if not league_table:
135
+ potential_table = soup.find('table', id=lambda x: x and 'overall' in x)
136
+ if potential_table: league_table = potential_table
137
+
138
+ if not league_table:
139
+ table_squad_standard = soup.find('table', {'id': 'stats_standard'})
140
+ if table_squad_standard:
141
+ temp_df_check = pd.read_html(str(table_squad_standard))[0]
142
+ temp_cols = temp_df_check.columns
143
+ if isinstance(temp_cols, pd.MultiIndex): temp_cols = temp_cols.droplevel(0)
144
+ if all(col in temp_cols for col in ['Squad', 'MP', 'W', 'D', 'L', 'Pts']):
145
+ league_table = table_squad_standard
146
+
147
+ if league_table:
148
+ df = pd.read_html(str(league_table))[0]
149
+ df = clean_fbref_df_columns(df)
150
+ df = df[df['Squad'].notna() & (df['Squad'] != 'Squad')]
151
+ df = df[df['Rk'].notna() & (df['Rk'] != 'Rk')]
152
+
153
+ numeric_cols = ['MP', 'W', 'D', 'L', 'GF', 'GA', 'GD', 'Pts', 'xG', 'xGA', 'xGD']
154
+ for col in df.columns:
155
+ if col in numeric_cols:
156
+ df[col] = pd.to_numeric(df[col], errors='coerce')
157
+ df = df.fillna(0)
158
+
159
+ st.session_state.squad_stats_data[key] = df
160
+ st.success(f"Successfully scraped squad stats for {LEAGUES[key]['name']}.")
161
+ else:
162
+ st.error(f"Could not find squad stats table for {LEAGUES[key]['name']}.")
163
+ time.sleep(3)
164
+ except Exception as e:
165
+ st.error(f"Error scraping squad stats for {LEAGUES[key]['name']}: {e}")
166
+ progress_bar.progress((i + 1) / total_leagues)
167
+ st.write("Squad stats scraping complete.")
168
+
169
+ def scrape_fixtures_st(league_keys_to_scrape):
170
+ st.write("### Scraping Fixtures...")
171
+ progress_bar = st.progress(0)
172
+ total_leagues = len(league_keys_to_scrape)
173
+
174
+ for i, key in enumerate(league_keys_to_scrape):
175
+ url = LEAGUES[key]['fixtures_url']
176
+ st.write(f"Fetching fixtures for: {LEAGUES[key]['name']}...")
177
+ try:
178
+ r = requests.get(url, headers=SCRAPE_HEADERS, timeout=30)
179
+ r.raise_for_status()
180
+ soup = BeautifulSoup(r.text, 'html.parser')
181
+
182
+ fixture_table = None
183
+ all_captions = soup.find_all('caption')
184
+ for caption_tag in all_captions:
185
+ if "scores and fixtures" in caption_tag.get_text().lower():
186
+ fixture_table = caption_tag.find_parent('table')
187
+ if fixture_table: break
188
+
189
+ if not fixture_table:
190
+ potential_tables = soup.find_all('table', class_="stats_table")
191
+ if potential_tables: fixture_table = potential_tables[0]
192
+
193
+ if fixture_table:
194
+ df = pd.read_html(str(fixture_table))[0]
195
+ df = clean_fbref_df_columns(df)
196
+ df = df[df['Wk'].notna()]
197
+ df = df[df['Home'].notna() & (df['Home'] != 'Home')]
198
+
199
+ if 'Score' in df.columns:
200
+ score_split = df['Score'].astype(str).str.split('–', expand=True)
201
+ if score_split.shape[1] == 2:
202
+ df['HomeGoals'] = pd.to_numeric(score_split[0], errors='coerce')
203
+ df['AwayGoals'] = pd.to_numeric(score_split[1], errors='coerce')
204
+ else:
205
+ df['HomeGoals'] = None
206
+ df['AwayGoals'] = None
207
+
208
+ if 'Date' in df.columns:
209
+ df['Date'] = pd.to_datetime(df['Date'], errors='coerce').dt.strftime('%Y-%m-%d')
210
+
211
+ st.session_state.fixtures_data[key] = df
212
+ st.success(f"Successfully scraped fixtures for {LEAGUES[key]['name']}.")
213
+ else:
214
+ st.error(f"Could not find fixtures table for {LEAGUES[key]['name']}.")
215
+ time.sleep(3)
216
+ except Exception as e:
217
+ st.error(f"Error scraping fixtures for {LEAGUES[key]['name']}: {e}")
218
+ progress_bar.progress((i + 1) / total_leagues)
219
+ st.write("Fixtures scraping complete.")
220
+
221
+ # ---------- Perplexity API Functions ----------
222
+ def get_perplexity_response(api_key, prompt, system_message="You are a helpful football analyst AI."):
223
+ if not api_key:
224
+ st.error("Perplexity API Key is not set. Please enter it in the sidebar.")
225
+ return None
226
+
227
+ headers = {
228
+ 'Authorization': f'Bearer {api_key}',
229
+ 'Content-Type': 'application/json'
230
+ }
231
+ payload = {
232
+ 'model': 'sonar-medium-online', # Or 'sonar-pro-online'
233
+ 'messages': [
234
+ {'role': 'system', 'content': system_message},
235
+ {'role': 'user', 'content': prompt}
236
+ ]
237
+ }
238
+ try:
239
+ with st.spinner("Querying Perplexity AI..."):
240
+ response = requests.post(PERPLEXITY_API_URL, headers=headers, json=payload, timeout=45)
241
+ response.raise_for_status()
242
+ data = response.json()
243
+ return data.get('choices', [{}])[0].get('message', {}).get('content', '')
244
+ except requests.exceptions.RequestException as e:
245
+ error_message = f"Error communicating with Perplexity API: {e}"
246
+ if e.response is not None:
247
+ try:
248
+ error_detail = e.response.json().get("error", {}).get("message", e.response.text)
249
+ error_message = f"Perplexity API error: {error_detail}"
250
+ except ValueError:
251
+ error_message = f"Perplexity API error: {e.response.status_code} - {e.response.reason}"
252
+ st.error(error_message)
253
+ return None
254
+ except Exception as e:
255
+ st.error(f"An unexpected error occurred with Perplexity API: {e}")
256
+ return None
257
+
258
+ # ---------- Streamlit UI ----------
259
+ st.set_page_config(layout="wide")
260
+ st.title("⚽ Football Data Scraper & Perplexity Tester")
261
+ st.markdown("Test data retrieval from FBRef and Perplexity API integration. No Firebase calls.")
262
+
263
+ # --- Sidebar ---
264
+ st.sidebar.header("API Keys")
265
+ st.session_state.perplexity_api_key = st.sidebar.text_input(
266
+ "Perplexity API Key:",
267
+ type="password",
268
+ value=st.session_state.perplexity_api_key,
269
+ help="Your Perplexity AI API key. Will not be stored permanently."
270
+ )
271
+
272
+ st.sidebar.markdown("---")
273
+ st.sidebar.header("Scraping Controls")
274
+ selected_league_keys = st.sidebar.multiselect(
275
+ "Select leagues to scrape:",
276
+ options=list(LEAGUES.keys()),
277
+ format_func=lambda key: LEAGUES[key]['name'],
278
+ default=[]
279
+ )
280
+
281
+ if st.sidebar.button("Scrape Player Stats"):
282
+ if selected_league_keys: scrape_player_stats_st(selected_league_keys)
283
+ else: st.sidebar.warning("Select leagues.")
284
+
285
+ if st.sidebar.button("Scrape Squad Stats"):
286
+ if selected_league_keys: scrape_squad_stats_st(selected_league_keys)
287
+ else: st.sidebar.warning("Select leagues.")
288
+
289
+ if st.sidebar.button("Scrape Fixtures"):
290
+ if selected_league_keys: scrape_fixtures_st(selected_league_keys)
291
+ else: st.sidebar.warning("Select leagues.")
292
+
293
+ st.sidebar.markdown("---")
294
+ st.sidebar.header("View Scraped Data")
295
+ display_league_key = st.sidebar.selectbox(
296
+ "Select league to display data for:",
297
+ options=[""] + list(LEAGUES.keys()),
298
+ format_func=lambda key: LEAGUES[key]['name'] if key else "Select a league"
299
+ )
300
+
301
+ # --- Main Content Area ---
302
+ if display_league_key:
303
+ tab1, tab2, tab3 = st.tabs(["Player Stats", "Squad Stats (League Table)", "Fixtures"])
304
+ with tab1:
305
+ st.subheader(f"Player Stats for {LEAGUES[display_league_key]['name']}")
306
+ if display_league_key in st.session_state.player_stats_data:
307
+ st.dataframe(st.session_state.player_stats_data[display_league_key])
308
+ else:
309
+ st.info("No player stats data loaded. Scrape first.")
310
+ with tab2:
311
+ st.subheader(f"Squad Stats for {LEAGUES[display_league_key]['name']}")
312
+ if display_league_key in st.session_state.squad_stats_data:
313
+ st.dataframe(st.session_state.squad_stats_data[display_league_key])
314
+ else:
315
+ st.info("No squad stats data loaded. Scrape first.")
316
+ with tab3:
317
+ st.subheader(f"Fixtures for {LEAGUES[display_league_key]['name']}")
318
+ if display_league_key in st.session_state.fixtures_data:
319
+ st.dataframe(st.session_state.fixtures_data[display_league_key])
320
+ else:
321
+ st.info("No fixtures data loaded. Scrape first.")
322
+ else:
323
+ st.info("Select a league from the sidebar to view its scraped data, or use the feature testers below.")
324
+
325
+ st.markdown("---")
326
+ st.header("FBRef Data Feature Testing (Local)")
327
+
328
+ # --- 1. Player Comparison Tool ---
329
+ st.subheader("1. Player Comparison (Local Data)")
330
+ col1_pc, col2_pc, col3_pc = st.columns(3)
331
+ pc_league = col1_pc.selectbox("League (Player Comparison):", options=[""] + list(st.session_state.player_stats_data.keys()), format_func=lambda k: LEAGUES[k]['name'] if k else "Select")
332
+ pc_player1_name = col2_pc.text_input("Player 1 Name:", key="pc_p1")
333
+ pc_player2_name = col3_pc.text_input("Player 2 Name:", key="pc_p2")
334
+
335
+ if st.button("Compare Players (Local)", key="compare_local_btn"):
336
+ # ... (Player comparison logic remains the same as before) ...
337
+ if pc_league and pc_player1_name and pc_player2_name:
338
+ if pc_league in st.session_state.player_stats_data:
339
+ all_players_df = st.session_state.player_stats_data[pc_league]
340
+ player1_data = all_players_df[all_players_df['Player'].str.contains(pc_player1_name, case=False, na=False)]
341
+ player2_data = all_players_df[all_players_df['Player'].str.contains(pc_player2_name, case=False, na=False)]
342
+
343
+ if not player1_data.empty:
344
+ st.write(f"**Stats for {pc_player1_name}:**")
345
+ st.dataframe(player1_data)
346
+ else:
347
+ st.warning(f"Could not find data for player: {pc_player1_name} in {LEAGUES[pc_league]['name']}")
348
+
349
+ if not player2_data.empty:
350
+ st.write(f"**Stats for {pc_player2_name}:**")
351
+ st.dataframe(player2_data)
352
+ else:
353
+ st.warning(f"Could not find data for player: {pc_player2_name} in {LEAGUES[pc_league]['name']}")
354
+ else:
355
+ st.error(f"Player stats data for {LEAGUES[pc_league]['name']} not loaded. Please scrape first.")
356
+ else:
357
+ st.warning("Please select a league and enter two player names for comparison.")
358
+
359
+
360
+ # --- 2. Fixture Analysis (Local Data) ---
361
+ st.subheader("2. Fixture Analysis (Local Data)")
362
+ # ... (Fixture analysis logic remains the same as before) ...
363
+ col1_fa, col2_fa, col3_fa = st.columns(3)
364
+ fa_league = col1_fa.selectbox("League (Fixture Analysis):", options=[""] + list(st.session_state.fixtures_data.keys()), format_func=lambda k: LEAGUES[k]['name'] if k else "Select")
365
+ fa_home_team = col2_fa.text_input("Home Team Name:", key="fa_home")
366
+ fa_away_team = col3_fa.text_input("Away Team Name:", key="fa_away")
367
+
368
+ if st.button("Analyze Fixture (Local)", key="analyze_local_btn"):
369
+ if fa_league and fa_home_team and fa_away_team:
370
+ if fa_league in st.session_state.fixtures_data:
371
+ all_fixtures_df = st.session_state.fixtures_data[fa_league]
372
+ home_team_norm = fa_home_team.strip().lower()
373
+ away_team_norm = fa_away_team.strip().lower()
374
+
375
+ h2h_matches = all_fixtures_df[
376
+ (all_fixtures_df['Home'].str.lower() == home_team_norm) & (all_fixtures_df['Away'].str.lower() == away_team_norm) |
377
+ (all_fixtures_df['Home'].str.lower() == away_team_norm) & (all_fixtures_df['Away'].str.lower() == home_team_norm)
378
+ ]
379
+ st.write(f"**Head-to-Head between {fa_home_team} and {fa_away_team}:**")
380
+ if not h2h_matches.empty:
381
+ st.dataframe(h2h_matches.sort_values(by='Date', ascending=False))
382
+ else:
383
+ st.info("No H2H matches found in the scraped data.")
384
+
385
+ def get_form_df(team_name, all_fixtures, num_matches=5):
386
+ team_matches = all_fixtures[
387
+ (all_fixtures['Home'].str.lower() == team_name.lower()) | (all_fixtures['Away'].str.lower() == team_name.lower())
388
+ ]
389
+ played_matches = team_matches[team_matches['HomeGoals'].notna()].sort_values(by='Date', ascending=False)
390
+ return played_matches.head(num_matches)
391
+
392
+ st.write(f"**Recent Form for {fa_home_team} (last 5 played):**")
393
+ home_form_df = get_form_df(fa_home_team, all_fixtures_df)
394
+ if not home_form_df.empty: st.dataframe(home_form_df)
395
+ else: st.info(f"No recent played matches found for {fa_home_team}.")
396
+
397
+ st.write(f"**Recent Form for {fa_away_team} (last 5 played):**")
398
+ away_form_df = get_form_df(fa_away_team, all_fixtures_df)
399
+ if not away_form_df.empty: st.dataframe(away_form_df)
400
+ else: st.info(f"No recent played matches found for {fa_away_team}.")
401
+ else:
402
+ st.error(f"Fixtures data for {LEAGUES[fa_league]['name']} not loaded. Please scrape first.")
403
+ else:
404
+ st.warning("Please select a league and enter home/away team names for analysis.")
405
+
406
+ # --- 3. Visualization Data (Local Data) ---
407
+ st.subheader("3. Visualization Data (Example: Top Scorers - Local Data)")
408
+ # ... (Visualization logic remains the same as before) ...
409
+ col1_vd, col2_vd = st.columns(2)
410
+ vd_league = col1_vd.selectbox("League (Visualization):", options=[""] + list(st.session_state.player_stats_data.keys()), format_func=lambda k: LEAGUES[k]['name'] if k else "Select")
411
+
412
+ if st.button("Show Top Scorers (Local)", key="top_scorers_local_btn"):
413
+ if vd_league:
414
+ if vd_league in st.session_state.player_stats_data:
415
+ player_df = st.session_state.player_stats_data[vd_league].copy()
416
+ player_df['Gls'] = pd.to_numeric(player_df.get('Gls'), errors='coerce').fillna(0)
417
+ player_df['Ast'] = pd.to_numeric(player_df.get('Ast'), errors='coerce').fillna(0)
418
+ top_scorers = player_df.sort_values(by=['Gls', 'Ast'], ascending=[False, False]).head(10)
419
+ st.write(f"**Top 10 Scorers Data for {LEAGUES[vd_league]['name']}:**")
420
+ st.dataframe(top_scorers[['Player', 'Squad', 'Gls', 'Ast']])
421
+ if not top_scorers.empty:
422
+ st.write("**Chart: Goals by Top Scorers**")
423
+ chart_data = top_scorers.set_index('Player')[['Gls', 'Ast']]
424
+ st.bar_chart(chart_data)
425
+ else:
426
+ st.error(f"Player stats data for {LEAGUES[vd_league]['name']} not loaded. Please scrape first.")
427
+ else:
428
+ st.warning("Please select a league for visualization data.")
429
+
430
+ st.markdown("---")
431
+ st.header("Perplexity API Testing")
432
+
433
+ # --- 4. Fixture Report via Perplexity ---
434
+ st.subheader("4. Fixture Report (via Perplexity AI)")
435
+ fr_home_team = st.text_input("Home Team (for Perplexity Report):", key="fr_home")
436
+ fr_away_team = st.text_input("Away Team (for Perplexity Report):", key="fr_away")
437
+ fr_match_date = st.text_input("Match Date (e.g., YYYY-MM-DD) (for Perplexity Report):", key="fr_date")
438
+
439
+ if st.button("Get Fixture Report from Perplexity", key="fr_perplexity_btn"):
440
+ if fr_home_team and fr_away_team and fr_match_date:
441
+ if not st.session_state.perplexity_api_key:
442
+ st.error("Perplexity API Key is not set in the sidebar.")
443
+ else:
444
+ prompt = (
445
+ f"Generate a concise pre-match report for the football match: {fr_home_team} vs {fr_away_team} scheduled for {fr_match_date}.\n"
446
+ "Include the following sections if possible, keeping each brief:\n"
447
+ "1. Recent Form (last 3-5 matches for each team, e.g., WWLDW).\n"
448
+ "2. Head-to-Head (H2H) summary of their last few encounters.\n"
449
+ "3. Key Players to Watch (one or two from each team with brief reason).\n"
450
+ "4. Brief Tactical Outlook or Prediction (optional, if confident).\n"
451
+ "Prioritize information from reputable football sources. Be objective."
452
+ )
453
+ report = get_perplexity_response(st.session_state.perplexity_api_key, prompt, "You are a football analyst providing pre-match reports.")
454
+ if report:
455
+ st.markdown("**Perplexity AI Fixture Report:**")
456
+ st.markdown(report)
457
+ else:
458
+ st.warning("Please enter Home Team, Away Team, and Match Date for the report.")
459
+
460
+ # --- 5. Custom Query via Perplexity ---
461
+ st.subheader("5. Custom Query (via Perplexity AI)")
462
+ custom_query_text = st.text_area("Enter your football-related question:", height=100, key="custom_q")
463
+
464
+ if st.button("Ask Perplexity AI", key="custom_q_btn"):
465
+ if custom_query_text:
466
+ if not st.session_state.perplexity_api_key:
467
+ st.error("Perplexity API Key is not set in the sidebar.")
468
+ else:
469
+ answer = get_perplexity_response(st.session_state.perplexity_api_key, custom_query_text)
470
+ if answer:
471
+ st.markdown("**Perplexity AI Answer:**")
472
+ st.markdown(answer)
473
+ else:
474
+ st.warning("Please enter a question to ask Perplexity AI.")
475
+
476
 
477
+ st.markdown("---")
478
+ st.caption("Streamlit test app by your AI assistant. API keys are not stored after session.")