Spaces:
Sleeping
Sleeping
Hatmanstack commited on
Commit ·
efaa2dc
1
Parent(s): e1d694a
fix: restore player search functionality and add HF frontmatter
Browse files- Update search_player_by_name to use partial matching via .str.contains()\n- Fix st.multiselect selection loss by preserving current team and selections in options\n- Add Hugging Face frontmatter to README.md for deployment
- README.md +12 -0
- pages/1_home_team.py +13 -5
- pages/2_play_game.py +1 -1
- src/database/queries.py +4 -4
- tests/test_database.py +5 -0
README.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# NBA Fantasy Predictor
|
| 2 |
|
| 3 |

|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: NBA Fantasy Predictor
|
| 3 |
+
emoji: 🏀
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: white
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: 1.28.0
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
# NBA Fantasy Predictor
|
| 14 |
|
| 15 |

|
pages/1_home_team.py
CHANGED
|
@@ -103,16 +103,24 @@ def find_home_team() -> pd.DataFrame:
|
|
| 103 |
player_search = find_player(player_add)
|
| 104 |
home_team_df = find_home_team()
|
| 105 |
|
| 106 |
-
# Combine search results with current team
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
|
| 112 |
def save_state() -> None:
|
| 113 |
"""Save the selected players to session state."""
|
| 114 |
st.session_state.home_team = st.session_state.player_selector
|
| 115 |
-
# No need for st.rerun() inside a callback usually,
|
| 116 |
# but it doesn't hurt. Streamlit reruns after callback.
|
| 117 |
|
| 118 |
|
|
|
|
| 103 |
player_search = find_player(player_add)
|
| 104 |
home_team_df = find_home_team()
|
| 105 |
|
| 106 |
+
# Combine search results with current team and current unsaved selections
|
| 107 |
+
# This ensures that selections don't disappear when the search term changes
|
| 108 |
+
current_team_names = home_team_df["FULL_NAME"].tolist() if not home_team_df.empty else []
|
| 109 |
+
current_selections = st.session_state.get("player_selector", [])
|
| 110 |
+
|
| 111 |
+
# Merge all into options list, maintaining uniqueness
|
| 112 |
+
combined_options = list(player_search)
|
| 113 |
+
for name in current_team_names + current_selections:
|
| 114 |
+
if name not in combined_options:
|
| 115 |
+
combined_options.append(name)
|
| 116 |
+
|
| 117 |
+
player_search = combined_options
|
| 118 |
|
| 119 |
|
| 120 |
def save_state() -> None:
|
| 121 |
"""Save the selected players to session state."""
|
| 122 |
st.session_state.home_team = st.session_state.player_selector
|
| 123 |
+
# No need for st.rerun() inside a callback usually,
|
| 124 |
# but it doesn't hurt. Streamlit reruns after callback.
|
| 125 |
|
| 126 |
|
pages/2_play_game.py
CHANGED
|
@@ -127,7 +127,7 @@ else:
|
|
| 127 |
# Only generate away team if we don't have one or it's empty
|
| 128 |
if st.session_state.get("away_team_df") is None or st.session_state.away_team_df.empty:
|
| 129 |
st.session_state.away_team_df = find_away_team(stats)
|
| 130 |
-
|
| 131 |
away_data = st.session_state.away_team_df
|
| 132 |
if away_data.empty:
|
| 133 |
teams_good = False
|
|
|
|
| 127 |
# Only generate away team if we don't have one or it's empty
|
| 128 |
if st.session_state.get("away_team_df") is None or st.session_state.away_team_df.empty:
|
| 129 |
st.session_state.away_team_df = find_away_team(stats)
|
| 130 |
+
|
| 131 |
away_data = st.session_state.away_team_df
|
| 132 |
if away_data.empty:
|
| 133 |
teams_good = False
|
src/database/queries.py
CHANGED
|
@@ -23,12 +23,12 @@ def search_player_by_name(df: pd.DataFrame, name: str) -> list[tuple[str]]:
|
|
| 23 |
"""
|
| 24 |
name_lower = name.lower().strip()
|
| 25 |
mask = (
|
| 26 |
-
|
| 27 |
-
|
|
| 28 |
-
|
|
| 29 |
)
|
| 30 |
results = df[mask]["FULL_NAME"].unique().tolist()
|
| 31 |
-
return [(
|
| 32 |
|
| 33 |
|
| 34 |
def get_player_by_full_name(
|
|
|
|
| 23 |
"""
|
| 24 |
name_lower = name.lower().strip()
|
| 25 |
mask = (
|
| 26 |
+
df["FULL_NAME_LOWER"].str.contains(name_lower, case=False, na=False)
|
| 27 |
+
| df["FIRST_NAME_LOWER"].str.contains(name_lower, case=False, na=False)
|
| 28 |
+
| df["LAST_NAME_LOWER"].str.contains(name_lower, case=False, na=False)
|
| 29 |
)
|
| 30 |
results = df[mask]["FULL_NAME"].unique().tolist()
|
| 31 |
+
return [(player_name,) for player_name in results]
|
| 32 |
|
| 33 |
|
| 34 |
def get_player_by_full_name(
|
tests/test_database.py
CHANGED
|
@@ -35,6 +35,11 @@ class TestSearchPlayerByName:
|
|
| 35 |
result = search_player_by_name(sample_player_df, "lebron")
|
| 36 |
assert result == [("LeBron James",)]
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
def test_returns_empty_on_no_match(self, sample_player_df: pd.DataFrame) -> None:
|
| 39 |
"""Verify empty list returned when no player found."""
|
| 40 |
result = search_player_by_name(sample_player_df, "NonExistent Player")
|
|
|
|
| 35 |
result = search_player_by_name(sample_player_df, "lebron")
|
| 36 |
assert result == [("LeBron James",)]
|
| 37 |
|
| 38 |
+
def test_search_partial_name(self, sample_player_df: pd.DataFrame) -> None:
|
| 39 |
+
"""Verify search finds player by partial name."""
|
| 40 |
+
result = search_player_by_name(sample_player_df, "Jord")
|
| 41 |
+
assert result == [("Michael Jordan",)]
|
| 42 |
+
|
| 43 |
def test_returns_empty_on_no_match(self, sample_player_df: pd.DataFrame) -> None:
|
| 44 |
"""Verify empty list returned when no player found."""
|
| 45 |
result = search_player_by_name(sample_player_df, "NonExistent Player")
|