Spaces:
Herc
/
Runtime error

File size: 13,711 Bytes
09cb456
 
c94d489
 
 
09cb456
 
 
9707f2e
 
c94d489
09cb456
c94d489
2ef814a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fbe06b5
 
 
 
 
 
2ef814a
7fca4bd
1a8d7cc
2ef814a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c94d489
 
69c4df6
1bccaa4
69c4df6
 
 
 
 
 
 
1bccaa4
c94d489
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69c4df6
1bccaa4
69c4df6
 
 
 
 
 
 
1bccaa4
c94d489
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69c4df6
1bccaa4
69c4df6
 
 
 
 
 
 
1bccaa4
c94d489
 
 
 
09cb456
c94d489
 
09cb456
c94d489
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69c4df6
1bccaa4
69c4df6
 
 
 
 
 
 
1bccaa4
c94d489
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09cb456
 
 
 
 
633d462
 
 
70a2667
 
 
 
 
 
 
9707f2e
c94d489
 
 
9707f2e
2414822
9707f2e
2414822
4d18edc
2414822
9707f2e
b64b0b7
9707f2e
09cb456
 
 
b64b0b7
 
 
 
09cb456
 
 
 
 
 
c73aec5
 
09cb456
 
 
4e81263
c73aec5
c8a0606
c73aec5
 
 
 
0fac78f
09cb456
 
 
a7e3d96
 
 
 
 
 
c73aec5
c94d489
a7e3d96
c94d489
 
a7e3d96
 
 
 
 
 
 
 
 
 
c73aec5
a7e3d96
c73aec5
 
c94d489
a7e3d96
c94d489
 
 
9707f2e
c94d489
 
c73aec5
 
 
 
 
 
 
 
 
c94d489
 
 
9707f2e
c94d489
 
 
 
 
9707f2e
c94d489
 
 
 
 
9707f2e
c94d489
 
1bccaa4
 
09cb456
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import date, timedelta
import random



# [All the scheduling functions and analytics functions here]



# 1. create_schedule
def create_schedule(num_teams, num_conferences, num_inter_games):
    full_schedule = []
    for i in range(num_conferences):
        conference_name = chr(65 + i)  # 'A', 'B', 'C', 'D', ...
        combined_schedule = combine_schedules(conference_name, num_teams, num_inter_games)
        assigned_dates = assign_dates_to_matches(combined_schedule)
        full_schedule.extend(assigned_dates)
    return pd.DataFrame(full_schedule, columns=["Team 1", "Team 2", "Date"])

# 2. combine_schedules
def combine_schedules(conference_name, num_teams, num_inter_games):
    intra_conf_matches = generate_intra_conference_schedule(conference_name, num_teams)
    inter_conf_matches = generate_inter_conference_schedule(conference_name, num_teams, num_inter_games)
    return intra_conf_matches + inter_conf_matches

# 3. generate_intra_conference_schedule
def generate_intra_conference_schedule(conference_name, num_teams):
    teams = [f"{conference_name}{i}" for i in range(1, num_teams + 1)]
    matches = []
    for i in range(len(teams)):
        for j in range(i+1, len(teams)):
            matches.append((teams[i], teams[j]))
            matches.append((teams[j], teams[i]))  # Home and away
    return matches

# 4. generate_inter_conference_schedule
def generate_inter_conference_schedule(conference_name, num_teams, num_inter_games):
    current_conf_teams = [f"{conference_name}{i}" for i in range(1, num_teams + 1)]
    other_confs = [chr(65 + i) for i in range(4) if chr(65 + i) != conference_name]
    other_conf_teams = [f"{conf}{i}" for conf in other_confs for i in range(1, num_teams + 1)]
    matches = []
    for team in current_conf_teams:
        opponents = random.sample(other_conf_teams, num_inter_games)
        for opp in opponents:
            matches.append((team, opp))
    return matches

