cricket-prophet / serve.py
Instantaneous1's picture
first commit
56f6887
from scrape import scrape, get_live_matches
import pandas as pd
import streamlit as st
from streamlit_echarts import st_echarts
import joblib
import numpy as np
import math, os
import datetime, time
import matplotlib.pyplot as plt
import pathlib
for folder in ["data", "model", "history", "result", "temp"]:
pathlib.Path(folder).mkdir(parents=True, exist_ok=True)
# ## Test on realdata
# In[16]:
from model import features
import streamlit_analytics
# features = [
# "batting_team",
# "balls",
# "runs",
# "wickets",
# "wkt_last_5_overs",
# "runrate_last_5_overs",
# "current_RR",
# "average",
# "balls_left",
# "wkts_left",
# "required_RR",
# "projected_score_more",
# "min_score_more",
# "max_score_more",
# "projected_avg_score_more",
# ]
all_teams_enc = list(range(len(np.load("model/team.npy", allow_pickle=True))))
def overtoball(over):
over = str(over)
full = int(over.split(".")[0]) * 6
part = min(int(over.split(".")[-1]), 6)
print(f"{over=}", "balls=", full + part)
return full + part
def save_history(fname, row, total_balls):
row.to_csv(
os.path.join("history", fname),
mode="a" if os.path.isfile(os.path.join("history", fname)) else "w+",
header=not os.path.isfile(os.path.join("history", fname)),
)
fig, ax = plt.subplots()
historydf = pd.read_csv(os.path.join("history", fname))
balls = (total_balls - historydf["balls_left"]).to_list()
runs = historydf["runs"].astype(int).to_list()
ax.plot(balls, runs, label="So Far")
balls.append(total_balls)
pred_runs = runs + [historydf["predicted"].astype(int).iloc[-1]]
ax.plot(balls[-2:], pred_runs[-2:], label="Predicted")
proj_runs = runs + [historydf["projected"].astype(int).iloc[-1]]
ax.plot(balls[-2:], proj_runs[-2:], label="Projected")
ax.annotate(str(runs[-1]), xy=(balls[-2], runs[-1]))
ax.annotate(str(pred_runs[-1]), xy=(balls[-1], pred_runs[-1]))
ax.annotate(str(proj_runs[-1]), xy=(balls[-1], proj_runs[-1]))
plt.xlim([0, total_balls])
plt.ylim([0, max(pred_runs[-1], proj_runs[-1]) + 100])
ax.set_xlabel("Balls")
ax.set_ylabel("Runs")
ax.legend()
return fig
def load_model(format):
return joblib.load(
"model/"
+ (
"t20features.feather.joblib"
if format == "T20"
else "odifeatures.feather.joblib"
if format == "ODI"
else None
)
)
def simulator(args, format):
inputdf = pd.DataFrame([args.values()], columns=args.keys())
model = load_model(format)
h = model.predict(inputdf)
return h
def predict(url):
fname = "".join(list(filter(str.isalnum, url))) + ".csv"
ret = scrape(url)
print(ret)
if len(ret) == 1:
err = ret[0]
return [err]
else:
(
matchState,
score,
run_last_5_overs,
wkt_last_5_overs,
runs,
wkts,
overs,
req_rr,
req,
current_rr,
format,
title,
status,
batting_team,
bowling_team,
batting_team_enc,
bowling_team_enc,
inning,
) = ret
if matchState != "inprogress":
return matchState, None, score, format, title, status, None, None, None, None
total_balls = 120 if format == "T20" else 300 if format == "ODI" else None
balls = overtoball(overs)
rr_last_5_overs = (int(run_last_5_overs) * 6) / min(30, balls)
# current_rr = (runs * 6) / balls
avg = runs / (wkts + 1)
req_rr = req_rr
wkts_left = 10 - wkts
balls_left = (total_balls - balls) if inning == 1 else math.ceil(req * 6 / req_rr)
min_score_avg, max_score_avg = (
math.ceil(balls_left * 0.5),
math.ceil(balls_left * 3),
)
rr_diff = rr_last_5_overs - current_rr
inputs = {
"batting_team": batting_team_enc,
"balls": balls,
"runs": runs,
"wickets": wkts,
"wkt_last_5_overs": wkt_last_5_overs,
"runrate_last_5_overs": rr_last_5_overs,
"current_RR": current_rr,
"runrate_last_5_overs-current_RR": rr_diff,
"average": avg,
"balls_left": int(balls_left),
"wkts_left": int(wkts_left),
"required_RR": -9999,
"projected_score_more": math.ceil(balls_left * ((runs) / (balls))),
"min_score_more": math.ceil(balls_left * 0.5),
"max_score_more": math.ceil(balls_left * 3),
"projected_avg_score_more": math.ceil((10 - wkts) * runs / (1 + wkts)),
}
inputdf = pd.DataFrame(inputs, index=[0])
if batting_team_enc is None:
inputdf = inputdf.drop(columns=["batting_team"])
inputdf = pd.concat([inputdf] * len(all_teams_enc))
inputdf["batting_team"] = all_teams_enc
inputdf = inputdf[features]
model = load_model(format)
h = model.predict(inputdf)
print(f"{h=}")
projected_score_more = balls_left * current_rr / 6
projected = math.ceil(projected_score_more + runs)
predicted_score_more = math.ceil(h.mean() + projected_score_more)
# predicted_score_more = min(max(min_score_avg, predicted_score_more), max_score_avg)
predicted = runs + predicted_score_more
print(f"{runs=}, {projected=}, {predicted=}")
inputdf["timestamp"] = datetime.datetime.now()
inputdf["runs"] = runs
if inning == 2:
target = req + runs
print(f"{target=}")
inputdf["target"] = target
batting_team_win = int(predicted - target)
else:
batting_team_win = None
inputdf["target"] = -9999
inputdf["predicted"] = int(predicted)
inputdf["projected"] = int(projected)
print(inputdf.to_string())
fig = save_history(fname, inputdf, total_balls)
return (
matchState,
predicted,
score,
format,
title,
status,
inning,
batting_team,
batting_team_win,
fig,
)
def getoption(predicted, maxscore):
return {
"series": [
{
"type": "gauge",
"startAngle": 180,
"endAngle": 0,
"min": 0,
"max": maxscore,
"center": ["50%", "50%"],
"splitNumber": 4,
"axisLine": {
"lineStyle": {
"width": 6,
"color": [
[0.25, "#FF403F"],
[0.5, "#FDDD60"],
[0.75, "#00FF00"],
[1, "#0000FF"],
],
}
},
"pointer": {
"icon": "path://M12.8,0.7l12,40.1H0.7L12.8,0.7z",
"length": "12%",
"width": 30,
"offsetCenter": [0, "-60%"],
"itemStyle": {"color": "auto"},
},
"axisTick": {
"length": 10,
"lineStyle": {"color": "auto", "width": 2},
},
"splitLine": {
"length": 15,
"lineStyle": {"color": "auto", "width": 5},
},
"axisLabel": {
"fontSize": 12,
"distance": -60,
},
"title": {
"offsetCenter": [0, "-20%"],
"fontSize": 20,
"color": "#0000FF"
if predicted > maxscore * 0.75
else "#00FF00"
if predicted > maxscore * 0.5
else "#FDDD60"
if predicted > maxscore * 0.25
else "#FF403F",
},
"detail": {
"fontSize": 15,
"offsetCenter": [0, "0%"],
"valueAnimation": True,
"color": "auto",
"formatter": "Predicted Score: {value}",
},
"data": [
{
"value": round(predicted),
}
# {
# "value": round(predicted),
# "name": "Great"
# if predicted > maxscore * 0.75
# else "Decent"
# if predicted > maxscore * 0.5
# else "Average"
# if predicted > maxscore * 0.25
# else "Bad",
# }
],
}
]
}
def timestamp(func):
def caller(*args):
print(
"\n---->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Initiated: ",
datetime.datetime.now(),
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<----",
)
ret = func(*args)
print(
"\n---->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Completed: ",
datetime.datetime.now(),
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<----",
)
return ret
return caller
@timestamp
def render(url):
markdown = []
option = None
print("fetching from", url)
ret = predict(url.strip())
if len(ret) == 1:
err = ret[0]
markdown.append("Error fetching url...")
return markdown, None, None
(
matchState,
predicted,
score,
format,
title,
status,
inning,
batting_team,
batting_team_win,
fig,
) = ret
if matchState:
markdown.append("Live score credits: cricbuzz.com")
if title:
if "|" in title:
l1 = (
title.split("|")[1]
.replace("Cricbuzz.com", "")
.replace("Cricbuzz", "")
)
if l1.strip():
markdown.append(l1.strip())
l2 = (
title.split("|")[0]
.replace("Cricbuzz.com", "")
.replace("Cricbuzz", "")
)
if l2.strip():
markdown.append(l2.strip())
else:
markdown.append(
title.replace("Cricbuzz.com", "").replace("Cricbuzz", "")
)
nutshell = ""
if status:
nutshell += status + "; "
if score:
nutshell += score + "; "
if matchState:
nutshell += matchState + "; "
if nutshell:
markdown.append(nutshell)
# if matchState and matchState != "inprogress":
# markdown.append(matchState)
if predicted:
if inning == 2:
if batting_team_win >= 0:
markdown.append(f"{batting_team} may win")
else:
markdown.append(
f"{batting_team} may lose by {-int(batting_team_win)} runs"
)
maxscore = 300 if format == "T20" else 500 if format == "ODI" else None
option = getoption(predicted, maxscore)
if matchState is None:
markdown.append("Error fetching url...")
return "\n".join(markdown), option, fig
if __name__ == "__main__":
with streamlit_analytics.track(unsafe_password="credict123"):
st.set_page_config(page_title="Cricket Prophet")
st.title("Cricket Prophet")
st.write("**An ML-driven Cricket Score Predictor**")
live_matches = get_live_matches("https://cricbuzz.com")
if live_matches:
option = st.selectbox(
"Choose a live match here",
list(live_matches.keys()) + ["Custom URL", "Simulator"],
)
if option == "Simulator":
format = st.selectbox("Format", ["T20", "ODI"])
args = {}
args["batting_team"] = 1
args["wkt_last_5_overs"] = st.number_input(
"wkt_last_5_overs", value=0.0, step=0.01, format="%f"
)
args["current_RR"] = st.number_input(
"current_RR", value=0.0, step=0.01, format="%f"
)
args["balls_left"] = st.number_input(
"balls_left", value=0.0, step=0.01, format="%f"
)
args["wkts_left"] = st.number_input(
"wkts_left", value=0.0, step=0.01, format="%f"
)
args["runrate_last_5_overs-current_RR"] = (
st.number_input(
"runrate_last_5_overs", value=0.0, step=0.01, format="%f"
)
- args["current_RR"]
)
balls = 300 if format == "ODI" else 120
st.text(
str(int((balls * args["current_RR"] / 6) + simulator(args, format)))
)
else:
if option == "Custom URL":
url = st.text_input("Enter cricbuzz match link")
else:
url = live_matches.get(option)
col1, col2 = st.columns([3.5, 0.6])
with col1:
live = st.button("Live", help="Livestream")
with col2:
fetch = st.button("Fetch", help="Refresh")
col3, _ = st.columns([1, 4])
with col3:
interval = st.number_input(
label="Sync Interval (Seconds)", step=1, min_value=1, value=100
)
placeholder = st.empty()
if fetch:
if url:
markdown, option, fig = render(url)
placeholder.empty()
with placeholder.container():
st.text(markdown)
st.text(f"Last updated at {time.strftime('%H:%M %p')}")
if option:
st_echarts(
option,
width="450px",
height="350px",
key="gauge" + str(datetime.datetime.now()),
)
if fig:
st.pyplot(fig)
if live:
if url:
while True:
markdown, option, fig = render(url)
placeholder.empty()
with placeholder.container():
st.text(markdown)
st.text(f"Last updated at {time.strftime('%H:%M %p')}")
if option:
st_echarts(
option,
width="450px",
height="350px",
key="gauge" + str(datetime.datetime.now()),
)
if fig:
st.pyplot(fig)
else:
break
time.sleep(interval)
else:
st.text("Error fetching matches")