318 lines
11 KiB
Python
318 lines
11 KiB
Python
# %%
|
|
import os, sys
|
|
from dotenv import load_dotenv
|
|
load_dotenv()
|
|
|
|
# %%
|
|
|
|
if os.environ.get('SERVER', None):
|
|
PROJECT_PATH = '/home/django/leagues/'
|
|
else:
|
|
PROJECT_PATH = '/home/md/Work/ligalytics/leagues_stable/'
|
|
|
|
sys.path.insert(0, PROJECT_PATH)
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "leagues.settings")
|
|
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
|
|
|
|
from leagues import settings
|
|
|
|
if os.environ.get('SERVER', None):
|
|
settings.DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql'
|
|
settings.DATABASES['default']['HOST'] = '0.0.0.0'
|
|
settings.DATABASES['default']['PORT'] = '5433'
|
|
settings.DATABASES['default']['USER'] = 'leagues_user'
|
|
settings.DATABASES['default']['PASSWORD'] = 'ligalytics'
|
|
settings.DATABASES['default']['NAME'] = 'prod_16'
|
|
else:
|
|
settings.DATABASES['default']['NAME'] = PROJECT_PATH+'/db.sqlite3'
|
|
settings.DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql'
|
|
settings.DATABASES['default']['HOST'] = '0.0.0.0'
|
|
settings.DATABASES['default']['PORT'] = '5432'
|
|
settings.DATABASES['default']['USER'] = 'postgres'
|
|
settings.DATABASES['default']['PASSWORD'] = 'secret123'
|
|
settings.DATABASES['default']['NAME'] = 'mypgsqldb'
|
|
settings.DATABASES['default']['ATOMIC_REQUESTS'] = False
|
|
settings.DATABASES['default']['AUTOCOMMIT'] = True
|
|
settings.DATABASES['default']['CONN_MAX_AGE'] = 0
|
|
settings.DATABASES['default']['CONN_HEALTH_CHECKS'] = False
|
|
settings.DATABASES['default']['OPTIONS'] = {}
|
|
|
|
|
|
import django
|
|
django.setup()
|
|
|
|
from scheduler.models import *
|
|
import pulp
|
|
from pulp import lpSum, value, XPRESS, GUROBI, PULP_CBC_CMD
|
|
from django.db.models import Q
|
|
from django.template.loader import render_to_string
|
|
|
|
from qualifiers.models import *
|
|
from common.models import GlobalTeam, GlobalCountry
|
|
from common.functions import getRandomHexColor
|
|
from scheduler.models import Season, Scenario, Team, DayObj, CountryClash, Country
|
|
from draws.models import SuperGroup
|
|
from qualifiers.draws import groupTeams, optimize_inversions4
|
|
from scheduler.solver.tasks.optimize import optimize
|
|
|
|
import random
|
|
import time
|
|
import json
|
|
import csv
|
|
import numpy as np
|
|
import networkx as nx
|
|
import matplotlib.pyplot as plt
|
|
from datetime import timedelta
|
|
|
|
scenario = Scenario.objects.get(id=9541)
|
|
season = scenario.season
|
|
|
|
supergroups = SuperGroup.objects.filter(draw__season=season).order_by('name')
|
|
teams = Team.objects.filter(season=season,active=True).order_by('pot')
|
|
|
|
|
|
# %%
|
|
nSimulations = season.scenarios.count()
|
|
|
|
|
|
teams_in_group_together = {
|
|
(t1,t2):0 for t1 in teams for t2 in teams
|
|
}
|
|
violated_wishes = defaultdict(lambda:{'violations':0,'comments':defaultdict(lambda:0)})
|
|
violated_blockings = defaultdict(lambda:{'violations':0,'comments':defaultdict(lambda:0)})
|
|
elementary_violations = defaultdict(lambda:defaultdict(lambda:0))
|
|
|
|
for scenario in season.scenarios.all():
|
|
for conference in Conference.objects.filter(scenario=scenario,display_group=True).order_by('name'):
|
|
for t1 in conference.teams.all():
|
|
for t2 in conference.teams.all():
|
|
if t1 != t2:
|
|
teams_in_group_together[(t1,t2)] += 1
|
|
for wish in EncWish.objects.filter(scenario=scenario).exclude(violation="").order_by('prio'):
|
|
violated_wishes[f"{wish.reason}"]['violations'] += 1
|
|
violated_wishes[f"{wish.reason}"]['comments'][wish.violation.strip()] += 1
|
|
for wish in HAWish.objects.filter(scenario=scenario).exclude(violation="").order_by('prio'):
|
|
violated_wishes[f"{wish.reason}"]['violations'] += 1
|
|
violated_wishes[f"{wish.reason}"]['comments'][wish.violation.strip()] += 1
|
|
for wish in Pairing.objects.filter(scenario=scenario).exclude(violation="").order_by('prio'):
|
|
violated_wishes[f"{wish.comment}"]['violations'] += 1
|
|
violated_wishes[f"{wish.comment}"]['comments'][wish.violation.strip()] += 1
|
|
for game in scenario.solutionlist():
|
|
blockings = Blocking.objects.filter(scenario=scenario,day__id=game[0]).filter(Q(team=game[1],type="Home") | Q(team=game[1],type="Away"))
|
|
if blockings:
|
|
for b in blockings:
|
|
violated_blockings[b.team]['violations'] += 1
|
|
violated_blockings[b.team]['comments'][f"{b.type} - {b.day}"] += 1
|
|
|
|
|
|
violated_blockings = dict(sorted(violated_blockings.items(), key=lambda x: x[1]['violations'], reverse=True))
|
|
violated_wishes = dict(sorted(violated_wishes.items(), key=lambda x: x[1]['violations'], reverse=True))
|
|
|
|
|
|
# %%
|
|
|
|
for key,val in violated_wishes.items():
|
|
for k,v in val['comments'].items():
|
|
suffix = ""
|
|
for i in k.split("<br>"):
|
|
if i == "":
|
|
continue
|
|
elif i in ["1 too many","1 too few"]:
|
|
suffix = f": {i}"
|
|
continue
|
|
else:
|
|
split_vio = i.split(":")
|
|
try:
|
|
day_str = datetime.datetime.strptime(split_vio[0].strip(),"%a, %b %d, %Y")
|
|
vio = ":".join(split_vio[1:])
|
|
except:
|
|
vio = ":".join(split_vio[0:])
|
|
|
|
if key in ["Soft winter venue teams shall play as few matches as possible at home in November",
|
|
"No early games on weekdays"]:
|
|
vio = vio.split(" - ")[0].strip()
|
|
|
|
elementary_violations[key][f"{vio}{suffix}"] += v
|
|
|
|
elementary_violations[key] = dict(sorted(elementary_violations[key].items(), key=lambda x: x[1], reverse=True))
|
|
# elementary_violations = dict(sorted(elementary_violations.items(), key=lambda x: sum(x[1].values()), reverse=True))
|
|
|
|
|
|
minVal = 999999
|
|
maxVal = 0
|
|
|
|
for key, val in teams_in_group_together.items():
|
|
if val > 0 and val < minVal:
|
|
minVal = val
|
|
if val > maxVal:
|
|
maxVal = val
|
|
|
|
|
|
mean = np.mean([val for val in teams_in_group_together.values() if val > 0])
|
|
|
|
|
|
# %%
|
|
|
|
|
|
def heatmap_color_for(value):
|
|
if value <= 0.5:
|
|
g = 256
|
|
r = 2 * max(0,value) * 256
|
|
if value > 0.5:
|
|
g = 2*(1-min(1,value))*256
|
|
r = 256
|
|
return f"rgb({r},{g},{0},0.5)"
|
|
|
|
|
|
def percentage(value):
|
|
return f"{round(value/max(nSimulations,1)*100)}%"
|
|
|
|
sol = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
|
|
sol += " \
|
|
<style> \
|
|
table, th, td { \
|
|
border: 1px solid black; \
|
|
border-collapse: collapse; \
|
|
text-align: center; \
|
|
min-width:40px; \
|
|
padding:3px; \
|
|
margin: 3px; \
|
|
font-family : Arial;\
|
|
} \
|
|
h1 {\
|
|
font-family : Arial;\
|
|
}\
|
|
\
|
|
#etable td:nth-child(5),#etable th:nth-child(5) { border-right: 5px solid black; }\
|
|
#etable td:nth-child(9),#etable th:nth-child(9) { border-right: 5px solid black; }\
|
|
#etable td:nth-child(13),#etable th:nth-child(13) { border-right: 5px solid black; }\
|
|
#etable tr:nth-child(4) { border-bottom: 5px solid black; }\
|
|
#etable tr:nth-child(8) { border-bottom: 5px solid black; }\
|
|
#etable tr:nth-child(12) { border-bottom: 5px solid black; }\
|
|
\
|
|
#ftable td:nth-child(3),#ftable th:nth-child(3) { border-right: 5px solid black; }\
|
|
#ftable tr:nth-child(2) { border-bottom: 5px solid black; }\
|
|
</style> \
|
|
"
|
|
sol += "</head><body>"
|
|
sol += "<h2>Probabilities of games</h2>"
|
|
|
|
|
|
for sg in supergroups:
|
|
sol += f"<h3>{sg}</h3>"
|
|
if sg.name == "Nations League D":
|
|
sol += "<table id='ftable' style='border:5px solid black;background-color:whitesmoke'>\n"
|
|
sol += "<thead>\n"
|
|
sol += "<tr>"
|
|
sol += f"<th>n={nSimulations}</th>"
|
|
sol += "<th colspan=2 style='border-right: 5px solid black;'>Pot A</th>"
|
|
sol += "<th colspan=4 style='border-right: 5px solid black;'>Pot B</th>"
|
|
sol += "</tr>"
|
|
else:
|
|
sol += "<table id='etable' style='border:5px solid black;background-color:whitesmoke'>\n"
|
|
sol += "<thead>\n"
|
|
sol += "<tr>"
|
|
sol += f"<th>n={nSimulations}</th>"
|
|
sol += "<th colspan=4 style='border-right: 5px solid black;'>Pot A</th>"
|
|
sol += "<th colspan=4 style='border-right: 5px solid black;'>Pot B</th>"
|
|
sol += "<th colspan=4 style='border-right: 5px solid black;'>Pot C</th>"
|
|
sol += "<th colspan=4>Pot D</th>"
|
|
sol += "</tr>"
|
|
sol += "<tr>\n"
|
|
sol += f"<th></th>\n"
|
|
for t in sg.teams.order_by('pot'):
|
|
sol += f"<th>{t.shortname} - {t.pot}</th>\n"
|
|
sol += "</tr>\n"
|
|
sol += "</thead>\n"
|
|
sol += "<tbody>\n"
|
|
for t1 in sg.teams.order_by('pot'):
|
|
sol += "<tr>\n"
|
|
sol += f"<td>{t1.shortname} - {t.pot}</td>"
|
|
for t2 in sg.teams.order_by('pot'):
|
|
color = heatmap_color_for((abs(teams_in_group_together[(t1,t2)]-mean))/((maxVal-minVal)/2 or 1))
|
|
opacity = "1"
|
|
if teams_in_group_together[(t1,t2)] == 0:
|
|
color = 'lightgrey'
|
|
opacity = "0.7"
|
|
val = f"{percentage(teams_in_group_together[(t1,t2)])}"
|
|
sol += f"<td style='background-color:{color};opacity:{opacity}'>{val}</td>"
|
|
sol += "</tr>\n"
|
|
|
|
sol += "</tbody>\n"
|
|
sol += "</table>\n"
|
|
|
|
|
|
sol += "<br>"
|
|
sol += "<br>"
|
|
sol += "<br>"
|
|
sol += "<hr>"
|
|
|
|
sol += "<h2>Availabilities</h2>"
|
|
sol += "<table id='' style='border:5px solid black'>\n"
|
|
sol += "<thead>\n"
|
|
sol += "<tr>\n"
|
|
for t in violated_blockings.keys():
|
|
sol += f"<th colspan='2'>{t.shortname}</th>\n"
|
|
sol += "</tr>\n"
|
|
sol += "</thead>\n"
|
|
sol += "<tbody>\n"
|
|
sol += "<tr>\n"
|
|
for val in violated_blockings.values():
|
|
sol += f"<td colspan='2'>{percentage(val['violations'])}</td>"
|
|
sol += "</tr>\n"
|
|
sol += "<tr>\n"
|
|
for val in violated_blockings.values():
|
|
sol += f"<td>"
|
|
for c,n in val['comments'].items():
|
|
sol += f"{percentage(n)}<br>"
|
|
sol += f"</td>"
|
|
sol += f"<td>"
|
|
for c,n in val['comments'].items():
|
|
sol += f"{c}<br>"
|
|
sol += f"</td>"
|
|
sol += "</tr>\n"
|
|
sol += "</tbody>\n"
|
|
sol += "</table>\n"
|
|
|
|
|
|
|
|
sol += "<br>"
|
|
sol += "<br>"
|
|
sol += "<br>"
|
|
sol += "<hr>"
|
|
|
|
sol += "<h2>Wishes</h2>"
|
|
sol += "<table id='' style='border:5px solid black'>\n"
|
|
sol += "<thead>\n"
|
|
sol += "<tr>\n"
|
|
for t in elementary_violations.keys():
|
|
sol += f"<th >{t}</th>\n"
|
|
sol += "</tr>\n"
|
|
sol += "</thead>\n"
|
|
sol += "<tbody>\n"
|
|
sol += "<tr>\n"
|
|
for key in elementary_violations.keys():
|
|
val = violated_wishes[key]
|
|
sol += f"<td >{percentage(val['violations'])}</td>"
|
|
sol += "</tr>\n"
|
|
sol += "<tr>\n"
|
|
for key,val in elementary_violations.items():
|
|
sol += f"<td style='text-align:left;vertical-align:top'>"
|
|
for c,n in elementary_violations[key].items():
|
|
sol += f"({percentage(n)}) {c} <br>"
|
|
sol += f"</td>"
|
|
sol += "</tr>\n"
|
|
sol += "</tbody>\n"
|
|
sol += "</table>\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
with open(f'analytics.html', 'w') as f:
|
|
f.write(sol)
|
|
|
|
# %%
|