Skip to content

Commit

Permalink
v0.1 - Release to Jammers (#11)
Browse files Browse the repository at this point in the history
* overhaul team viewer, couple small fixes

* clarification on the update cadence
  • Loading branch information
NathanEmb authored Dec 17, 2024
1 parent 891af66 commit 2e91dcb
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 98 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# space-jam-dashboard
Web dashboard for Space Jam fantasy basketball.
# Space Jammers
Web dashboard for the Space Jam fantasy basketball league. It is currently hosted at https://space-jammers.com!

## About
This project [espn-api](https://github.com/cwendt94/espn-api) this app creates a basic web application using [Streamlit](https://streamlit.io/) as the web framework, Pandas for data manipulation, and [Jinja2](https://jinja.palletsprojects.com/en/stable/) for some html delivery inside the application.
12 changes: 6 additions & 6 deletions src/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,17 @@ def get_team_breakdown(team_cat_ranks: dict) -> tuple[dict, dict, dict]:
strengths (list): The categories in which the team excels.
weaknesses (list): The categories in which the team is average.
punts (list): The categories in which the team is weak."""
strengths = {}
weaknesses = {}
punts = {}
strengths = []
weaknesses = []
punts = []

for cat in const.NINE_CATS:
if team_cat_ranks[cat] <= 4:
strengths[cat] = team_cat_ranks[cat]
strengths.append({"label": cat, "value": team_cat_ranks[cat]})
elif team_cat_ranks[cat] >= 8:
punts[cat] = team_cat_ranks[cat]
punts.append({"label": cat, "value": team_cat_ranks[cat]})
else:
weaknesses[cat] = team_cat_ranks[cat]
weaknesses.append({"label": cat, "value": team_cat_ranks[cat]})
return strengths, weaknesses, punts


Expand Down
52 changes: 13 additions & 39 deletions src/frontend/Spacejam_Dashboard.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,36 @@
import matplotlib.pyplot as plt
import streamlit as st
from streamlit_autorefresh import st_autorefresh

import src.backend as be
from src.frontend.streamlit_utils import page_setup

# App configuration
icon_url = "https://spacejam-dashboard.s3.us-east-2.amazonaws.com/assets/the-last-spacejam.jpg"
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")


# Cached data behind it all
@st.cache_data
def update_league_data():
return be.get_league()


if "league_data" not in st.session_state:
league_data = update_league_data()
st.session_state.league_data = 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
if "teams" not in st.session_state:
teams = [team.team_name for team in league_data.teams]
st.session_state.teams = teams
page_setup()

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.success("Welcome to the Space Jammers Lounge, written by the Tatums.")
st.sidebar.subheader("And now, a joke powered by AI 🤖")
st.sidebar.write(be.get_mainpage_joke())

# Main Page content
st.title("Spacejam Dashboard")
st.title("Space Jammers Lounge")
st.subheader("About")
st.markdown(
"This dashboard serves two purposes: \n 1. Be a fun project for me to work on. \n 2. Provide some more data to everyone who isn't already paying for a fancy schmancy site already."
"This site serves two purposes: \n 1. Be a fun project for me to work on. \n 2. Provide some more data to everyone who isn't already paying for a fancy schmancy site already."
)
st.subheader("Category Rankings (Green is good)")
st.markdown(
"This was the first bit of data that I wanted to understand when I was left with my head in my hands after a loss to Will saying 'What are the Tatum's good for?'"
"I hope you find this at minimum, mildly interesting, and at best, useful! Scroll down to see the first bit of data I worked out, and visit the sidebar to see a couple other pages."
)

st.header("Category Rankings")
st.subheader("Green is good", divider=True)
st.markdown(
"This was the first bit of data that I wanted to understand when I was left with my head in my hands after a loss to Will saying 'What are the Tatum's good for?'. It shows each team's ranking per category, hopefully providing insight into punt strategies (or just seeing that your team sucks)."
)
st.info("On mobile, landscape makes this more usable.\n", icon="ℹ️")
st.write("")
gyr = plt.colormaps["RdYlGn"].reversed()
league_df = league_df.set_index("Team")
league_df_styled = league_df.style.background_gradient(cmap=gyr)
Expand Down
7 changes: 7 additions & 0 deletions src/frontend/components/html_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,10 @@ def get_matchup_html(data: MatchupInput):
ties=data.ties,
matchup_scores=data.matchup_scores,
)


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

return template.render(metrics=data)
84 changes: 84 additions & 0 deletions src/frontend/components/team_viewer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Metric Grid</title>
<link rel="stylesheet" href="metrics.css">
</head>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #0E1117;
color: white;
}

.metric-grid-container {
width: 80%;
display: flex;
justify-content: center;
align-items: center;
}

.metric-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(75px, 1fr)); /* Dynamic columns */
gap: 16px;
background: #262730;
border-radius: 12px;
padding: 16px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 800px; /* Optional: constrain the total grid width */
margin: 0 auto; /* Center the grid */
}

.metric-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(224, 128, 38, 0.819);
border-radius: 8px;
padding: 12px;
text-align: center;
color: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.metric-item .metric-value {
font-size: 20px;
font-weight: bold;
margin-bottom: 4px;
}

.metric-item .metric-label {
font-size: 14px;
opacity: 0.8;
}

</style>
<body>
<div class="metric-grid-container">
<div class="metric-grid">
{% for metric in metrics %}
<div class="metric-item">
<div class="metric-value">{{ metric.value }}</div>
<div class="metric-label">{{ metric.label }}</div>
</div>
{% else %}
<div class="metric-item">
<div class="metric-value">No Data</div>
<div class="metric-label">N/A</div>
</div>
{% endfor %}
</div>
</div>
</body>
</html>
36 changes: 8 additions & 28 deletions src/frontend/pages/1_Team_Viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,18 @@
import src.backend as be
import src.constants as const
import src.frontend.figures as fig
import src.frontend.streamlit_utils as su
from src.frontend.components.html_component import get_team_viewer_html
from src.frontend.streamlit_utils import page_setup

# App configuration
icon_url = "https://spacejam-dashboard.s3.us-east-2.amazonaws.com/assets/the-last-spacejam.jpg"
st.set_page_config(page_title="Team Viewer", page_icon=icon_url)
st.logo(icon_url, size="large")


# Cached data behind it all
@st.cache_data
def update_league_data():
return be.get_league()


if "league_data" not in st.session_state:
league_data = update_league_data()
st.session_state.league_data = 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
if "teams" not in st.session_state:
teams = [team.team_name for team in league_data.team_dict.values()]
st.session_state.teams = teams
page_setup()


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

st.title(const.TEAM_PAGE_TITLE)
chosen_team = st.selectbox("Team", teams)
chosen_team = st.sidebar.radio("Team", teams)
st.title(chosen_team)
team_data = league_data.team_dict[chosen_team]
seven_day_stats = be.get_average_team_stats(team_data, 7)
fifteen_day_stats = be.get_average_team_stats(team_data, 15)
Expand All @@ -55,18 +36,17 @@ def update_league_data():
team_data = league_df.loc[league_df["Team"] == chosen_team].to_dict("records")[0]
strengths, weaknesses, punts = be.get_team_breakdown(team_data)

num_cats_per_row = 2
st.subheader("Team Strengths")
st.markdown("Team ranks in top 4 of these categories.")
su.create_metric_grid(strengths, num_cats_per_row)
st.html(get_team_viewer_html(strengths))

st.subheader("Could go either way")
st.markdown("Team ranks in middle 4 of these categories.")
su.create_metric_grid(weaknesses, num_cats_per_row)
st.html(get_team_viewer_html(weaknesses))

st.subheader("Team Punts")
st.markdown("Team ranks in bottom 4 of league in these categories. (hopefully on purpose)")
su.create_metric_grid(punts, num_cats_per_row)
st.html(get_team_viewer_html(punts))

with st.expander("🏀 Individual Player Stats"):
timeframe = st.radio("Past:", ["7 Days", "15 Days", "30 Days"], horizontal=True)
Expand Down
30 changes: 7 additions & 23 deletions src/frontend/pages/2_Matchup_Viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,9 @@
import src.backend as be
import src.constants as const
import src.frontend.components.html_component as html
from src.frontend.streamlit_utils import page_setup

# 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="Matchup Viewer", page_icon=icon_url)
st.logo(icon_url, size="large")


# Cached data behind it all
@st.cache_data
def update_league_data():
return be.get_league()


if "league_data" not in st.session_state:
league_data = update_league_data()
st.session_state.league_data = 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
if "teams" not in st.session_state:
teams = [team.team_name for team in league_data.team_dict.values()]
st.session_state.teams = teams
page_setup()

league_data = st.session_state.league_data
teams = st.session_state.teams
Expand All @@ -40,7 +21,7 @@ def update_league_data():
]


selected_match = st.sidebar.selectbox("Matches", box_scores_formatted)
selected_match = st.sidebar.radio("Matches", box_scores_formatted)
match_index = box_scores_formatted.index(selected_match)

box_score = box_scores[match_index]
Expand All @@ -56,6 +37,9 @@ def update_league_data():
}
)

st.info(
"These are updated once a a day in early morning. If I could make them real time I would but ESPN is mean."
)
matchup_input = html.MatchupInput(
box_score.home_team,
box_score.away_team,
Expand All @@ -64,7 +48,7 @@ def update_league_data():
box_score.home_ties,
matchup_scores=agg_cat_scores,
)
components.html(html.get_matchup_html(matchup_input), height=450)
components.html(html.get_matchup_html(matchup_input), height=425)

st.markdown(
"This is basically just ESPN viewer right now...I get that. I'd like to add actual vs projected for the week + some information surrounding available players and how they might impact your matchup...but it's a lot of work."
Expand Down
36 changes: 36 additions & 0 deletions src/frontend/streamlit_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import streamlit as st
from streamlit_autorefresh import st_autorefresh

import src.backend as be


def create_metric_grid(data: dict[str, float], num_cats_per_row: int = 3):
Expand All @@ -14,3 +17,36 @@ def create_metric_grid(data: dict[str, float], num_cats_per_row: int = 3):
val = data.pop(cat)
rows_of_cols[j][i].metric(cat, val)
j += 1


def page_setup(title="Space Jammers Lounge"):
# App configuration
icon_url = "https://spacejam-dashboard.s3.us-east-2.amazonaws.com/assets/the-last-spacejam.jpg"
st.set_page_config(
page_title=title,
page_icon=icon_url,
menu_items={
"Report a bug": "https://github.com/NathanEmb/space-jam-dashboard/issues",
"About": "https://github.com/NathanEmb/space-jam-dashboard",
},
layout="wide",
)

st.logo(icon_url, size="large")
refresh_in_sec = 600
st_autorefresh(interval=refresh_in_sec * 1000, limit=100, key="statscounter")

# Cached data behind it all
@st.cache_data
def update_league_data():
return be.get_league()

if "league_data" not in st.session_state:
league_data = update_league_data()
st.session_state.league_data = 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
if "teams" not in st.session_state:
teams = [team.team_name for team in league_data.teams]
st.session_state.teams = teams

0 comments on commit 2e91dcb

Please sign in to comment.