# 5. assign_dates_to_matches
def assign_dates_to_matches(matches):
    start_date = date(2022, 11, 6)
    end_date = date(2023, 3, 1)
    available_dates = [start_date + timedelta(days=i) for i in range((end_date - start_date).days) if (start_date + timedelta(days=i)).weekday() in [0, 2, 3, 5]]
    random.shuffle(available_dates)
    
    # Ensure cyclic reuse of dates
    extended_dates = available_dates * (len(matches) // len(available_dates) + 1)
    
    return [(match[0], match[1], extended_dates[i]) for i, match in enumerate(matches)]


# 6. generate_mock_historical_data
def generate_mock_historical_data(num_teams, num_conferences, num_inter_games, start_date, end_date):
    full_schedule = []
    for i in range(num_conferences):
        conference_name = chr(65 + i)
        combined_schedule = combine_schedules(conference_name, num_teams, num_inter_games)
        shuffled_dates = assign_dates_to_matches(combined_schedule)
        random.shuffle(shuffled_dates)
        for match in shuffled_dates:
            full_schedule.append({
                "Team 1": match[0],
                "Team 2": match[1],
                "Date": match[2]
            })
    return pd.DataFrame(full_schedule)


# Team Workload Analysis
def team_workload_analysis(schedule_df):
    # Check if the DataFrame is None
    if schedule_df is None:
        plt.figure(figsize=(10, 6))
        plt.text(0.5, 0.5, 'Please generate the schedule first before viewing analytics.', 
                 horizontalalignment='center', verticalalignment='center', 
                 fontsize=14, color='red')
        plt.axis('off')
        plt.tight_layout()
        plt.show()
        return
    """Generate a bar chart showing the number of matches each team has per week."""
    schedule_df['Week'] = schedule_df['Date'].dt.isocalendar().week
    team_counts = schedule_df.groupby(['Week', 'Team 1']).size().unstack().fillna(0)
    
    # Plot
    team_counts.plot(kind='bar', stacked=True, figsize=(15, 7), cmap='Oranges')
    plt.title('Team Workload Analysis')
    plt.ylabel('Number of Matches')
    plt.xlabel('Week Number')
    plt.tight_layout()
    plt.legend(title='Teams', bbox_to_anchor=(1.05, 1), loc='upper left')
    plt.show()

# Match Distribution
def match_distribution(schedule_df):
    # Check if the DataFrame is None
    if schedule_df is None:
        plt.figure(figsize=(10, 6))
        plt.text(0.5, 0.5, 'Please generate the schedule first before viewing analytics.', 
                 horizontalalignment='center', verticalalignment='center', 
                 fontsize=14, color='red')
        plt.axis('off')
        plt.tight_layout()
        plt.show()
        return
    """Generate a histogram showing match distribution across months."""
    schedule_df['Month'] = schedule_df['Date'].dt.month_name()
    month_order = ['November', 'December', 'January', 'February', 'March']
    
    # Plot
    plt.figure(figsize=(10, 6))
    sns.countplot(data=schedule_df, x='Month', order=month_order, palette='Oranges_r')
    plt.title('Match Distribution Across Months')
    plt.ylabel('Number of Matches')
    plt.xlabel('Month')
    plt.tight_layout()
    plt.show()

# Inter-Conference Match Analysis
def inter_conference_analysis(schedule_df):
    # Check if the DataFrame is None
    if schedule_df is None:
        plt.figure(figsize=(10, 6))
        plt.text(0.5, 0.5, 'Please generate the schedule first before viewing analytics.', 
                 horizontalalignment='center', verticalalignment='center', 
                 fontsize=14, color='red')
        plt.axis('off')
        plt.tight_layout()
        plt.show()
        return
    """Generate a heatmap showing inter-conference match frequencies."""
    # Extract the conference from the team names
    schedule_df['Conference 1'] = schedule_df['Team 1'].str[0]
    schedule_df['Conference 2'] = schedule_df['Team 2'].str[0]
    
    # Filter out intra-conference matches
    inter_conference_df = schedule_df[schedule_df['Conference 1'] != schedule_df['Conference 2']]
    
    # Create a crosstab for the heatmap
    heatmap_data = pd.crosstab(inter_conference_df['Conference 1'], inter_conference_df['Conference 2'])
    
    # Ensure every conference combination has a value
    all_conferences = schedule_df['Conference 1'].unique()
    for conf in all_conferences:
        if conf not in heatmap_data.columns:
            heatmap_data[conf] = 0
        if conf not in heatmap_data.index:
            heatmap_data.loc[conf] = 0
    
    heatmap_data = heatmap_data.sort_index().sort_index(axis=1)
    
    # Plot
    plt.figure(figsize=(8, 6))
    sns.heatmap(heatmap_data, annot=True, cmap='Oranges', linewidths=.5, cbar_kws={'label': 'Number of Matches'})
    plt.title('Inter-Conference Match Analysis')
    plt.ylabel('Conference 1')
    plt.xlabel('Conference 2')
    plt.show()

# Commissioner Analytics
def commissioner_analytics(schedule_df, commissioners):
    # Check if the DataFrame is None
    if schedule_df is None:
        plt.figure(figsize=(10, 6))
        plt.text(0.5, 0.5, 'Please generate the schedule first before viewing analytics.', 
                 horizontalalignment='center', verticalalignment='center', 
                 fontsize=14, color='red')
        plt.axis('off')
        plt.tight_layout()
        plt.show()
        return
    """Generate a bar chart showing matches overseen by each commissioner."""
    # Assuming each commissioner oversees a specific conference
    comm_dict = {f"Conference {chr(65+i)}": comm for i, comm in enumerate(commissioners)}
    schedule_df['Commissioner'] = schedule_df['Conference 1'].map(comm_dict)
    
    # Count matches overseen by each commissioner
    commissioner_counts = schedule_df['Commissioner'].value_counts()
    
    # Plot using matplotlib
    plt.figure(figsize=(10, 6))
    plt.bar(commissioner_counts.index, commissioner_counts.values, color='orange')
    plt.title('Matches Overseen by Each Commissioner')
    plt.ylabel('Number of Matches')
    plt.xlabel('Commissioner')
    plt.xticks(rotation=45)
    plt.tight_layout()
    plt.show()



# Streamlit App

st.title("Basketball Game Schedule Generator")

st.set_option('deprecation.showPyplotGlobalUse', False)


if 'num_teams' not in st.session_state:
    st.session_state.num_teams = 10
if 'num_conferences' not in st.session_state:
    st.session_state.num_conferences = 4
if 'num_inter_games' not in st.session_state:
    st.session_state.num_inter_games = 3

# Initialize session state for schedule_df and st.session_state.historical_data
if 'schedule_df' not in st.session_state:
    st.session_state.schedule_df = None

if 'st.session_state.historical_data' not in st.session_state:
    st.session_state.historical_data = None

if st.session_state.historical_data is None:
    st.session_state.historical_data = generate_mock_historical_data(st.session_state.num_teams, st.session_state.num_conferences, st.session_state.num_inter_games, date(2022, 11, 6), date(2023, 3, 1))
    st.session_state.historical_data['Date'] = pd.to_datetime(st.session_state.historical_data['Date'])


    
# Configuration UI
st.header("Configuration")

st.session_state.num_teams = st.number_input("Number of teams per conference:", min_value=2, value=st.session_state.num_teams)
st.session_state.num_conferences = st.number_input("Number of conferences:", min_value=2, value=st.session_state.num_conferences)
st.session_state.num_inter_games = st.number_input("Number of inter-conference games per team:", min_value=1, value=st.session_state.num_inter_games)


commissioners = st.multiselect("Add commissioners:", options=[], default=[])

add_commissioner = st.text_input("New commissioner name:")
if add_commissioner:
    commissioners.append(add_commissioner)
    st.session_state.commissioners = commissioners  # Make sure to update session state


# Schedule Generation
if st.button("Generate Schedule"):
    st.session_state.schedule_df = create_schedule(st.session_state.num_teams, st.session_state.num_conferences, st.session_state.num_inter_games)
    if st.session_state.schedule_df is not None and not st.session_state.schedule_df.empty:
        st.session_state.schedule_df['Date'] = pd.to_datetime(st.session_state.schedule_df['Date'])
        st.success("Schedule generated successfully!")
        print("Stored schedule in session state.")
    else:
        st.error("Failed to generate schedule. Please check input parameters.")


# Schedule Viewing
st.header("View Schedule")

# Generating the list of conferences dynamically based on the number of conferences in session state
conference_options = ["All"] + [f"Conference {chr(65+i)}" for i in range(st.session_state.num_conferences)]
conference_selector = st.selectbox("Select conference to view schedule:", options=conference_options)

# Check if the schedule DataFrame exists and is not empty
if st.session_state.schedule_df is not None and not st.session_state.schedule_df.empty:
    if conference_selector == "All":
        # Display the entire schedule if "All" is selected
        st.dataframe(st.session_state.schedule_df)
    else:
        # Filter the schedule based on the selected conference
        filtered_schedule = st.session_state.schedule_df[
            (st.session_state.schedule_df["Team 1"].str.startswith(conference_selector)) |
            (st.session_state.schedule_df["Team 2"].str.startswith(conference_selector))
        ]

        if filtered_schedule.empty:
            st.write(f"No matches found for {conference_selector}.")  # Provide feedback if no matches are found
        else:
            st.dataframe(filtered_schedule)  # Display the filtered schedule
else:
    # Display a message if no schedule is available
    st.write("No schedule available. Please generate the schedule.")



# Analytics & Comparisons
st.header("Analytics & Comparisons")
analytics_option = st.selectbox("Choose an analysis type:", ["Team Workload Analysis", "Match Distribution", "Inter-Conference Match Analysis", "Commissioner Analytics"])
st.session_state.historical_data['Date'] = pd.to_datetime(st.session_state.historical_data['Date'])

if analytics_option == "Team Workload Analysis":
    if st.session_state.historical_data is not None and not st.session_state.historical_data.empty:
        st.subheader("Historical Data")
        st.pyplot(team_workload_analysis(st.session_state.historical_data))
    if st.session_state.schedule_df is not None and not st.session_state.schedule_df.empty:
        st.subheader("Current Data")
        st.pyplot(team_workload_analysis(st.session_state.schedule_df))
    else:
        st.warning("No current data to display. Generate the schedule first.")


elif analytics_option == "Match Distribution":
    st.subheader("Historical Data")
    st.pyplot(match_distribution(st.session_state.historical_data))
    st.subheader("Current Data")
    st.pyplot(match_distribution(st.session_state.schedule_df))

elif analytics_option == "Inter-Conference Match Analysis":
    st.subheader("Historical Data")
    st.pyplot(inter_conference_analysis(st.session_state.historical_data))
    st.subheader("Current Data")
    st.pyplot(inter_conference_analysis(st.session_state.schedule_df))

elif analytics_option == "Commissioner Analytics":
    st.subheader("Historical Data")
    st.pyplot(commissioner_analytics(st.session_state.historical_data, commissioners))
    st.subheader("Current Data")
    st.pyplot(commissioner_analytics(st.session_state.schedule_df, commissioners))
else:
    st.warning("Please generate the schedule first before viewing analytics.")

# Export functionality can be added later