Skip to content

Commit

Permalink
Add a matchup page (#9)
Browse files Browse the repository at this point in the history
* constant docstrings

* update prompt engineering a bit

* update prompt language

* first pass matchup viewer

* minor changes

* remove ai on team viewer cause it's not good

* an absurd amount of work for what is essentially recreating the espn ui

* add note

* add sad note
  • Loading branch information
NathanEmb authored Dec 16, 2024
1 parent 55b6763 commit 33bc596
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 63 deletions.
16 changes: 16 additions & 0 deletions .streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[client]
showErrorDetails = "none"
#toolbarMode = "viewer"

[logger]
level = "info"
messageFormat = "%(asctime)s %(message)s"

[server]
headless = true

[browser]
gatherUsageStats = false

[theme]
base="dark"
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies = [
"streamlit>=1.40.2",
"matplotlib>=3.9.3",
"groq>=0.13.0",
"jinja2>=3.1.4",
]

[tool.ruff]
Expand Down
49 changes: 35 additions & 14 deletions src/backend.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import os
import random
from copy import deepcopy

import pandas as pd
from espn_api.basketball import League, Team
from espn_api.basketball import League, Matchup, Team
from groq import Groq

import src.constants as const
Expand All @@ -13,14 +12,14 @@
def get_league(league_id: int = const.SPACEJAM_LEAGUE_ID, year: int = const.YEAR) -> League:
"""Get the league object for the specified league_id and year."""
league = League(league_id, year)
league.teams = {team.team_name: team for team in league.teams}
league.team_dict = {team.team_name: team for team in league.teams}
return league


def get_league_all_raw_stats_df(league: League) -> pd.DataFrame:
"""Get every team's stats for all categories."""
league_stats = []
for team in league.teams.values():
for team in league.team_dict.values():
temp_dict = deepcopy(team.stats)
temp_dict["Team"] = team.team_name
temp_dict["Standing"] = team.standing
Expand Down Expand Up @@ -52,7 +51,7 @@ def get_league_all_raw_data_rankings(league: League) -> pd.DataFrame:
def get_league_cat_raw_stats_df(league: League) -> pd.DataFrame:
"""Get every team's stats for only roto categories."""
league_stats = []
for team in league.teams.values():
for team in league.team_dict.values():
temp_dict = deepcopy(team.stats)
temp_dict["Team"] = team.team_name
temp_dict["Standing"] = team.standing
Expand Down Expand Up @@ -153,24 +152,18 @@ def get_prompt(prompt_map: dict):


def get_mainpage_joke():
client = Groq(
# This is the default and can be omitted
api_key=os.environ.get("GROQ_API_KEY")
)
client = Groq()
prompt = get_prompt(prompts.mainpage_prompt_map)
chat_completion = client.chat.completions.create(messages=prompt, model="llama3-8b-8192")
return chat_completion.choices[0].message.content


def get_teamviewer_joke(team_name):
client = Groq(
# This is the default and can be omitted
api_key=os.environ.get("GROQ_API_KEY")
)
client = Groq()
prompt = [
{
"role": "system",
"content": "Your job is to roast fantasy basketball team names. Be witty, and a little mean.",
"content": "Be a witty and kind of offensive when responding. Speak as an expert on fantasy basketball. Don't repeat yourself, and make sure to keep your sentences fresh.",
},
{
"role": "user",
Expand All @@ -181,5 +174,33 @@ def get_teamviewer_joke(team_name):
return chat_completion.choices[0].message.content


def get_league_box_scores(league: League):
"""Get the matchups for the current week."""
box_scores = league.box_scores(league.currentMatchupPeriod)
return box_scores


def get_matchup_score_df(matchup: Matchup):
"""Get the score dataframe for a given matchup."""
home_team_name = matchup.home_team.team_abbrev
away_team_name = matchup.away_team.team_abbrev
home_df = (
pd.DataFrame(matchup.home_team_cats)
.T.drop(columns=["result"])
.rename(columns={"score": home_team_name})
)
away_df = (
pd.DataFrame(matchup.away_team_cats)
.T.drop(columns=["result"])
.rename(columns={"score": away_team_name})
)
combined_df = pd.concat([home_df, away_df], axis=1)
combined_df[f"{home_team_name}-{away_team_name}"] = (
combined_df[home_team_name] - combined_df[away_team_name]
)
combined_df = combined_df.astype(float).round(2)
return combined_df


if __name__ == "__main__":
pass
4 changes: 4 additions & 0 deletions src/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
MATCHUP_PAGE_TITLE = "The Thunderdome"

NINE_CATS = ["PTS", "BLK", "STL", "AST", "REB", "TO", "3PM", "FG%", "FT%"]
"""The 9 standard categories in fantasy basketball."""

ALL_RAW_DATA_TABLE_DEF = {
"Team": str,
Expand Down Expand Up @@ -76,7 +77,10 @@
}

WANT_BIG_NUM = ["PTS", "BLK", "STL", "AST", "REB", "FGM", "FGA", "FTM", "FTA", "3PM", "FG%", "FT%"]
"""All stats where a big number is better."""

WANT_BIG_NUM_CATS = ["PTS", "BLK", "STL", "AST", "REB", "3PM", "FG%", "FT%"]
"""Roto Categories where a big number is better."""

WANT_SMALL_NUM = ["Standing", "TO"]
"""All stats where a small number is better."""
21 changes: 17 additions & 4 deletions src/frontend/Spacejam_Dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@

# App configuration
icon_url = "https://spacejam-dashboard.s3.us-east-2.amazonaws.com/assets/the-last-spacejam.jpg"
st.set_page_config(layout="wide", page_title="Spacejam Dashboard", page_icon=icon_url)
st.set_page_config(
page_title="Spacejam Dashboard",
page_icon=icon_url,
menu_items={
"Get Help": "https://www.extremelycoolapp.com/help",
"Report a bug": "https://www.extremelycoolapp.com/bug",
"About": "# This is a header. This is an *extremely* cool app!",
},
)

st.logo(icon_url, size="large")
refresh_in_sec = 600
count = st_autorefresh(interval=refresh_in_sec * 1000, limit=100, key="statscounter")
Expand All @@ -18,16 +27,20 @@ def update_league_data():
return be.get_league()


league_data = update_league_data()
if "league_data" not in st.session_state:
league_data = update_league_data()
st.session_state.league_data = league_data
league_df = be.get_league_cat_data_rankings(league_data)
if "league_df" not in st.session_state:
league_df = be.get_league_cat_data_rankings(league_data)
st.session_state.league_df = league_df
teams = [team.team_name for team in league_data.teams.values()]
if "teams" not in st.session_state:
teams = [team.team_name for team in league_data.teams]
st.session_state.teams = teams

league_data = st.session_state.league_data
teams = st.session_state.teams
league_df = st.session_state.league_df

# Sidebar for page selection
st.sidebar.success("Welcome to the Spacejam Dashboard, written by the Tatums.")
st.sidebar.subheader("And now, a joke powered by AI 🤖")
Expand Down
30 changes: 30 additions & 0 deletions src/frontend/components/html_component.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from dataclasses import dataclass

from espn_api.basketball import Team
from jinja2 import Template


@dataclass
class MatchupInput:
home_team: Team
away_team: Team
home_team_score: int
away_team_score: int
ties: int
matchup_scores: list[dict[str, int]]


def get_matchup_html(data: MatchupInput):
with open("src/frontend/components/matchup.html", "r") as f:
template = Template(f.read())

return template.render(
home_logo=data.home_team.logo_url,
away_logo=data.away_team.logo_url,
home_team=data.home_team.team_name,
away_team=data.away_team.team_name,
wins=data.home_team_score,
losses=data.away_team_score,
ties=data.ties,
matchup_scores=data.matchup_scores,
)
162 changes: 162 additions & 0 deletions src/frontend/components/matchup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fantasy Basketball Dashboard</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: fit-content;
background-color: #0E1117;
color: white;
}

.score-card {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
background: #262730;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 16px;
width: 80%;
margin-bottom: 1px;
color: white;
}

.team {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}

.team img {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 50%;
margin-bottom: 8px;
}

.team-name {
font-size: 16px;
font-weight: bold;
text-align: center;
word-wrap: break-word;
}

.score {
font-size: 24px;
font-weight: bold;
white-space: nowrap;
text-align: center;
flex-shrink: 0;
}

table {
width: 80%;
border-collapse: collapse;
margin-top: 0px;
background: rgba(224, 128, 38, 0.819);
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
padding: 16px;
width: 80%;
border-top: 1px solid rgba(224, 128, 38, 0.819);
}


table th, table td {
padding: 6px;
text-align: center;
border-bottom: 1px solid #0907078e;
border-left: 1px solid #0907071e;
}
table th img {
width: 50px; /* Set a consistent width */
height: 50px; /* Set a consistent height */
object-fit: cover; /* Ensure proper aspect ratio */
}

.highlight {
background-color: rgb(230, 131, 39);;
font-weight: bold;
}
.highlight-black {
background-color: #262730;
}


@media (max-width: 480px) {
.score-card {
flex-direction: row;
text-align: center;
padding: 4px;
width: 95%;
}

.team img {
width: 40px;
height: 40px;
}

.team-name {
font-size: 14px;
}

.score {
font-size: 18px;
}

table {
width: 95%;
}

/* table th, table td {
padding: 8px;
} */
}
</style>
</head>
<body>
<div class="score-card">
<div class="team">
<img src="{{ home_logo }}" alt="Team 1 Logo">
<div class="team-name">{{ home_team }}</div>
</div>
<div class="score">{{ wins }} - {{ ties }} - {{ losses }}</div>
<div class="team">
<img src="{{ away_logo }}" alt="Team 2 Logo">
<div class="team-name">{{ away_team }}</div>
</div>
</div>
<table>
<tbody>
{% for category in matchup_scores %}
<tr>
<td
class="{% if (category.name == 'TO' and category.home < category.away) or (category.name != 'TO' and category.home > category.away) %}highlight{% endif %}">
{{ category.home }}
</td>
<td class="highlight-black">{{ category.name }}</td>
<td
class="{% if (category.name == 'TO' and category.away < category.home) or (category.name != 'TO' and category.away > category.home) %}highlight{% endif %}">
{{ category.away }}
</td>
</tr>
{% endfor %}
</tbody>
</table>

</body>
</html>
Loading

0 comments on commit 33bc596

Please sign in to comment.