import base64
import json
from datetime import datetime, timedelta
import pandas as pd
import pytz
import streamlit as st
# File paths
predictions_csv = 'predictions.csv'
users_json = 'users.json'
matches_json = 'matches.json'
outcomes_json = 'match_outcomes.json'
image_path = 'ipl_image.png'
# Initialize CSV and JSON files if they don't exist
def initialize_files():
# Initialize predictions CSV
try:
pd.read_csv(predictions_csv)
except FileNotFoundError:
df = pd.DataFrame(columns=['user_name', 'match_id', 'predicted_winner', 'predicted_motm', 'bid_points'])
df.to_csv(predictions_csv, index=False)
# Load users from JSON
def get_users():
try:
with open(users_json, 'r') as file:
users = json.load(file)
return list(users.keys())
except FileNotFoundError:
return []
# Load matches from JSON
def load_matches():
try:
with open(matches_json, 'r') as f:
return json.load(f)
except FileNotFoundError:
return []
def load_match_outcomes():
try:
with open(outcomes_json, 'r') as file:
return json.load(file)
except FileNotFoundError:
return []
# Load the player list from the JSON file
def load_player_list():
with open('players.json', 'r') as file:
return json.load(file)
def get_base64_of_image(path):
with open(path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode()
# Get today's date in IST to load today's match
def get_current_date_ist():
tz_IST = pytz.timezone('Asia/Kolkata')
datetime_ist = datetime.now(tz_IST)
return datetime_ist.strftime('%Y-%m-%d')
# Function to get matches for today
def get_today_matches():
today = get_current_date_ist()
matches = load_matches()
today_matches = [match for match in matches if match['date'] == today]
return today_matches
# Function to check if prediction submission is allowed
def is_submission_allowed(match_id):
matches = load_matches() # This loads matches correctly with IST times
for match in matches:
if match["match_id"] == match_id:
# Parse the match start time in IST
tz_IST = pytz.timezone('Asia/Kolkata')
match_datetime_str = f'{match["date"]} {match["time"]}'
# The match time string is like "2024-03-21 7:30 PM"
match_datetime = datetime.strptime(match_datetime_str, "%Y-%m-%d %I:%M %p")
match_datetime = tz_IST.localize(match_datetime) # Set the timezone to IST
# Get the current time in IST
current_datetime = datetime.now(tz_IST)
if current_datetime > match_datetime + timedelta(minutes=30):
return False
else:
return True
return False # If match_id not found, default to False
# Submit prediction function
def submit_prediction(
user_name,
match_id,
predicted_winner,
predicted_motm,
bid_points,
max_bid_points
):
# Validation for user selection
if user_name == "Select a user...":
st.warning("Please select a valid user.")
return
# Check if prediction submission is allowed for the match
if not is_submission_allowed(match_id):
st.error("Prediction submission time has passed. Predictions can't be submitted after match start.")
return
if bid_points > max_bid_points:
st.error(f"Your bid points exceed the 20% limit of your total points. Maximum allowed bid points: {max_bid_points}")
return
# Ensure predictions DataFrame is loaded or initialized correctly
try:
predictions = pd.read_csv(predictions_csv)
# Check if all expected columns are present, if not, reinitialize the DataFrame
expected_columns = ['user_name', 'match_id', 'predicted_winner', 'predicted_motm', 'bid_points']
if not all(column in predictions.columns for column in expected_columns):
raise ValueError("CSV file missing one or more columns; Reinitializing.")
except (FileNotFoundError, ValueError) as e:
predictions = pd.DataFrame(columns=expected_columns)
# Check for duplicate prediction for the same match by the same user
if user_name != "Select a user...":
existing_predictions = predictions[(predictions['user_name'] == user_name) & (predictions['match_id'] == match_id)]
if not existing_predictions.empty:
st.error("You've already submitted a prediction for this match.")
return
# Append new prediction
new_prediction = {
'user_name': user_name,
'match_id': match_id,
'predicted_winner': predicted_winner,
'predicted_motm': predicted_motm,
'bid_points': bid_points
}
predictions = pd.concat([predictions, pd.DataFrame([new_prediction])], ignore_index=True)
predictions.to_csv(predictions_csv, index=False)
st.success("Prediction submitted successfully!")
def get_user_total_points(user_name):
with open(users_json, 'r') as file:
users = json.load(file)
return users.get(user_name, 0)
# Define the new function
def calculate_max_bid_points(user_name):
total_points = get_user_total_points(user_name)
max_bid_points = int(total_points * 0.20) # 20% of total points
return max_bid_points
# Streamlit UI
encoded_image = get_base64_of_image(image_path)
custom_css = f"""
"""
# Apply custom CSS
st.markdown(custom_css, unsafe_allow_html=True)
# Use the custom class in a div with your title
st.markdown('
', unsafe_allow_html=True)
st.write("🏆 Predict, Compete, and Win 🏏 - Where Every Guess Counts! 🏆")
user_guide_content = """
### 📘 User Guide
#### Submitting Predictions
- **Match Selection**: Choose the match you want to predict from today's available matches.
- **Team and Player Prediction**: Select the team you predict will win and the "Man of the Match".
- **Bid Points**: Enter the number of points you wish to bid on your prediction. Remember, the maximum you can bid is capped at 20% of your total points.
#### Scoring System
- **Winning Team Prediction**: Correct predictions earn you 1000 points, while incorrect predictions deduct 200 points.
- **Man of the Match Prediction**: Correctly predicting the "Man of the Match" awards you 200 points. No penalty for incorrect guesses.
- **Bonus Points**: An additional 200 points bonus is awarded for getting both the team and "Man of the Match" predictions right.
#### Bid Point Constraints
- You cannot bid more than 20% of your current total points.
- Bid points will be doubled if your prediction is correct, and deducted if incorrect.
#### Rules for Submission
- Predictions must be submitted before the match starts.
- Only one prediction per match is allowed.
- Review your prediction carefully before submission, as it cannot be changed once submitted.
"""
# User Guide as an expander
with st.expander("User Guide 📘"):
st.markdown(user_guide_content)
# Prediction form
with st.expander("Submit Prediction 📝"):
# User selection
user_name = st.selectbox("Select User", ["Select a user..."] + get_users())
# Initialize max_bid_points to None
max_bid_points = None
if user_name != "Select a user...":
max_bid_points = calculate_max_bid_points(user_name)
# Display the max bid points
st.write(f"Maximum bid points you can submit: {max_bid_points}")
# Match selection
matches = get_today_matches()
if matches:
match_choice = st.selectbox("Select Today's Match", matches, format_func=lambda match: f"{match['teams'][0]} vs {match['teams'][1]}")
match_id = match_choice['match_id']
teams = match_choice['teams']
else:
st.write("No matches are scheduled for today.")
st.stop()
# Predictions
predicted_winner = st.selectbox("Predicted Winner", teams)
# Load the player list and populate the dropdown based on the selected team
predicted_motm = ""
player_list = load_player_list()
if predicted_winner in player_list:
players = player_list[predicted_winner]
predicted_motm = st.selectbox("Predicted Man of the Match", players)
# predicted_motm = st.text_input("Predicted Man of the Match")
bid_points = st.number_input("Bid Points", min_value=1, value=100, format="%d")
# Submit button
if st.button("Submit Prediction"):
if user_name != "Select a user...":
submit_prediction(user_name, match_id, predicted_winner, predicted_motm, bid_points, max_bid_points)
# Show predictions
with st.expander("Predictions 🔍"):
# Display predictions
if st.button("Show Predictions"):
try:
df = pd.read_csv(predictions_csv)
st.dataframe(df, hide_index=True)
except FileNotFoundError:
st.write("No predictions have been submitted yet.")
# Show leaderboard
with st.expander("Leaderboard 🏆"):
# Display leaderboard
if st.button("Show Leaderboard"):
try:
with open(users_json, 'r') as f:
users = json.load(f)
leaderboard = sorted(users.items(), key=lambda x: x[1], reverse=True)
df_leaderboard = pd.DataFrame(leaderboard, columns=['User', 'Points'])
# Add a 'Rank' column starting from 1
df_leaderboard['Rank'] = range(1, len(df_leaderboard) + 1)
# Reorder DataFrame columns so 'Rank' is first
df_leaderboard = df_leaderboard[['Rank', 'User', 'Points']]
# Reset index to remove the default index column
df_leaderboard.reset_index(drop=True, inplace=True)
st.dataframe(df_leaderboard, hide_index=True)
except FileNotFoundError:
st.write("Leaderboard data not available.")
############################# Admin Panel ##################################
ADMIN_PASSPHRASE = "admin123"
def load_predictions():
# loading predictions from 'predictions.csv'
try:
return pd.read_csv(predictions_csv)
except FileNotFoundError:
return pd.DataFrame(columns=['user_name', 'match_id', 'predicted_winner', 'predicted_motm', 'bid_points'])
def load_users():
with open(users_json, 'r') as file:
return json.load(file)
def save_users(users):
with open(users_json, 'w') as file:
json.dump(users, file, indent=4)
def save_match_outcomes(outcomes):
with open(outcomes_json, 'w') as file:
json.dump(outcomes, file, indent=4)
def update_leaderboard_and_outcomes(match_id, winning_team, man_of_the_match):
outcomes = load_match_outcomes() # Load existing match outcomes
predictions = load_predictions() # Load existing predictions
users = load_users() # Load existing user points
# Update match outcomes
match_outcome = next((outcome for outcome in outcomes if outcome['match_id'] == match_id), None)
if match_outcome:
match_outcome['winning_team'] = winning_team
match_outcome['man_of_the_match'] = man_of_the_match
else:
outcomes.append({
"match_id": match_id,
"winning_team": winning_team,
"man_of_the_match": man_of_the_match
})
# Update user points based on prediction accuracy
match_predictions = predictions[predictions['match_id'] == match_id]
for _, prediction in match_predictions.iterrows():
user_name = prediction['user_name']
# Initialize user points if not present
users[user_name] = users.get(user_name, 0)
# Points for correct or incorrect team prediction
if prediction['predicted_winner'] == winning_team:
users[user_name] += 1000 # Correct team prediction
# Check for double or nothing bid points
users[user_name] += prediction['bid_points'] * 2
else:
users[user_name] -= 200 # Deduct points for incorrect team prediction
users[user_name] -= prediction['bid_points'] # Lose bid points for incorrect prediction
# Points for correct man of the match prediction
if prediction['predicted_motm'] == man_of_the_match:
users[user_name] += 200 # Correct man of the match prediction
# Bonus for getting both right
if prediction['predicted_winner'] == winning_team:
users[user_name] += 200
# Save updated outcomes and user points
save_match_outcomes(outcomes)
save_users(users)
with st.sidebar:
expander = st.expander("Admin Panel", expanded=False)
admin_pass = expander.text_input("Enter admin passphrase:", type="password", key="admin_pass")
if admin_pass == ADMIN_PASSPHRASE:
expander.success("Authenticated")
matches = get_today_matches() # This function fetches today's matches
# If matches are available, let the admin select one
if matches:
match_selection = expander.selectbox("Select Match", matches, format_func=lambda match: f"{match['teams'][0]} vs {match['teams'][1]}", key="match_selection")
selected_match_id = match_selection['match_id']
teams = match_selection['teams']
# Let admin select the winning team
winning_team = expander.selectbox("Winning Team", teams, key="winning_team")
# Fetch and display players for the selected winning team
player_list = load_player_list()
if winning_team in player_list:
players = player_list[winning_team]
man_of_the_match = expander.selectbox("Man of the Match", players, key="man_of_the_match")
else:
players = []
man_of_the_match = expander.text_input("Man of the Match (Type if not listed)", key="man_of_the_match_fallback")
if expander.button("Submit Match Outcome", key="submit_outcome"):
update_leaderboard_and_outcomes(selected_match_id, winning_team, man_of_the_match)
expander.success("Match outcome submitted and leaderboard updated!")
else:
expander.write("No matches are available for today.")
else:
if admin_pass: # Show error only if something was typed
expander.error("Not authenticated")