This commit is contained in:
martin 2024-11-30 09:57:00 +01:00
parent e50d566327
commit 859e17cf5a
20 changed files with 3556 additions and 27 deletions

View File

@ -86,8 +86,8 @@ from math import sqrt, sin, cos, atan2, pi
import numpy as np
# XPRESS ENVIRONMENT
os.environ['XPRESSDIR'] = "/opt/xpressmp_9.2.5"
os.environ['XPRESS'] = "/opt/xpressmp_9.2.5/bin"
os.environ['XPRESSDIR'] = "/opt/xpressmp_9.5.0"
os.environ['XPRESS'] = "/opt/xpressmp_9.5.0/bin"
os.environ['LD_LIBRARY_PATH'] = os.environ['XPRESSDIR'] + \
"/lib:"+os.environ['LD_LIBRARY_PATH']
os.environ['DYLD_LIBRARY_PATH'] = os.environ['XPRESSDIR']+"/lib:"
@ -326,13 +326,64 @@ def Availabilities(scenario_id,showSolution = "0"):
class Draw_Simulator:
def __init__(self,algorithm, opponent_func, html_output, use_db=False):
def __init__(self,algorithm, opponent_func, html_output, use_db=False, use_chess=False):
self.chess = use_chess
self.blue_groups = []
self.red_groups = []
if self.chess:
# basepots {'D': [
# {'id': 47715, 'pot': 'D', 'name': 'Rouen Dragons', 'country': 'France', 'coeff': 2.0, 'lat': 49.443232, 'lon': 1.099971},
# {'id': 47747, 'pot': 'D', 'name': 'Fehérvár AV19', 'country': 'Austria', 'coeff': 3.0, 'lat': 47.1860262, 'lon': 18.4221358},
# {'id': 52169, 'pot': 'D', 'name': 'Sheffield Steelers', 'country': 'United Kingdom', 'coeff': 3.0, 'lat': 53.38112899999999, 'lon': -1.470085},
# {'id': 52170, 'pot': 'D', 'name': 'SønderjyskE Vojens', 'country': 'Denmark', 'coeff': 1.0, 'lat': 55.249489, 'lon': 9.3019649},
# {'id': 52171, 'pot': 'D', 'name': 'Unia Oświęcim', 'country': 'Poland', 'coeff': 1.0, 'lat': 50.0343982, 'lon': 19.2097782},
# {'id': 47741, 'pot': 'D', 'name': 'Storhamar Hamar', 'country': 'Norway', 'coeff': 1.0, 'lat': 60.794442, 'lon': 11.0442983}], 'C': [
# {'id': 52167, 'pot': 'C', 'name': 'KAC Klagenfurt', 'country': 'Austria', 'coeff': 3.0, 'lat': 46.625704, 'lon': 14.3137371},
# {'id': 47742, 'pot': 'C', 'name': 'Växjö Lakers', 'country': 'Sweden', 'coeff': 4.0, 'lat': 56.8790044, 'lon': 14.8058522},
# {'id': 47743, 'pot': 'C', 'name': 'Straubing Tigers', 'country': 'Germany', 'coeff': 4.0, 'lat': 48.8777333, 'lon': 12.5801538},
# {'id': 47755, 'pot': 'C', 'name': 'Sparta Prague', 'country': 'Czechia', 'coeff': 4.0, 'lat': 50.0755381, 'lon': 14.4378005},
# {'id': 52168, 'pot': 'C', 'name': 'Lausanne HC', 'country': 'Switzerland', 'coeff': 3.0, 'lat': 46.5196535, 'lon': 6.6322734},
# {'id': 47737, 'pot': 'C', 'name': 'Lahti Pelicans', 'country': 'Finland', 'coeff': 2.0, 'lat': 60.9826749, 'lon': 25.6612096}], 'B': [
# {'id': 52166, 'pot': 'B', 'name': 'Pinguins Bremerhaven', 'country': 'Germany', 'coeff': 4.0, 'lat': 53.5395845, 'lon': 8.5809424},
# {'id': 47761, 'pot': 'B', 'name': 'Red Bull Salzburg', 'country': 'Austria', 'coeff': 3.0, 'lat': 47.80949, 'lon': 13.05501},
# {'id': 47724, 'pot': 'B', 'name': 'Ilves Tampere', 'country': 'Finland', 'coeff': 4.0, 'lat': 61.4977524, 'lon': 23.7609535},
# {'id': 47735, 'pot': 'B', 'name': 'Dynamo Pardubice', 'country': 'Czechia', 'coeff': 4.0, 'lat': 50.0343092, 'lon': 15.7811994},
# {'id': 47740, 'pot': 'B', 'name': 'Färjestad Karlstad', 'country': 'Sweden', 'coeff': 4.0, 'lat': 59.4002601, 'lon': 13.5009352},
# {'id': 47717, 'pot': 'B', 'name': 'Fribourg-Gottéron', 'country': 'Switzerland', 'coeff': 4.0, 'lat': 46.8064773, 'lon': 7.1619719}], 'A': [
# {'id': 47721, 'pot': 'A', 'name': 'Eisbären Berlin', 'country': 'Germany', 'coeff': 4.0, 'lat': 52.5200066, 'lon': 13.404954},
# {'id': 47719, 'pot': 'A', 'name': 'ZSC Lions Zurich', 'country': 'Switzerland', 'coeff': 4.0, 'lat': 47.4124497, 'lon': 8.5578995},
# {'id': 47756, 'pot': 'A', 'name': 'Oceláři Třinec', 'country': 'Czechia', 'coeff': 4.0, 'lat': 49.677631, 'lon': 18.6707901},
# {'id': 47757, 'pot': 'A', 'name': 'Skellefteå AIK', 'country': 'Sweden', 'coeff': 4.0, 'lat': 64.750244, 'lon': 20.950917},
# {'id': 47725, 'pot': 'A', 'name': 'Tappara Tampere', 'country': 'Finland', 'coeff': 4.0, 'lat': 61.4977524, 'lon': 23.7609535},
# {'id': 47733, 'pot': 'A', 'name': 'Genève-Servette', 'country': 'Switzerland', 'coeff': 4.0, 'lat': 46.2043907, 'lon': 6.1431577}]}
self.blue_teams = [
52169, 47717, 47719
]
self.red_teams = [
47755
]
# self.blue_teams = [
# 47733,47721,52169,47719,47717,
# ]
# self.red_teams = [
# 47724,47755,47725,
# ]
else:
self.blue_teams = []
self.red_teams = []
self.pots = ['D','C','B','A']
if use_db:
scenario_id = 9529
scenario_id = 12098
scenario = Scenario.objects.get(id=scenario_id)
self.basepots = {
@ -354,50 +405,77 @@ class Draw_Simulator:
df_rank = pd.read_csv('rankings_2.0.csv')
for pot in self.basepots:
for team in self.basepots[pot]:
try:
ranking = df_rank[df_rank['TEAM'] == team['name']].iloc[0]
team['coeff'] = ranking['RANK']
except:
team['coeff'] = 0
else:
self.basepots = {
'A': [
{'id': 0, 'pot': 'A', 'name': 'Rögle Ängelholm', 'country': 'Sweden'},
{'id': 1, 'pot': 'A', 'name': 'Färjestad Karlstad', 'country': 'Sweden'},
{'id': 2, 'pot': 'A', 'name': 'EV Zug', 'country': 'Switzerland'},
{'id': 3, 'pot': 'A', 'name': 'Eisbären Berlin', 'country': 'Germany'},
{'id': 4, 'pot': 'A', 'name': 'Tappara Tampere', 'country': 'Finland'},
{'id': 5, 'pot': 'A', 'name': 'Oceláři Třinec',
'country': 'Czech Republic'},
],
'B': [
{'id': 6, 'pot': 'B', 'name': 'Red Bull Salzburg', 'country': 'Austria'},
{'id': 7, 'pot': 'B', 'name': 'Lulea Hockey', 'country': 'Sweden'},
{'id': 8, 'pot': 'B', 'name': 'Fribourg-Gottéron',
'country': 'Switzerland'},
{'id': 9, 'pot': 'B', 'name': 'Red Bull Munich', 'country': 'Germany'},
{'id': 10, 'pot': 'B', 'name': 'Jukurit Mikkeli', 'country': 'Finland'},
{'id': 11, 'pot': 'B', 'name': 'Mountfield HK',
'country': 'Czech Republic'},
],
'C': [
{'id': 12, 'pot': 'C', 'name': 'VS Villach',
'country': 'Austria'},
{'id': 13, 'pot': 'C', 'name': 'ZSC Lions Zürich',
'country': 'Switzerland'},
{'id': 14, 'pot': 'C', 'name': 'Grizzlys Wolfsburg', 'country': 'Germany'},
{'id': 15, 'pot': 'C', 'name': 'Ilves Tampere',
'country': 'Finland'},
{'id': 16, 'pot': 'C', 'name': 'Sparta Prague',
'country': 'Czech Republic'},
{'id': 17, 'pot': 'C', 'name': 'Fehérvár AV19',
'country': 'Austria'},
],
'D': [
{'id': 18, 'pot': 'D', 'name': 'Belfast Giants',
'country': 'United Kingdom'},
{'id': 19, 'pot': 'D', 'name': 'Grenoble', 'country': 'France'},
{'id': 20, 'pot': 'D', 'name': 'GKS Katowice', 'country': 'Poland'},
{'id': 21, 'pot': 'D', 'name': 'Aalborg Pirates', 'country': 'Denmark'},
{'id': 22, 'pot': 'D', 'name': 'Stavanger Oilers', 'country': 'Norway'},
{'id': 23, 'pot': 'D', 'name': 'Slovan Bratislava', 'country': 'Slovakia'},
]
}
@ -683,6 +761,11 @@ class Draw_Simulator:
return opponents, homeGames, awayGames
def groups_1_24_martin_chess(self):
return self.groups_1_24_martin()
def groups_1_24_martin(self):
"""
1 groups of 24 teams
@ -885,9 +968,16 @@ class Draw_Simulator:
xp.Sum([x_xp[t['id'], g] for t in self.teams if t['country'] == c]) <= 2)
if self.chess:
model_xp.addConstraint(xp.Sum(x_xp[t,g] for t in self.red_teams for g in self.blue_groups) <= 0)
model_xp.addConstraint(xp.Sum(x_xp[t,g] for t in self.blue_teams for g in self.red_groups) <= 0)
# do not play other countries more than 3 times
# for t1 in self.teams:
# for c in ['FIN']:
# for c in ['Finland']:
# for (g1, p1), awayGames in self.awayGames.items():
# if p1 == t1['pot']:
# model_xp.addConstraint(
@ -900,7 +990,7 @@ class Draw_Simulator:
# # model_xp.addConstraint(
# xp.Sum([x_xp[t['id'], g] for t in self.teams if t['country'] == c]) <= 2)
# for t in self.teams:
# for c in ['FIN']:
# for c in ['Finland']:
# for t2 in self.teams_by_country[c]:
# for (g1, p1), awayGames in self.awayGames.items():
# if p1 == t1['pot']:
@ -997,14 +1087,15 @@ class Draw_Simulator:
def simulate(self, nTimes):
tt = time.time()
if nTimes < 1:
return None
tmp_stats = defaultdict(lambda: {})
for n in range(nTimes):
print(f"{self.opponent_func.__name__} - Simulation {n}")
print(f"{self.opponent_func.__name__} - Simulation {n} - in {time.time()-tt}")
ttt =time.time()
sol_dict = {
@ -1018,6 +1109,8 @@ class Draw_Simulator:
total_model_time = 0
total_computations = 0
self.blue_groups = []
self.red_groups = []
feasible = False
for p in self.pots:
@ -1039,6 +1132,13 @@ class Draw_Simulator:
total_model_time += modeltime
if feasible:
feasible = True
if self.chess and not self.blue_groups:
if r['id'] in self.blue_teams:
self.blue_groups = [1,3,5] if g % 2 == 1 else [2,4,6]
self.red_groups = [2,4,6] if g % 2 == 1 else [1,3,5]
elif r['id'] in self.red_teams:
self.blue_groups = [2,4,6] if g % 2 == 1 else [1,3,5]
self.red_groups = [1,3,5] if g % 2 == 1 else [2,4,6]
break
else:
# print("\tCONFLICT: skipping group {} for team {}".format(g,r))
@ -1064,7 +1164,7 @@ class Draw_Simulator:
for ag in self.awayGames[g, p]:
t2 = sol_dict[ag[0]][ag[1]]
travel[t1['id']] += self.distance_matrix[t1['id'],t2['id']]
if t2['country'] == 'FIN':
if t2['country'] == 'Switzerland':
travel_finland[t1['id']] += 1
for op in self.opponents[g,p]:
t2 = sol_dict[op[0]][op[1]]
@ -1073,13 +1173,13 @@ class Draw_Simulator:
else:
coefficients[t1['id']] += abs(t1['coeff']-t2['coeff'])
visited_countries[t1['id']].add(t2['country'])
if t2['country'] == 'FIN':
if t2['country'] == 'Switzerland':
visited_finland[t1['id']] += 1
# blockings, breaks = self.create_schedule(sol_dict, n)
blockings = defaultdict(lambda:0)
breaks = defaultdict(lambda:0)
blockings, breaks = self.create_schedule(sol_dict, n)
# blockings = defaultdict(lambda:0)
# breaks = defaultdict(lambda:0)
@ -1220,7 +1320,7 @@ class Draw_Simulator:
# print("\t",game[0],game[1],sol_dict[game[0]][game[1]])
# for game in self.awayGames[g, p]:
# print("\t",game[0],game[1],sol_dict[game[0]][game[1]])
base_scenario_id = 9529
base_scenario_id = 12098
base_scenario = Scenario.objects.get(id=base_scenario_id)
scenario = copy_scenario(base_scenario,f" - {self.opponent_func.__name__} - {nSim}")
@ -1282,7 +1382,13 @@ class Draw_Simulator:
sol += "<thead>\n"
sol += "<tr><th></th>"
for g in self.groups:
sol += f"<th>{g}</th>"
if g in self.blue_groups:
color = 'blue'
elif g in self.red_groups:
color = 'red'
else:
color = 'white'
sol += f"<th style='background-color:{color}'>{g}</th>"
sol += "</tr>"
sol += "</thead>\n"
sol += "<tbody>\n"
@ -1295,10 +1401,16 @@ class Draw_Simulator:
nFixed += 1
else:
color = 'lightyellow'
if sol_dict[g][p]['id'] in self.blue_teams:
text_color = 'blue'
elif sol_dict[g][p]['id'] in self.red_teams:
text_color = 'red'
else:
text_color = 'black'
tpot = sol_dict[g][p]['pot']
tname = sol_dict[g][p]['name']
tcountry = sol_dict[g][p]['country']
sol += f"<td style='background-color:{color}'>({tpot}){tname}({tcountry})</td>"
sol += f"<td style='background-color:{color};color:{text_color}'>({tpot}){tname}({tcountry})</td>"
sol += "</tr>"
sol += "</tbody>\n"
sol += "</table>\n"
@ -1338,22 +1450,26 @@ funcs = [
# Draw_Simulator.groups_6_4,
# Draw_Simulator.groups_3_8,
Draw_Simulator.groups_1_24_martin,
Draw_Simulator.groups_2_12_martin,
Draw_Simulator.groups_1_24_martin_chess,
# Draw_Simulator.groups_2_12_martin,
# Draw_Simulator.groups_2_12_stephan,
# Draw_Simulator.groups_1_24_stephan,
Draw_Simulator.groups_3_8_stephan,
# Draw_Simulator.groups_3_8_stephan,
]
scenario_id = 9529
scenario_id = 12098
scenario = Scenario.objects.get(id=scenario_id)
# Scenario.objects.filter(base_scenario=scenario).delete()
stats = {}
for func in funcs:
if func == Draw_Simulator.groups_1_24_martin_chess:
simulator = Draw_Simulator(algorithm='XP', opponent_func = func, html_output=True,use_db=True,use_chess=True)
else:
simulator = Draw_Simulator(algorithm='XP', opponent_func = func, html_output=True,use_db=True)
nSim = 1000
nSim = 100
stats[func.__name__] = simulator.simulate(nSim)
@ -1391,16 +1507,16 @@ sol += "<td colspan='2'>Coe.</td>"
sol += "<td colspan='1' rowspan='2'>Block</td>"
sol += "<td colspan='1' rowspan='2'>No Travel</td>"
sol += "<td colspan='1' rowspan='2'>Countr.</td>"
sol += "<td colspan='1' rowspan='2'>Play Finland 3x</td>"
sol += "<td colspan='1' rowspan='2'>Travel Finland 3x</td>"
sol += "<td colspan='1' rowspan='2'>Play Switzerland 3x</td>"
sol += "<td colspan='1' rowspan='2'>Travel Switzerland 3x</td>"
for t in simulator.teams:
sol += "<td colspan='2' rowspan='1'>Trav.</td>"
sol += "<td colspan='2' rowspan='1'>Coe.</td>"
sol += "<td colspan='1' rowspan='2'>Block</td>"
sol += "<td colspan='1' rowspan='2'>No Travel</td>"
sol += "<td colspan='1' rowspan='2'>Countr.</td>"
sol += "<td colspan='1' rowspan='2'>Play Finland 3x</td>"
sol += "<td colspan='1' rowspan='2'>Travel Finland 3x</td>"
sol += "<td colspan='1' rowspan='2'>Play Switzerland 3x</td>"
sol += "<td colspan='1' rowspan='2'>Travel Switzerland 3x</td>"
sol += "</tr>"
sol += "<tr>"
sol += "<td>M</td>"

View File

@ -0,0 +1,145 @@
# %%
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
import time
# %%
with open("data/previous_stats_road_distance.json", "r", encoding="utf-8") as f:
stats_previous_road = json.load(f)
stats_previous_road = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_previous_road.items()}
with open("data/previous_stats_road_duration.json", "r", encoding="utf-8") as f:
stats_previous_duration = json.load(f)
stats_previous_duration = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_previous_duration.items()}
with open("data/new_stats_road_distance.json", "r", encoding="utf-8") as f:
stats_new_road = json.load(f)
stats_new_road = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_new_road.items()}
with open("data/new_stats_road_duration.json", "r", encoding="utf-8") as f:
stats_new_duration = json.load(f)
stats_new_duration = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_new_duration.items()}
# %%
print(stats_previous_road['overall'])
print(stats_previous_duration['overall'])
print(stats_new_road['overall'])
print(stats_new_duration['overall'])
overall_stats = {
"previous": {
"road": stats_previous_road['overall'],
"duration": stats_previous_duration['overall'],
"average_max_team_distance": sum([v['max_team_distance'] for k, v in stats_previous_road.items() if k != "overall"]) / len([v['max_team_distance'] for k, v in stats_previous_road.items() if k != "overall"])
},
"new": {
"road": stats_new_road['overall'],
"duration": stats_new_duration['overall'],
"average_max_team_distance": sum([v['max_team_distance'] for k, v in stats_new_road.items() if k != "overall"]) / len([v['max_team_distance'] for k, v in stats_new_road.items() if k != "overall"])
}
}
# %%
import plotly.graph_objects as go
# %%
""" create bar plot for overall stats """
fig = go.Figure()
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["previous"]["road"]['total_distance'], overall_stats["previous"]["duration"]['total_distance']],
name="Previous",
marker_color='rgb(55, 83, 109)'
))
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["new"]["road"]['total_distance'], overall_stats["new"]["duration"]['total_distance']],
name="New",
marker_color='rgb(26, 118, 255)'
))
fig.update_layout(
title="Total Distances",
xaxis_title="",
yaxis_title="Distance in km / Time in m",
barmode='group'
)
fig.show()
# %%
""" create bar plot for overall stats """
fig = go.Figure()
fig.add_trace(go.Bar(
x=["Distance", "Duration", "Max Team Distance"],
y=[overall_stats["previous"]["road"]['average_distance'], overall_stats["previous"]["duration"]['average_distance'], overall_stats["previous"]["average_max_team_distance"]],
name="Previous",
marker_color='rgb(55, 83, 109)'
))
fig.add_trace(go.Bar(
x=["Distance", "Duration", "Max Team Distance"],
y=[overall_stats["new"]["road"]['average_distance'], overall_stats["new"]["duration"]['average_distance'], overall_stats["new"]["average_max_team_distance"]],
name="New",
marker_color='rgb(26, 118, 255)'
))
fig.update_layout(
title="Average Total Distance per Team",
xaxis_title="",
yaxis_title="Distance in km / Time in m",
barmode='group'
)
fig.show()
# %%
""" create bar plot for overall stats """
fig = go.Figure()
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["previous"]["road"]['average_group_distance'], overall_stats["previous"]["duration"]['average_group_distance']],
name="Previous",
marker_color='rgb(55, 83, 109)'
))
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["new"]["road"]['average_group_distance'], overall_stats["new"]["duration"]['average_group_distance']],
name="New",
marker_color='rgb(26, 118, 255)'
))
fig.update_layout(
title="Average Total Distance per Group",
xaxis_title="",
yaxis_title="Distance in km / Time in m",
barmode='group'
)
fig.show()
# %%

59
dfbnet/competitions.py Normal file
View File

@ -0,0 +1,59 @@
import pandas as pd
def get_teams_from_staffel(staffel=None):
dresden = pd.read_excel("data/beispiel_daten_dresden.xlsx")
# staffeln = dresden["STAFFEL"].unique()
if staffel:
teams_in_staffel = {}
teams = dresden[dresden["STAFFEL"] == staffel][
[
"MANNSCHAFT",
"MS_ART",
"SP_KLASSE",
"SCHLUESSEL_ZAHL",
"WUNSCH_WOCHENTAG",
"WUNSCH_TAG",
"WUNSCH_ZEIT",
"SPIELSTAETTE",
]
].to_dict(orient="records")
for t in teams:
if t['MANNSCHAFT'] not in teams_in_staffel:
teams_in_staffel[t['MANNSCHAFT']] = t
return teams_in_staffel
else:
staffeln = dresden["STAFFEL"].unique()
teams_in_staffel = {}
staffel_type = None
for staffel in staffeln:
find_duplicates = []
teams_in_staffel[staffel] = []
teams = dresden[dresden["STAFFEL"] == staffel][
[
"MANNSCHAFT",
"MS_ART",
"SP_KLASSE",
"SCHLUESSEL_ZAHL",
"WUNSCH_WOCHENTAG",
"WUNSCH_TAG",
"WUNSCH_ZEIT",
"SPIELSTAETTE",
]
].to_dict(orient="records")
for t in teams:
if t['MANNSCHAFT'] not in find_duplicates:
teams_in_staffel[staffel].append(t)
find_duplicates.append(t['MANNSCHAFT'])
return staffeln, teams_in_staffel

View File

@ -0,0 +1,649 @@
# %%
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
# %%
def convert_xlsx_to_json():
gmaps = googlemaps.Client(key="AIzaSyB76EhR4OqjdXHQUiTkHZC0Svx_7cPGqyU")
staffel = None
dresden = pd.read_excel("data/beispiel_daten_sachsen.xlsx")
# staffeln = dresden["STAFFEL"].unique()
unique_staffeln = list(
dresden[["MS_ART", "SP_KLASSE", "STAFFEL"]]
.drop_duplicates()
.itertuples(index=False, name=None)
)
competitions_dict = {s: {} for s in unique_staffeln}
teams_in_competition = {}
staffel_type = None
for art, klasse, staffel in unique_staffeln:
find_duplicates = []
teams_in_competition[(art, klasse, staffel)] = []
teams = dresden[
(dresden["MS_ART"] == art)
& (dresden["SP_KLASSE"] == klasse)
& (dresden["STAFFEL"] == staffel)
][
[
"GEB_VEREIN",
"GEB_MS",
"MANNSCHAFT",
"MS_KEY",
"MS_ART",
"SP_KLASSE",
"STAFFEL",
"SCHLUESSEL_ZAHL",
"WUNSCH_WOCHENTAG",
"WUNSCH_TAG",
"WUNSCH_ZEIT",
"SPIELSTAETTE",
]
].to_dict(
orient="records"
)
for t in teams:
if t["MANNSCHAFT"] not in find_duplicates:
teams_in_competition[(art, klasse, staffel)].append(t)
find_duplicates.append(t["MANNSCHAFT"])
geocode_result = gmaps.geocode(
f"{t['GEB_VEREIN']} {t['MANNSCHAFT']} {t['SPIELSTAETTE']}"
)
latitude = 0
longitude = 0
if len(geocode_result) > 0:
location = geocode_result[0]["geometry"]["location"]
latitude = location["lat"]
longitude = location["lng"]
t["LATITUDE"] = latitude
t["LONGITUDE"] = longitude
competitions_dict[(art, klasse, staffel)]["teams"] = teams_in_competition[
(art, klasse, staffel)
]
competitions_dict[(art, klasse, staffel)]["nTeams"] = len(
teams_in_competition[(art, klasse, staffel)]
)
competitions_dict[(art, klasse, staffel)]["art"] = art
competitions_dict[(art, klasse, staffel)]["klasse"] = klasse
competitions_dict[(art, klasse, staffel)]["staffel"] = staffel
competitions_dict_list_keys = {str(k): v for k, v in competitions_dict.items()}
with open("data/sachsen.json", "w", encoding="utf-8") as f:
json.dump(
competitions_dict_list_keys, f, ensure_ascii=False, indent=4, default=str
)
# %%
from math import sqrt, sin, cos, atan2, pi
def degreesToRadians(degrees):
"""Convert degrees to radians"""
return degrees * pi / 180
def distanceInKmByGPS(lat1, lon1, lat2, lon2):
"""Calculate the distance between two points in km"""
earthRadiusKm = 6371
dLat = degreesToRadians(lat2 - lat1)
dLon = degreesToRadians(lon2 - lon1)
lat1 = degreesToRadians(lat1)
lat2 = degreesToRadians(lat2)
a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(lat1) * cos(
lat2
)
c = 2 * atan2(sqrt(a), sqrt(1 - a))
return int(earthRadiusKm * c)
# %%
with open("data/sachsen.json", "r", encoding="utf-8") as f:
competitions = json.load(f)
competitions = {ast.literal_eval(k): v for k, v in competitions.items()}
# STAFFELN PRO ART UND KLASSE
# ('Herren', 'Landesliga') 1
# ('Herren', 'Landesklasse') 3
# ('Frauen', 'Landesliga') 1
# ('Frauen', 'Landesklasse') 3
# ('A-Junioren', 'Landesliga') 1
# ('A-Junioren', 'Landesklasse') 4
# ('Herren', 'Kreisoberliga') 13
# ('Herren', '1.Kreisliga (A)') 19
# ('Herren', '2.Kreisliga (B)') 8
# ('Herren', '3.Kreisliga (C)') 1
# ('Herren', '1.Kreisklasse') 21
# ('Herren', '2.Kreisklasse') 9
# ('A-Junioren', 'Kreisoberliga') 10
# ('A-Junioren', '1.Kreisliga (A)') 6
# ('Frauen', 'Kreisoberliga') 4
# ('Frauen', '1.Kreisliga (A)') 1
# ('Frauen', '1.Kreisklasse') 3
# ('B-Junioren', 'Landesliga') 1
# ('B-Junioren', 'Landesklasse') 4
# ('B-Junioren', 'Kreisoberliga') 13
# ('B-Junioren', '1.Kreisliga (A)') 13
# ('B-Junioren', '1.Kreisklasse') 1
# ('C-Junioren', 'Landesliga') 1
# ('C-Junioren', 'Landesklasse') 4
# ('C-Junioren', 'Kreisoberliga') 16
# ('C-Junioren', '1.Kreisliga (A)') 15
# ('C-Junioren', '1.Kreisklasse') 9
# ('D-Junioren', 'Landesliga') 1
# ('D-Junioren', 'Landesklasse') 6
# ('D-Junioren', 'Kreisoberliga') 16
# ('D-Junioren', '1.Kreisliga (A)') 24
# ('D-Junioren', '2.Kreisliga (B)') 8
# ('D-Junioren', '3.Kreisliga (C)') 2
# ('D-Junioren', '1.Kreisklasse') 33
# ('D-Junioren', '2.Kreisklasse') 10
# ('B-Juniorinnen', 'Landesliga') 1
# ('B-Juniorinnen', 'Landesklasse') 2
# ('C-Juniorinnen', 'Landesklasse') 3
# ('D-Juniorinnen', 'Kreisoberliga') 1
# ('Herren Ü35', 'Kreisoberliga') 4
# ('Herren Ü35', '1.Kreisliga (A)') 3
# ('Herren Ü35', '1.Kreisklasse') 3
# ('Herren Ü35', '2.Kreisklasse') 1
# ('Herren Ü40', '1.Kreisliga (A)') 5
# ('Herren Ü40', '1.Kreisklasse') 1
# ('Herren Ü50', '1.Kreisliga (A)') 1
# ('Herren Ü50', '1.Kreisklasse') 1
# ('Freizeitsport', '1.Kreisliga (A)') 3
# ('Freizeitsport', '1.Kreisklasse') 2
some_colors = [
"red",
"blue",
"green",
"yellow",
"purple",
"orange",
"pink",
"brown",
"black",
"white",
"gray",
"cyan",
"magenta",
"lime",
"indigo",
"violet",
"turquoise",
"gold",
"silver",
"beige",
"maroon",
"olive",
"navy",
"teal",
"coral",
"lavender",
"salmon",
"chocolate",
"crimson",
"aqua",
"ivory",
"khaki",
"plum",
"orchid",
"peru",
"tan",
"tomato",
"wheat",
"azure",
"mint",
"apricot",
"chartreuse",
"amber",
"fuchsia",
"jade",
"ruby",
"amethyst",
"rose",
"sapphire",
"cerulean",
"moss",
"denim",
"copper",
"peach",
"sand",
"pearl",
"mulberry",
"lemon",
"cream",
"ocher",
"brass",
"eggplant",
"cinnamon",
"mustard",
"rust",
"sienna",
"sepia",
"umber",
"limegreen",
"seagreen",
"forestgreen",
"dodgerblue",
"mediumslateblue",
"royalblue",
"firebrick",
"darkolivegreen",
"midnightblue",
"darkturquoise",
"lightcoral",
"palevioletred",
"hotpink",
"deeppink",
"darkkhaki",
"lightseagreen",
"darkslategray",
"slategray",
"lightsteelblue",
"skyblue",
"lightblue",
"powderblue",
"darkorange",
"lightsalmon",
"indianred",
"thistle",
"burlywood",
"mediumaquamarine",
"mediumorchid",
"mediumvioletred",
"papayawhip",
"moccasin",
"bisque",
"blanchedalmond",
"antiquewhite",
"mistyrose",
"lavenderblush",
"linen",
"snow",
"honeydew",
"palegreen",
"lightcyan",
"aliceblue",
"ghostwhite",
"whitesmoke",
"gainsboro",
]
latitude = 51.18292980165227
longitude = 13.11435805600463
gmap = GoogleMapPlotter(
latitude, longitude, 8, apikey="AIzaSyAPzFyMk3ZA0kL9TUlJ_kpV_IY56uBwdrc"
)
def random_color():
return "#{:06x}".format(random.randint(0, 0xFFFFFF))
competition_details = {}
color = None
for staffel, attr in competitions.items():
if (staffel[0], staffel[1]) != ("Herren", "Kreisoberliga"):
# if (staffel[0], staffel[1]) != ('Herren', '1.Kreisklasse'):
continue
competitions[staffel]["distance"] = []
if (staffel[0], staffel[1]) not in competition_details:
competition_details[(staffel[0], staffel[1])] = {
"nStaffeln": 1,
"nTeams": 0,
"previous_distances": [],
"teams": [],
}
color = some_colors.pop(0)
else:
competition_details[(staffel[0], staffel[1])]["nStaffeln"] += 1
color = some_colors.pop(0)
latitudes = []
longitudes = []
markers_text = []
for team1 in attr["teams"]:
competition_details[(staffel[0], staffel[1])]["nTeams"] += 1
competition_details[(staffel[0], staffel[1])]["teams"].append(team1)
for team2 in attr["teams"]:
distance = 0
if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
distance = distanceInKmByGPS(
team1["LATITUDE"],
team1["LONGITUDE"],
team2["LATITUDE"],
team2["LONGITUDE"],
)
competition_details[(staffel[0], staffel[1])][
"previous_distances"
].append(distance)
competitions[staffel]["distance"].append(distance)
latitudes.append(team1["LATITUDE"])
longitudes.append(team1["LONGITUDE"])
markers_text.append(f"{team1['MANNSCHAFT']} @{team1['SPIELSTAETTE']}")
# Plot the points on the map
gmap.scatter(latitudes, longitudes, color=color, size=40, marker=False)
for (lat1, lon1), (lat2, lon2) in itertools.combinations(
zip(latitudes, longitudes), 2
):
gmap.plot([lat1, lat2], [lon1, lon2], color=color, edge_width=2)
for lat, lon, text in zip(latitudes, longitudes, markers_text):
gmap.marker(lat, lon, title=text.replace('"', ""), color=color)
print(color, staffel, attr["nTeams"], sum(attr["distance"]))
for competition, details in competition_details.items():
print(
competition,
details["nStaffeln"],
details["nTeams"],
sum(details["previous_distances"]),
)
# Optionally, draw a line path connecting the points
# gmap.plot(latitudes, longitudes, color='blue', edge_width=2.5)
# Save the map to an HTML file
gmap.draw("map_previous.html")
# %%
# for key, value in competition_details.items():
# print(key,value['nStaffeln'])
# %%
"""" GENERATE ALL DISTANCES BETWEEN TEAMS """
distance_between_teams = {}
for competition, details in competition_details.items():
print(f"Calculating distances for {competition}")
for team1 in details["teams"]:
distance_between_teams[team1["MANNSCHAFT"]] = {}
for team2 in details["teams"]:
distance = 0
if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
distance = distanceInKmByGPS(
team1["LATITUDE"],
team1["LONGITUDE"],
team2["LATITUDE"],
team2["LONGITUDE"],
)
distance_between_teams[team1["MANNSCHAFT"]][team2["MANNSCHAFT"]] = distance
for comp, attr in competition_details.items():
teams = attr["teams"]
print(teams)
"""" RECLUSTERING THE COMPETITION INTO DIVISIONS """
# from pulp import (
# LpVariable,
# LpProblem,
# LpMinimize,
# lpSum,
# LpStatus,
# value,
# LpInteger,
# XPRESS,
# )
# model = LpProblem("Cluster", LpMinimize)
# """ x = 1 if team i is in same division as j, 0 otherwise """
# x = {}
# for team1 in teams:
# for team2 in teams:
# x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] = LpVariable(
# f"team_{team1['MANNSCHAFT']}_{team2['MANNSCHAFT']}",
# lowBound=0,
# upBound=1,
# cat=LpInteger,
# )
# """ g = 1 if team i is i group j, 0 otherwise """
# groups = range(1,20)
# g = {}
# for team in teams:
# for group in groups:
# g[(team["MANNSCHAFT"], group)] = LpVariable(
# f"team_{team['MANNSCHAFT']}_{group}",
# lowBound=0,
# upBound=1,
# cat=LpInteger,
# )
# """ Each team is in exactly one division """
# for team1 in teams:
# model += lpSum(g[(team["MANNSCHAFT"], group)] for group in groups) == 1
# """ Each team is in same divisin as itself """
# for team in teams:
# model += x[(team["MANNSCHAFT"], team["MANNSCHAFT"])] == 1
# """ Each team is in same division with at least 14 and at most 16 other teams"""
# for team1 in teams:
# model += (
# lpSum(
# x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] for team2 in teams
# )
# >= 14
# )
# model += (
# lpSum(
# x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] for team2 in teams
# )
# <= 16
# )
# """ no more than 16 teams in a division """
# for group in groups:
# model += lpSum(g[(team["MANNSCHAFT"], group)] for team in teams) <= 16
# """ if team1 and team2 are paired, than they are in the same division """
# for group in groups:
# for team1 in teams:
# for team2 in teams:
# if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
# model += x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] + g[(team1["MANNSCHAFT"], group)] <= 1 + g[(team2["MANNSCHAFT"], group)]
# model += x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] + g[(team2["MANNSCHAFT"], group)] <= 1 + g[(team1["MANNSCHAFT"], group)]
# """ symmetry constraint """
# for t1, t2 in x.keys():
# model += x[(t1, t2)] == x[(t2, t1)]
# """ MINIMIZE THE TRAVEL DISTANCE """
# model += lpSum(
# distance_between_teams[team1["MANNSCHAFT"]][team2["MANNSCHAFT"]]
# * x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])]
# for team1 in teams
# for team2 in teams
# )
# model.solve(XPRESS(msg=1, gapRel=0.5))
# new_cluster = 1
# for group in groups:
# for team in teams:
# if value(g[(team['MANNSCHAFT'], group)]) > 0.9:
# print(f"TEAM {team['MANNSCHAFT']} - {group}")
# # for team1 in teams:
# # for team2 in teams:
# # if value(x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])]) == 1:
# # print(
# # f"SAME CLUSTER {team1['MANNSCHAFT']} - {team2['MANNSCHAFT']} - {value(g[(team1["MANNSCHAFT"], group)])}"
# # )
# # some_colors = [
# # "red",
# # "blue",
# # "green",
# # "yellow",
# # "purple",
# # "orange",
# # "pink",
# # "brown",
# # "black",
# # "white",
# # "gray",
# # "cyan",
# # "magenta",
# # "lime",
# # "indigo",
# # "violet",
# # "turquoise",
# # "gold",
# # "silver",
# # "beige",
# # "maroon",
# # "olive",
# # "navy",
# # "teal",
# # "coral",
# # "lavender",
# # "salmon",
# # "chocolate",
# # "crimson",
# # "aqua",
# # "ivory",
# # "khaki",
# # "plum",
# # "orchid",
# # "peru",
# # "tan",
# # "tomato",
# # "wheat",
# # "azure",
# # "mint",
# # "apricot",
# # "chartreuse",
# # "amber",
# # "fuchsia",
# # "jade",
# # "ruby",
# # "amethyst",
# # "rose",
# # "sapphire",
# # "cerulean",
# # "moss",
# # "denim",
# # "copper",
# # "peach",
# # "sand",
# # "pearl",
# # "mulberry",
# # "lemon",
# # "cream",
# # "ocher",
# # "brass",
# # "eggplant",
# # "cinnamon",
# # "mustard",
# # "rust",
# # "sienna",
# # "sepia",
# # "umber",
# # "limegreen",
# # "seagreen",
# # "forestgreen",
# # "dodgerblue",
# # "mediumslateblue",
# # "royalblue",
# # "firebrick",
# # "darkolivegreen",
# # "midnightblue",
# # "darkturquoise",
# # "lightcoral",
# # "palevioletred",
# # "hotpink",
# # "deeppink",
# # "darkkhaki",
# # "lightseagreen",
# # "darkslategray",
# # "slategray",
# # "lightsteelblue",
# # "skyblue",
# # "lightblue",
# # "powderblue",
# # "darkorange",
# # "lightsalmon",
# # "indianred",
# # "thistle",
# # "burlywood",
# # "mediumaquamarine",
# # "mediumorchid",
# # "mediumvioletred",
# # "papayawhip",
# # "moccasin",
# # "bisque",
# # "blanchedalmond",
# # "antiquewhite",
# # "mistyrose",
# # "lavenderblush",
# # "linen",
# # "snow",
# # "honeydew",
# # "palegreen",
# # "lightcyan",
# # "aliceblue",
# # "ghostwhite",
# # "whitesmoke",
# # "gainsboro",
# # ]
# # latitude = 51.18292980165227
# # longitude = 13.11435805600463
# # gmap = GoogleMapPlotter(
# # latitude, longitude, 8, apikey="AIzaSyAPzFyMk3ZA0kL9TUlJ_kpV_IY56uBwdrc"
# # )
# # # Plot the points on the map
# # gmap.scatter(latitudes, longitudes, color=color, size=40, marker=False)
# # for (lat1, lon1), (lat2, lon2) in itertools.combinations(
# # zip(latitudes, longitudes), 2
# # ):
# # gmap.plot([lat1, lat2], [lon1, lon2], color=color, edge_width=2)
# # for lat, lon, text in zip(latitudes, longitudes, markers_text):
# # gmap.marker(lat, lon, title=text.replace('"', ""), color=color)
# %%

View File

@ -0,0 +1,896 @@
# %%
from pulp import (
LpVariable,
LpProblem,
LpMinimize,
lpSum,
LpStatus,
value,
LpInteger,
LpContinuous,
XPRESS,
)
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
import time
# %%
def convert_xlsx_to_json():
gmaps = googlemaps.Client(key="AIzaSyB76EhR4OqjdXHQUiTkHZC0Svx_7cPGqyU")
staffel = None
dresden = pd.read_excel("data/beispiel_daten_sachsen.xlsx")
# staffeln = dresden["STAFFEL"].unique()
unique_staffeln = list(
dresden[["MS_ART", "SP_KLASSE", "STAFFEL"]]
.drop_duplicates()
.itertuples(index=False, name=None)
)
competitions_dict = {s: {} for s in unique_staffeln}
teams_in_competition = {}
staffel_type = None
for art, klasse, staffel in unique_staffeln:
find_duplicates = []
teams_in_competition[(art, klasse, staffel)] = []
teams = dresden[
(dresden["MS_ART"] == art)
& (dresden["SP_KLASSE"] == klasse)
& (dresden["STAFFEL"] == staffel)
][
[
"GEB_VEREIN",
"GEB_MS",
"MANNSCHAFT",
"MS_KEY",
"MS_ART",
"SP_KLASSE",
"STAFFEL",
"SCHLUESSEL_ZAHL",
"WUNSCH_WOCHENTAG",
"WUNSCH_TAG",
"WUNSCH_ZEIT",
"SPIELSTAETTE",
]
].to_dict(
orient="records"
)
for t in teams:
if t["MANNSCHAFT"] not in find_duplicates:
teams_in_competition[(art, klasse, staffel)].append(t)
find_duplicates.append(t["MANNSCHAFT"])
geocode_result = gmaps.geocode(
f"{t['GEB_VEREIN']} {t['MANNSCHAFT']} {t['SPIELSTAETTE']}"
)
latitude = 0
longitude = 0
if len(geocode_result) > 0:
location = geocode_result[0]["geometry"]["location"]
latitude = location["lat"]
longitude = location["lng"]
t["LATITUDE"] = latitude
t["LONGITUDE"] = longitude
competitions_dict[(art, klasse, staffel)]["teams"] = teams_in_competition[
(art, klasse, staffel)
]
competitions_dict[(art, klasse, staffel)]["nTeams"] = len(
teams_in_competition[(art, klasse, staffel)]
)
competitions_dict[(art, klasse, staffel)]["art"] = art
competitions_dict[(art, klasse, staffel)]["klasse"] = klasse
competitions_dict[(art, klasse, staffel)]["staffel"] = staffel
competitions_dict_list_keys = {str(k): v for k, v in competitions_dict.items()}
with open("data/sachsen.json", "w", encoding="utf-8") as f:
json.dump(
competitions_dict_list_keys, f, ensure_ascii=False, indent=4, default=str
)
# %%
from math import sqrt, sin, cos, atan2, pi
def degreesToRadians(degrees):
"""Convert degrees to radians"""
return degrees * pi / 180
def distanceInKmByGPS(lat1, lon1, lat2, lon2):
"""Calculate the distance between two points in km"""
earthRadiusKm = 6371
dLat = degreesToRadians(lat2 - lat1)
dLon = degreesToRadians(lon2 - lon1)
lat1 = degreesToRadians(lat1)
lat2 = degreesToRadians(lat2)
a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(lat1) * cos(
lat2
)
c = 2 * atan2(sqrt(a), sqrt(1 - a))
return int(earthRadiusKm * c)
for metric in ["road_distance", "road_duration", "flight_distance"]:
print("\n\n#######################################################")
print(f"Calculating {metric}")
# %%
""" read csv and skip first row """
distance_dict = {}
with open("data/distances.csv", "r", encoding="utf-8") as f:
csv_distances = f.readlines()
for i, row in enumerate(csv_distances):
if i == 0:
continue
_, _, team1, team2, road_distance, road_duration, flight_distance = (
row.split(",")
)
distance_dict[(team1, team2)] = {
"road_distance": float(road_distance),
"road_duration": float(road_duration),
"flight_distance": float(flight_distance),
}
# %%
with open("data/sachsen.json", "r", encoding="utf-8") as f:
competitions = json.load(f)
competitions = {ast.literal_eval(k): v for k, v in competitions.items()}
# region
# STAFFELN PRO ART UND KLASSE
# ('Herren', 'Landesliga') 1
# ('Herren', 'Landesklasse') 3
# ('Frauen', 'Landesliga') 1
# ('Frauen', 'Landesklasse') 3
# ('A-Junioren', 'Landesliga') 1
# ('A-Junioren', 'Landesklasse') 4
# ('Herren', 'Kreisoberliga') 13
# ('Herren', '1.Kreisliga (A)') 19
# ('Herren', '2.Kreisliga (B)') 8
# ('Herren', '3.Kreisliga (C)') 1
# ('Herren', '1.Kreisklasse') 21
# ('Herren', '2.Kreisklasse') 9
# ('A-Junioren', 'Kreisoberliga') 10
# ('A-Junioren', '1.Kreisliga (A)') 6
# ('Frauen', 'Kreisoberliga') 4
# ('Frauen', '1.Kreisliga (A)') 1
# ('Frauen', '1.Kreisklasse') 3
# ('B-Junioren', 'Landesliga') 1
# ('B-Junioren', 'Landesklasse') 4
# ('B-Junioren', 'Kreisoberliga') 13
# ('B-Junioren', '1.Kreisliga (A)') 13
# ('B-Junioren', '1.Kreisklasse') 1
# ('C-Junioren', 'Landesliga') 1
# ('C-Junioren', 'Landesklasse') 4
# ('C-Junioren', 'Kreisoberliga') 16
# ('C-Junioren', '1.Kreisliga (A)') 15
# ('C-Junioren', '1.Kreisklasse') 9
# ('D-Junioren', 'Landesliga') 1
# ('D-Junioren', 'Landesklasse') 6
# ('D-Junioren', 'Kreisoberliga') 16
# ('D-Junioren', '1.Kreisliga (A)') 24
# ('D-Junioren', '2.Kreisliga (B)') 8
# ('D-Junioren', '3.Kreisliga (C)') 2
# ('D-Junioren', '1.Kreisklasse') 33
# ('D-Junioren', '2.Kreisklasse') 10
# ('B-Juniorinnen', 'Landesliga') 1
# ('B-Juniorinnen', 'Landesklasse') 2
# ('C-Juniorinnen', 'Landesklasse') 3
# ('D-Juniorinnen', 'Kreisoberliga') 1
# ('Herren Ü35', 'Kreisoberliga') 4
# ('Herren Ü35', '1.Kreisliga (A)') 3
# ('Herren Ü35', '1.Kreisklasse') 3
# ('Herren Ü35', '2.Kreisklasse') 1
# ('Herren Ü40', '1.Kreisliga (A)') 5
# ('Herren Ü40', '1.Kreisklasse') 1
# ('Herren Ü50', '1.Kreisliga (A)') 1
# ('Herren Ü50', '1.Kreisklasse') 1
# ('Freizeitsport', '1.Kreisliga (A)') 3
# ('Freizeitsport', '1.Kreisklasse') 2
# endregion
some_colors = [
"red",
"blue",
"green",
"yellow",
"purple",
"orange",
"pink",
"brown",
"black",
"white",
"gray",
"cyan",
"magenta",
"lime",
"indigo",
"violet",
"turquoise",
"gold",
"silver",
"beige",
"maroon",
"olive",
"navy",
"teal",
"coral",
"lavender",
"salmon",
"chocolate",
"crimson",
"aqua",
"ivory",
"khaki",
"plum",
"orchid",
"peru",
"tan",
"tomato",
"wheat",
"azure",
"mint",
"apricot",
"chartreuse",
"amber",
"fuchsia",
"jade",
"ruby",
"amethyst",
"rose",
"sapphire",
"cerulean",
"moss",
"denim",
"copper",
"peach",
"sand",
"pearl",
"mulberry",
"lemon",
"cream",
"ocher",
"brass",
"eggplant",
"cinnamon",
"mustard",
"rust",
"sienna",
"sepia",
"umber",
"limegreen",
"seagreen",
"forestgreen",
"dodgerblue",
"mediumslateblue",
"royalblue",
"firebrick",
"darkolivegreen",
"midnightblue",
"darkturquoise",
"lightcoral",
"palevioletred",
"hotpink",
"deeppink",
"darkkhaki",
"lightseagreen",
"darkslategray",
"slategray",
"lightsteelblue",
"skyblue",
"lightblue",
"powderblue",
"darkorange",
"lightsalmon",
"indianred",
"thistle",
"burlywood",
"mediumaquamarine",
"mediumorchid",
"mediumvioletred",
"papayawhip",
"moccasin",
"bisque",
"blanchedalmond",
"antiquewhite",
"mistyrose",
"lavenderblush",
"linen",
"snow",
"honeydew",
"palegreen",
"lightcyan",
"aliceblue",
"ghostwhite",
"whitesmoke",
"gainsboro",
]
latitude = 51.18292980165227
longitude = 13.11435805600463
gmap = GoogleMapPlotter(
latitude, longitude, 8, apikey="AIzaSyAPzFyMk3ZA0kL9TUlJ_kpV_IY56uBwdrc"
)
def random_color():
return "#{:06x}".format(random.randint(0, 0xFFFFFF))
previous_statistics = {}
competition_details = {}
color = None
for staffel, attr in competitions.items():
if (staffel[0], staffel[1]) != ("Herren", "Kreisoberliga"):
# if (staffel[0], staffel[1]) != ('Herren', '1.Kreisklasse'):
continue
competitions[staffel]["distance"] = []
if (staffel[0], staffel[1]) not in competition_details:
competition_details[(staffel[0], staffel[1])] = {
"nStaffeln": 1,
"nTeams": 0,
"previous_distances": [],
"teams": [],
}
color = some_colors.pop(0)
else:
competition_details[(staffel[0], staffel[1])]["nStaffeln"] += 1
color = some_colors.pop(0)
latitudes = []
longitudes = []
markers_text = []
distance_for_team = {}
for team1 in attr["teams"]:
competition_details[(staffel[0], staffel[1])]["nTeams"] += 1
competition_details[(staffel[0], staffel[1])]["teams"].append(team1)
distance_for_team[team1["MANNSCHAFT"]] = []
for team2 in attr["teams"]:
distance = 0
if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
distance = distance_dict[
(team1["MANNSCHAFT"], team2["MANNSCHAFT"])
][metric]
competition_details[(staffel[0], staffel[1])][
"previous_distances"
].append(distance)
competitions[staffel]["distance"].append(distance)
distance_for_team[team1["MANNSCHAFT"]].append(distance)
latitudes.append(team1["LATITUDE"])
longitudes.append(team1["LONGITUDE"])
markers_text.append(f"{team1['MANNSCHAFT']} @{team1['SPIELSTAETTE']}")
# Plot the points on the map
gmap.scatter(latitudes, longitudes, color=color, size=40, marker=False)
for (lat1, lon1), (lat2, lon2) in itertools.combinations(
zip(latitudes, longitudes), 2
):
gmap.plot([lat1, lat2], [lon1, lon2], color=color, edge_width=2)
for lat, lon, text in zip(latitudes, longitudes, markers_text):
gmap.marker(lat, lon, title=text.replace('"', ""), color=color)
print(color, staffel, attr["nTeams"], sum(attr["distance"]))
previous_statistics[staffel] = {
"nTeams": attr["nTeams"],
"total_distance": sum(attr["distance"]),
"average_distance": sum(attr["distance"]) / attr["nTeams"],
"max_distance": max(attr["distance"]),
"min_distance": min(attr["distance"]),
"max_team": max(distance_for_team, key=lambda x: sum(distance_for_team[x])),
"max_team_distance": max(
[sum(distance_for_team[x]) for x in distance_for_team]
),
"min_team": min(distance_for_team, key=lambda x: sum(distance_for_team[x])),
"min_team_distance": min(
[sum(distance_for_team[x]) for x in distance_for_team]
),
}
""" GATHER SOME PREVIOUS STATISTICS """
for key, val in previous_statistics.items():
print(key, val)
""" add overall statistics """
for competition, details in competition_details.items():
print(
competition,
details["nStaffeln"],
details["nTeams"],
sum(details["previous_distances"]),
)
previous_statistics["overall"] = {
"nStaffeln": sum(
[details["nStaffeln"] for details in competition_details.values()]
),
"nTeams": sum(
[details["nTeams"] for details in competition_details.values()]
),
"total_distance": sum(
[
sum(details["previous_distances"])
for details in competition_details.values()
]
),
"average_distance": sum(
[
sum(details["previous_distances"])
for details in competition_details.values()
]
)
/ sum([details["nTeams"] for details in competition_details.values()]),
"max_distance": max(
[
max(details["previous_distances"])
for details in competition_details.values()
]
),
"min_distance": min(
[
min(details["previous_distances"])
for details in competition_details.values()
]
),
"average_group_distance": sum(
[
sum(details["previous_distances"])
for details in competition_details.values()
]
)
/ sum([details["nStaffeln"] for details in competition_details.values()]),
}
previous_statistics_str_keys = {str(k): v for k, v in previous_statistics.items()}
with open(f"data/previous_stats_{metric}.json", "w", encoding="utf-8") as f:
json.dump(
previous_statistics_str_keys, f, ensure_ascii=False, indent=4, default=str
)
# Optionally, draw a line path connecting the points
# gmap.plot(latitudes, longitudes, color='blue', edge_width=2.5)
# Save the map to an HTML file
gmap.draw(f"map_previous_{metric}.html")
# %%
# for key, value in competition_details.items():
# print(key,value['nStaffeln'])
# %%
"""" GENERATE ALL DISTANCES BETWEEN TEAMS """
distance_between_teams = {}
for competition, details in competition_details.items():
print(f"Calculating distances for {competition}")
for team1 in details["teams"]:
distance_between_teams[team1["MANNSCHAFT"]] = {}
for team2 in details["teams"]:
distance = 0
if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
distance = distance_dict[
(team1["MANNSCHAFT"], team2["MANNSCHAFT"])
][metric]
distance_between_teams[team1["MANNSCHAFT"]][
team2["MANNSCHAFT"]
] = distance
for comp, attr in competition_details.items():
teams = attr["teams"]
"""" RECLUSTERING THE COMPETITION INTO DIVISIONS """
model = LpProblem("Cluster", LpMinimize)
""" x = 1 if team i is in same division as j, 0 otherwise """
x = {}
for team1 in teams:
for team2 in teams:
x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] = LpVariable(
f"team_{team1['MANNSCHAFT']}_{team2['MANNSCHAFT']}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
""" g = 1 if team i is i group j, 0 otherwise """
groups = range(1, 14)
g = {}
for team in teams:
for group in groups:
g[(team["MANNSCHAFT"], group)] = LpVariable(
f"team_{team['MANNSCHAFT']}_{group}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
""" Each team is in exactly one division """
for team in teams:
model += lpSum(g[(team["MANNSCHAFT"], group)] for group in groups) == 1
""" Each team is in same divisin as itself """
for team in teams:
model += x[(team["MANNSCHAFT"], team["MANNSCHAFT"])] == 1
""" Each team is in same division with at least 14 and at most 16 other teams"""
for team1 in teams:
model += (
lpSum(x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] for team2 in teams)
>= 14
)
model += (
lpSum(x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])] for team2 in teams)
<= 16
)
if False:
""" no more than 16 teams in a division """
for group in groups:
model += lpSum(g[(team["MANNSCHAFT"], group)] for team in teams) <= 16
""" use each group / at least one team per group """
for group in groups:
model += lpSum(g[(team["MANNSCHAFT"], group)] for team in teams) >= 1
model += lpSum(g[(team["MANNSCHAFT"], 1)] for team in teams) == 14
model += lpSum(g[(team["MANNSCHAFT"], 2)] for team in teams) == 14
model += lpSum(g[(team["MANNSCHAFT"], 3)] for team in teams) == 16
model += lpSum(g[(team["MANNSCHAFT"], 4)] for team in teams) == 14
model += lpSum(g[(team["MANNSCHAFT"], 5)] for team in teams) == 14
model += lpSum(g[(team["MANNSCHAFT"], 6)] for team in teams) == 14
model += lpSum(g[(team["MANNSCHAFT"], 7)] for team in teams) == 16
model += lpSum(g[(team["MANNSCHAFT"], 8)] for team in teams) == 16
model += lpSum(g[(team["MANNSCHAFT"], 9)] for team in teams) == 14
model += lpSum(g[(team["MANNSCHAFT"], 10)] for team in teams) == 14
model += lpSum(g[(team["MANNSCHAFT"], 11)] for team in teams) == 15
model += lpSum(g[(team["MANNSCHAFT"], 12)] for team in teams) == 16
model += lpSum(g[(team["MANNSCHAFT"], 13)] for team in teams) == 14
""" if team1 and team2 are paired, than they are in the same division """
for group in groups:
for team1 in teams:
for team2 in teams:
if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
model += (
x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])]
+ g[(team1["MANNSCHAFT"], group)]
<= 1 + g[(team2["MANNSCHAFT"], group)]
)
model += (
x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])]
+ g[(team2["MANNSCHAFT"], group)]
<= 1 + g[(team1["MANNSCHAFT"], group)]
)
""" symmetry constraint """
for t1, t2 in x.keys():
model += x[(t1, t2)] == x[(t2, t1)]
""" MINIMIZE THE TRAVEL DISTANCE """
model += lpSum(
distance_between_teams[team1["MANNSCHAFT"]][team2["MANNSCHAFT"]]
* x[(team1["MANNSCHAFT"], team2["MANNSCHAFT"])]
for team1 in teams
for team2 in teams
)
model.solve(XPRESS(msg=1, gapRel=0.01, timeLimit=1))
localsearch_time = 3600*2
start_time = time.time()
while time.time() - start_time < localsearch_time:
used_groups = [
group
for group in groups
if sum(value(g[(team["MANNSCHAFT"], group)]) for team in teams) > 0.9
]
group_size = random.randint(2, 6)
opt_groups = random.sample(used_groups, group_size)
opt_stalltime = group_size * 25
print(f"Time: {time.time() - start_time}")
print("Optimizing groups", opt_groups)
for key in x.keys():
x[key].setInitialValue(value(x[key]))
x[key].lowBound = value(x[key])
for key in g.keys():
g[key].setInitialValue(value(g[key]))
if key[1] in opt_groups:
g[key].lowBound = 0
for (t1,t2) in x.keys():
if t1 == key[0] or t2 == key[0]:
x[(t1,t2)].lowBound = 0
else:
g[key].lowBound = value(g[key])
model.solve(
XPRESS(
msg=1,
gapRel=0.01,
warmStart=True,
options=[f"MAXSTALLTIME={opt_stalltime}"],
)
)
some_colors = [
"red",
"blue",
"green",
"yellow",
"purple",
"orange",
"pink",
"brown",
"black",
"white",
"gray",
"cyan",
"magenta",
"lime",
"indigo",
"violet",
"turquoise",
"gold",
"silver",
"beige",
"maroon",
"olive",
"navy",
"teal",
"coral",
"lavender",
"salmon",
"chocolate",
"crimson",
"aqua",
"ivory",
"khaki",
"plum",
"orchid",
"peru",
"tan",
"tomato",
"wheat",
"azure",
"mint",
"apricot",
"chartreuse",
"amber",
"fuchsia",
"jade",
"ruby",
"amethyst",
"rose",
"sapphire",
"cerulean",
"moss",
"denim",
"copper",
"peach",
"sand",
"pearl",
"mulberry",
"lemon",
"cream",
"ocher",
"brass",
"eggplant",
"cinnamon",
"mustard",
"rust",
"sienna",
"sepia",
"umber",
"limegreen",
"seagreen",
"forestgreen",
"dodgerblue",
"mediumslateblue",
"royalblue",
"firebrick",
"darkolivegreen",
"midnightblue",
"darkturquoise",
"lightcoral",
"palevioletred",
"hotpink",
"deeppink",
"darkkhaki",
"lightseagreen",
"darkslategray",
"slategray",
"lightsteelblue",
"skyblue",
"lightblue",
"powderblue",
"darkorange",
"lightsalmon",
"indianred",
"thistle",
"burlywood",
"mediumaquamarine",
"mediumorchid",
"mediumvioletred",
"papayawhip",
"moccasin",
"bisque",
"blanchedalmond",
"antiquewhite",
"mistyrose",
"lavenderblush",
"linen",
"snow",
"honeydew",
"palegreen",
"lightcyan",
"aliceblue",
"ghostwhite",
"whitesmoke",
"gainsboro",
]
latitude = 51.18292980165227
longitude = 13.11435805600463
gmap = GoogleMapPlotter(
latitude, longitude, 8, apikey="AIzaSyAPzFyMk3ZA0kL9TUlJ_kpV_IY56uBwdrc"
)
new_statistics = {}
f = open(f"data/new_solution_{metric}.csv", "w")
for group in groups:
print(f"GROUP {group}")
new_statistics[group] = {
"nTeams": 0,
"total_distance": 0,
"average_distance": 0,
"max_distance": 0,
"min_distance": 0,
"max_team": "",
"max_team_distance": 0,
"min_team": "",
"min_team_distance": 0,
}
latitudes = []
longitudes = []
markers_text = []
color = some_colors.pop(0)
for team in teams:
if value(g[(team["MANNSCHAFT"], group)]) > 0.9:
print(f"TEAM {team['MANNSCHAFT']} - {group}")
f.write(f"{team['MANNSCHAFT']},{group}\n")
latitudes.append(team["LATITUDE"])
longitudes.append(team["LONGITUDE"])
markers_text.append(f"{team['MANNSCHAFT']} @{team['SPIELSTAETTE']}")
new_statistics[group]["nTeams"] += 1
for t1, t2 in x.keys():
if t1 == team["MANNSCHAFT"] and value(x[(t1, t2)]) > 0.9:
new_statistics[group][
"total_distance"
] += distance_between_teams[t1][t2]
new_statistics[group]["average_distance"] = new_statistics[group][
"total_distance"
] / (max(new_statistics[group]["nTeams"], 1))
teams_in_group = [
team["MANNSCHAFT"]
for team in teams
if value(g[(team["MANNSCHAFT"], group)]) > 0.9
]
new_statistics[group]["max_distance"] = max(
[
distance_between_teams[t1][t2]
for t1 in teams_in_group
for t2 in teams_in_group
],
default=0,
)
new_statistics[group]["min_distance"] = min(
[
distance_between_teams[t1][t2]
for t1 in teams_in_group
for t2 in teams_in_group
],
default=0,
)
new_statistics[group]["max_team"] = max(
teams_in_group,
key=lambda x: sum([distance_between_teams[x][t2] for t2 in teams_in_group]),
default=0,
)
new_statistics[group]["max_team_distance"] = sum(
[
distance_between_teams[new_statistics[group]["max_team"]][t2]
for t2 in teams_in_group
]
)
new_statistics[group]["min_team"] = min(
teams_in_group,
key=lambda x: sum([distance_between_teams[x][t2] for t2 in teams_in_group]),
default=0,
)
new_statistics[group]["min_team_distance"] = sum(
[
distance_between_teams[new_statistics[group]["min_team"]][t2]
for t2 in teams_in_group
]
)
# Plot the points on the map
gmap.scatter(latitudes, longitudes, color=color, size=40, marker=False)
for (lat1, lon1), (lat2, lon2) in itertools.combinations(
zip(latitudes, longitudes), 2
):
gmap.plot([lat1, lat2], [lon1, lon2], color=color, edge_width=2)
for lat, lon, text in zip(latitudes, longitudes, markers_text):
gmap.marker(lat, lon, title=text.replace('"', ""), color=color)
f.close()
gmap.draw(f"map_new_{metric}.html")
new_statistics["overall"] = {
"nGroups": len(
[group for group in groups if new_statistics[group]["nTeams"] > 0]
),
"nTeams": sum([new_statistics[group]["nTeams"] for group in groups]),
"total_distance": sum(
[new_statistics[group]["total_distance"] for group in groups]
),
"average_distance": sum(
[new_statistics[group]["total_distance"] for group in groups]
)
/ sum([new_statistics[group]["nTeams"] for group in groups]),
"max_distance": max(
[new_statistics[group]["max_distance"] for group in groups]
),
"min_distance": min(
[new_statistics[group]["min_distance"] for group in groups]
),
}
new_statistics["overall"]["average_group_distance"] = (
new_statistics["overall"]["total_distance"]
/ new_statistics["overall"]["nGroups"]
)
new_statistics_str_keys = {str(k): v for k, v in new_statistics.items()}
with open(f"data/new_stats_{metric}.json", "w", encoding="utf-8") as f:
json.dump(new_statistics_str_keys, f, ensure_ascii=False, indent=4, default=str)

102
dfbnet/dfbnet.py Normal file
View File

@ -0,0 +1,102 @@
# %%
import json
from datetime import datetime, time, date
from serializers import ProblemSerializer
from competitions import get_teams_from_staffel
from schluesselzahlen import get_schluesselzahlen
from rahmentermine import get_rahmentermine
PROJECT_PATH = '/home/md/Work/ligalytics/leagues_stable/'
import os, sys
sys.path.insert(0, PROJECT_PATH)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "leagues.settings")
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
""" read json """
with open("data/competitions.json", "r", encoding="utf-8") as f:
competitions = json.load(f)
prob = ProblemSerializer(data=competitions)
prob.is_valid()
# %%
""" Create pulp model for solving a schedule for a given set of teams and rahmentermine """
rounds1 = list(range(1, len(rahmentermine)//2+1))
rounds2 = list(range(len(rahmentermine)//2+1, len(rahmentermine)+1))
rounds = rounds1 + rounds2
# %%
from pulp import LpVariable, LpProblem, LpMinimize, lpSum, LpStatus, value, LpInteger, XPRESS
model = LpProblem("Spielplan", LpMinimize)
# Create a variable for each team and each rahmentermin
x = {}
for team, attr in teams.items():
for round in rounds:
x[(team, round)] = LpVariable(
f"team_{attr['MANNSCHAFT']}_{round}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
# Create home variables """
home = {}
for team, attr in teams.items():
for round in rounds:
home[(team, round)] = LpVariable(
f"home_{attr['MANNSCHAFT']}_{round}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
# Create pattern variables
assignPattern = {}
for team, attr in teams.items():
for p in pattern:
assignPattern[(team, p)] = LpVariable(
f"pattern_{attr['MANNSCHAFT']}_{p}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
""" Each team exactly one pattern """
for team in teams:
model += (lpSum(assignPattern[(team, p)] for p in pattern) == 1, f"team_{team}_one_pattern")
""" Patterns cannot be used more than once """
for p in pattern:
model += (lpSum(assignPattern[(team, p)] for team in teams) <= 1, f"pattern_{p}_used_once")
""" Couple patterns with home variables """
for round in rounds1:
for team, attr in teams.items():
model += (lpSum(assignPattern[(team, p)] for p in pattern if pattern[p][round-1] == "H") == home[(team, round)], f"coupling_pattern_home_{attr['MANNSCHAFT']}_{round}")
# %%
model.solve(XPRESS(msg=1))
""" print patterns """
for team, attr in teams.items():
for p in pattern:
if value(assignPattern[(team, p)]) == 1:
print(f"{attr['MANNSCHAFT']} is assigned pattern {p}")

70
dfbnet/dfbnet_tojson.py Normal file
View File

@ -0,0 +1,70 @@
# %%
from datetime import datetime, time, date
from competitions import get_teams_from_staffel
from schluesselzahlen import get_schluesselzahlen
from rahmentermine import get_rahmentermine
from spielstaetten import get_venues
# staffel = "Brandible Stadtliga B"
# teams = get_teams_from_staffel(staffel)
def datetime_serializer(obj):
if isinstance(obj, datetime) or isinstance(obj, date) or isinstance(obj, time):
return obj.isoformat() # or use obj.strftime("%Y-%m-%d %H:%M:%S")
raise TypeError("Type not serializable")
staffeln_raw, teams = get_teams_from_staffel()
staffeln = []
divisions = []
court_names = []
venues = []
for s in staffeln_raw:
if len(teams[s]) < 4 or len(teams[s]) % 2 == 1:
# if len(teams[s]) > 0:
# print(s, teams[s][0]['MS_ART'], len(teams[s]))
# else:
# print(s, "no teams")
print("Incompatible", s)
continue
nTeams = len(teams[s])
pattern, opponent = get_schluesselzahlen(nTeams)
ms_art = teams[s][0]['MS_ART']
if ms_art in ["A-Junioren","B-Junioren","C-Junioren","D-Junioren","D-Juniorinnen"]:
ms_art = "Junioren A-D"
elif ms_art in ["Herren Ü50"]:
ms_art = "Senioren Ü50"
elif ms_art in ["Herren Ü35"]:
ms_art = "Senioren Ü35"
rahmentermine = get_rahmentermine(ms_art, nTeams)
if not rahmentermine:
print("No rahmentermine for", s, ms_art, nTeams)
continue
divisions.append({
"name": s,
"teams": teams[s],
"nTeams": nTeams,
"ms_art": ms_art,
"pattern": pattern,
"opponent": opponent,
"rahmentermine": rahmentermine
})
# for t in teams[s]:
# if not t['SPIELSTAETTE'] in court_names:
# courts += [{"name":t['SPIELSTAETTE']}]
# court_names += [t['SPIELSTAETTE']]
venues = get_venues()
""" dump json """
import json
with open("data/competitions.json", "w", encoding="utf-8") as f:
json.dump({'divisions':divisions,'venues':venues}, f, default=datetime_serializer, ensure_ascii=False, indent=4)
# %%

View File

@ -0,0 +1,116 @@
# %%
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
import time
from math import sqrt, sin, cos, atan2, pi
gmaps = googlemaps.Client(key='AIzaSyB76EhR4OqjdXHQUiTkHZC0Svx_7cPGqyU')
def degreesToRadians(degrees):
""" Convert degrees to radians """
return degrees * pi / 180
def distanceInKmByGPS(lat1, lon1, lat2, lon2):
""" Calculate the distance between two points in km """
earthRadiusKm = 6371
dLat = degreesToRadians(lat2 - lat1)
dLon = degreesToRadians(lon2 - lon1)
lat1 = degreesToRadians(lat1)
lat2 = degreesToRadians(lat2)
a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * \
sin(dLon / 2) * cos(lat1) * cos(lat2)
c = 2 * atan2(sqrt(a), sqrt(1 - a))
return int(earthRadiusKm * c)
# %%
with open('data/sachsen.json', 'r', encoding='utf-8') as f:
competitions = json.load(f)
competitions = {ast.literal_eval(k): v for k, v in competitions.items()}
competition_details = {}
color = None
teams_in_competition = {}
for staffel ,attr in competitions.items():
if (staffel[0],staffel[1]) not in competition_details:
competition_details[(staffel[0],staffel[1])] = {
'nStaffeln': 1,
'nTeams': 0,
'distances': {},
'teams': []
}
else:
competition_details[(staffel[0],staffel[1])]['nStaffeln'] += 1
for team in attr['teams']:
if team['MANNSCHAFT'] not in competition_details[(staffel[0],staffel[1])]['teams']:
competition_details[(staffel[0],staffel[1])]['teams'].append(team)
csv_file = 'data/distances.csv'
f = open(csv_file, 'w')
f.write('art,klasse,team1,team2,road_distance,road_duration,flight_distance\n')
for competition, details in competition_details.items():
print(f"Calculating distances for {competition}")
for team1 in details['teams']:
details['distances'][team1['MANNSCHAFT']] = {}
for team2 in details['teams']:
if team1 != team2:
start = team1
end = team2
c = 0
while(c <= 1):
try:
road_distance = gmaps.distance_matrix((start['LATITUDE'],start['LONGITUDE']),(end['LATITUDE'],end['LONGITUDE']))['rows'][0]['elements'][0]['distance']['value']
road_duration = gmaps.distance_matrix((start['LATITUDE'],start['LONGITUDE']),(end['LATITUDE'],end['LONGITUDE']))['rows'][0]['elements'][0]['duration']['value']
c = 3
except:
road_distance = 0
road_duration = 0
print(f"Error with {team1['MANNSCHAFT']} and {team2['MANNSCHAFT']}")
time.sleep(3)
c += 1
road_distance = round(road_distance*0.001)
road_duration = round(road_duration*0.016666)
flight_distance = distanceInKmByGPS(team1['LATITUDE'], team1['LONGITUDE'], team2['LATITUDE'], team2['LONGITUDE'])
# details['distances'][team1['MANNSCHAFT']][team2['MANNSCHAFT']] = {
# 'road_distance': road_distance,
# 'road_duration': road_duration,
# 'flight_distance': flight_distance
# }
# print(f"Distance between {team1['MANNSCHAFT']} and {team2['MANNSCHAFT']} calculated", details['distances'][team1['MANNSCHAFT']][team2['MANNSCHAFT']])
f.write(f"{competition[0]},{competition[1]},{team1['MANNSCHAFT']},{team2['MANNSCHAFT']},{road_distance},{road_duration},{flight_distance}\n")
f.close()
# matrix_str_keys = {str(k): v for k, v in competition_details.items()}
# with open('data/sachsen_matrix.json', 'w', encoding='utf-8') as f:
# json.dump(matrix_str_keys, f, ensure_ascii=False, indent=4, default=str)
# %%
# %%

View File

@ -0,0 +1,116 @@
# %%
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
import time
from math import sqrt, sin, cos, atan2, pi
gmaps = googlemaps.Client(key='AIzaSyB76EhR4OqjdXHQUiTkHZC0Svx_7cPGqyU')
def degreesToRadians(degrees):
""" Convert degrees to radians """
return degrees * pi / 180
def distanceInKmByGPS(lat1, lon1, lat2, lon2):
""" Calculate the distance between two points in km """
earthRadiusKm = 6371
dLat = degreesToRadians(lat2 - lat1)
dLon = degreesToRadians(lon2 - lon1)
lat1 = degreesToRadians(lat1)
lat2 = degreesToRadians(lat2)
a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * \
sin(dLon / 2) * cos(lat1) * cos(lat2)
c = 2 * atan2(sqrt(a), sqrt(1 - a))
return int(earthRadiusKm * c)
# %%
with open('data/sachsen.json', 'r', encoding='utf-8') as f:
competitions = json.load(f)
competitions = {ast.literal_eval(k): v for k, v in competitions.items()}
competition_details = {}
color = None
teams_in_competition = {}
for staffel ,attr in competitions.items():
if (staffel[0],staffel[1]) not in competition_details:
competition_details[(staffel[0],staffel[1])] = {
'nStaffeln': 1,
'nTeams': 0,
'distances': {},
'teams': []
}
else:
competition_details[(staffel[0],staffel[1])]['nStaffeln'] += 1
for team in attr['teams']:
if team['MANNSCHAFT'] not in competition_details[(staffel[0],staffel[1])]['teams']:
competition_details[(staffel[0],staffel[1])]['teams'].append(team)
csv_file = 'data/distances_2.csv'
f = open(csv_file, 'w')
f.write('art,klasse,team1,team2,road_distance,road_duration,flight_distance\n')
for competition, details in sorted(competition_details.items(), key=lambda x:x[0], reverse=True):
print(f"Calculating distances for {competition}")
for team1 in details['teams']:
details['distances'][team1['MANNSCHAFT']] = {}
for team2 in details['teams']:
if team1 != team2:
start = team1
end = team2
c = 0
while(c <= 1):
try:
road_distance = gmaps.distance_matrix((start['LATITUDE'],start['LONGITUDE']),(end['LATITUDE'],end['LONGITUDE']))['rows'][0]['elements'][0]['distance']['value']
road_duration = gmaps.distance_matrix((start['LATITUDE'],start['LONGITUDE']),(end['LATITUDE'],end['LONGITUDE']))['rows'][0]['elements'][0]['duration']['value']
c = 3
except:
road_distance = 0
road_duration = 0
print(f"Error with {team1['MANNSCHAFT']} and {team2['MANNSCHAFT']}")
time.sleep(3)
c += 1
road_distance = round(road_distance*0.001)
road_duration = round(road_duration*0.016666)
flight_distance = distanceInKmByGPS(team1['LATITUDE'], team1['LONGITUDE'], team2['LATITUDE'], team2['LONGITUDE'])
# details['distances'][team1['MANNSCHAFT']][team2['MANNSCHAFT']] = {
# 'road_distance': road_distance,
# 'road_duration': road_duration,
# 'flight_distance': flight_distance
# }
# print(f"Distance between {team1['MANNSCHAFT']} and {team2['MANNSCHAFT']} calculated", details['distances'][team1['MANNSCHAFT']][team2['MANNSCHAFT']])
f.write(f"{competition[0]},{competition[1]},{team1['MANNSCHAFT']},{team2['MANNSCHAFT']},{road_distance},{road_duration},{flight_distance}\n")
f.close()
# matrix_str_keys = {str(k): v for k, v in competition_details.items()}
# with open('data/sachsen_matrix.json', 'w', encoding='utf-8') as f:
# json.dump(matrix_str_keys, f, ensure_ascii=False, indent=4, default=str)
# %%
# %%

665
dfbnet/kmeans_sachsen.py Normal file
View File

@ -0,0 +1,665 @@
# %%
from pulp import (
LpVariable,
LpProblem,
LpMinimize,
lpSum,
LpStatus,
value,
LpInteger,
LpContinuous,
XPRESS,
)
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
import time
import os
from scipy.cluster.vq import kmeans, vq
import numpy as np
import matplotlib.pyplot as plt
os.environ["XPRESSDIR"] = "/opt/xpressmp_9.5.0"
os.environ["XPRESS"] = "/opt/xpressmp_9.5.0/bin"
os.environ["LD_LIBRARY_PATH"] = os.environ["XPRESSDIR"] + "/lib"
os.environ["DYLD_LIBRARY_PATH"] = os.environ["XPRESSDIR"] + "/lib"
os.environ["SHLIB_PATH"] = os.environ["XPRESSDIR"] + "/lib"
os.environ["LIBPATH"] = os.environ["XPRESSDIR"] + "/lib"
os.environ["PYTHONPATH"] = os.environ["XPRESSDIR"] + "/lib"
os.environ["CLASSPATH"] = os.environ["XPRESSDIR"] + "/lib/xprs.jar"
os.environ["CLASSPATH"] = os.environ["XPRESSDIR"] + "/lib/xprb.jar" + os.pathsep + os.environ["CLASSPATH"]
os.environ["CLASSPATH"] = os.environ["XPRESSDIR"] + "/lib/xprm.jar" + os.pathsep + os.environ["CLASSPATH"]
os.environ["PATH"] = os.environ["XPRESSDIR"] + "/bin" + os.pathsep + os.environ["PATH"]
# %%
from math import sqrt, sin, cos, atan2, pi
def degreesToRadians(degrees):
"""Convert degrees to radians"""
return degrees * pi / 180
def distanceInKmByGPS(lat1, lon1, lat2, lon2):
"""Calculate the distance between two points in km"""
earthRadiusKm = 6371
dLat = degreesToRadians(lat2 - lat1)
dLon = degreesToRadians(lon2 - lon1)
lat1 = degreesToRadians(lat1)
lat2 = degreesToRadians(lat2)
a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(lat1) * cos(
lat2
)
c = 2 * atan2(sqrt(a), sqrt(1 - a))
return int(earthRadiusKm * c)
def random_color():
return "#{:06x}".format(random.randint(0, 0xFFFFFF))
# %%
with open("data/sachsen.json", "r", encoding="utf-8") as f:
competitions = json.load(f)
competitions = {ast.literal_eval(k): v for k, v in competitions.items()}
# %%
# region
# STAFFELN PRO ART UND KLASSE
# ('Herren', 'Landesliga') 1
# ('Herren', 'Landesklasse') 3
# ('Frauen', 'Landesliga') 1
# ('Frauen', 'Landesklasse') 3
# ('A-Junioren', 'Landesliga') 1
# ('A-Junioren', 'Landesklasse') 4
# ('Herren', 'Kreisoberliga') 13
# ('Herren', '1.Kreisliga (A)') 19
# ('Herren', '2.Kreisliga (B)') 8
# ('Herren', '3.Kreisliga (C)') 1
# ('Herren', '1.Kreisklasse') 21
# ('Herren', '2.Kreisklasse') 9
# ('A-Junioren', 'Kreisoberliga') 10
# ('A-Junioren', '1.Kreisliga (A)') 6
# ('Frauen', 'Kreisoberliga') 4
# ('Frauen', '1.Kreisliga (A)') 1
# ('Frauen', '1.Kreisklasse') 3
# ('B-Junioren', 'Landesliga') 1
# ('B-Junioren', 'Landesklasse') 4
# ('B-Junioren', 'Kreisoberliga') 13
# ('B-Junioren', '1.Kreisliga (A)') 13
# ('B-Junioren', '1.Kreisklasse') 1
# ('C-Junioren', 'Landesliga') 1
# ('C-Junioren', 'Landesklasse') 4
# ('C-Junioren', 'Kreisoberliga') 16
# ('C-Junioren', '1.Kreisliga (A)') 15
# ('C-Junioren', '1.Kreisklasse') 9
# ('D-Junioren', 'Landesliga') 1
# ('D-Junioren', 'Landesklasse') 6
# ('D-Junioren', 'Kreisoberliga') 16
# ('D-Junioren', '1.Kreisliga (A)') 24
# ('D-Junioren', '2.Kreisliga (B)') 8
# ('D-Junioren', '3.Kreisliga (C)') 2
# ('D-Junioren', '1.Kreisklasse') 33
# ('D-Junioren', '2.Kreisklasse') 10
# ('B-Juniorinnen', 'Landesliga') 1
# ('B-Juniorinnen', 'Landesklasse') 2
# ('C-Juniorinnen', 'Landesklasse') 3
# ('D-Juniorinnen', 'Kreisoberliga') 1
# ('Herren Ü35', 'Kreisoberliga') 4
# ('Herren Ü35', '1.Kreisliga (A)') 3
# ('Herren Ü35', '1.Kreisklasse') 3
# ('Herren Ü35', '2.Kreisklasse') 1
# ('Herren Ü40', '1.Kreisliga (A)') 5
# ('Herren Ü40', '1.Kreisklasse') 1
# ('Herren Ü50', '1.Kreisliga (A)') 1
# ('Herren Ü50', '1.Kreisklasse') 1
# ('Freizeitsport', '1.Kreisliga (A)') 3
# ('Freizeitsport', '1.Kreisklasse') 2
# endregion
competition_details = {}
color = None
for staffel, attr in competitions.items():
# if (staffel[0], staffel[1]) != ('Herren', 'Kreisoberliga'):
# continue
competitions[staffel]["distance"] = []
if (staffel[0], staffel[1]) not in competition_details:
competition_details[(staffel[0], staffel[1])] = {
"nStaffeln": 1,
"nTeams": len(attr["teams"]),
"teams": attr["teams"],
"group_sizes": [len(attr["teams"])],
"clusters": {},
}
else:
competition_details[(staffel[0], staffel[1])]["nStaffeln"] += 1
competition_details[(staffel[0], staffel[1])]["group_sizes"].append(len(attr["teams"]))
competition_details[(staffel[0], staffel[1])]["nTeams"] += len(attr["teams"])
competition_details[(staffel[0], staffel[1])]["teams"] += attr["teams"]
"""" GENERATE ALL DISTANCES BETWEEN TEAMS """
distance_between_teams = {}
for competition, details in competition_details.items():
# competition = ('Herren', 'Kreisoberliga')
# details = competition_details[competition]
print(f"Calculating distances for {competition}")
for id, team1 in enumerate(details["teams"]):
team1['ID'] = id
distance_between_teams[team1["MANNSCHAFT"]] = {}
for team2 in details["teams"]:
distance = 0
if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
distance = distanceInKmByGPS(
team1["LATITUDE"],
team1["LONGITUDE"],
team2["LATITUDE"],
team2["LONGITUDE"],
)
distance_between_teams[team1["MANNSCHAFT"]][
team2["MANNSCHAFT"]
] = distance
teams = details["teams"]
# print("Number of teams", len(teams))
locations = []
for team in teams:
locations.append([team["LATITUDE"], team["LONGITUDE"]])
data = np.array(locations)
k = details['nStaffeln']
# print("Number of groups", k)
centroids, _ = kmeans(data, k)
cluster_labels, _ = vq(data, centroids)
# print("Initial centroids", len(centroids), centroids)
for diff in range(len(centroids), k):
centroids = np.append(centroids, [[0, 0]], axis=0)
"""" RECLUSTERING THE COMPETITION INTO DIVISIONS """
improvement = True
it = 0
last_objective = False
while(improvement):
it += 1
print("Iteration", it)
model = LpProblem(f"KMeans_{it}", LpMinimize)
""" x = 1 if team i is in same division as j, 0 otherwise """
x = {}
""" g = 1 if team i is i group j, 0 otherwise """
groups = range(1, k+1)
g = {}
for team in teams:
for group in groups:
g[(team["MANNSCHAFT"], group)] = LpVariable(
f"team_{team['ID']}_{group}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
""" Each team is in exactly one division """
for team in teams:
model += lpSum(g[(team["MANNSCHAFT"], group)] for group in groups) == 1
for group, group_size in enumerate(details["group_sizes"]):
# print(group+1, group_size)
model += lpSum(g[(team["MANNSCHAFT"], group+1)] for team in teams) == group_size
""" MINIMIZE THE DISTANCE TO THE CLUSTER CENTROID """
model += lpSum(g[team["MANNSCHAFT"], group] * distanceInKmByGPS(
team["LATITUDE"],
team["LONGITUDE"],
centroids[group - 1][0],
centroids[group - 1][1],
) for team in teams for group in groups
)
""" write the model to a file """
# model.writeLP(f"kmeans/kmeans_{competition}_{it}.lp")
model.solve(XPRESS(msg=0, gapRel=0.01))
if last_objective:
if last_objective <= value(model.objective):
improvement = False
last_objective = value(model.objective)
""" recompute the centroids """
centroids = []
for group in groups:
latitudes = []
longitudes = []
for team in teams:
if value(g[(team["MANNSCHAFT"], group)]) > 0.9:
latitudes.append(team["LATITUDE"])
longitudes.append(team["LONGITUDE"])
centroids.append([np.mean(latitudes), np.mean(longitudes)])
clusters = {k: [] for k in range(1, len(groups)+1)}
augmented_teams = []
for group in groups:
for team in teams:
if value(g[(team["MANNSCHAFT"], group)]) > 0.9:
clusters[group].append(team)
competition_details[competition]["clusters"] = clusters
some_colors = [
"red",
"blue",
"green",
"yellow",
"purple",
"orange",
"pink",
"brown",
"black",
"white",
"gray",
"cyan",
"magenta",
"lime",
"indigo",
"violet",
"turquoise",
"gold",
"silver",
"beige",
"maroon",
"olive",
"navy",
"teal",
"coral",
"lavender",
"salmon",
"chocolate",
"crimson",
"aqua",
"ivory",
"khaki",
"plum",
"orchid",
"peru",
"tan",
"tomato",
"wheat",
"azure",
"mint",
"apricot",
"chartreuse",
"amber",
"fuchsia",
"jade",
"ruby",
"amethyst",
"rose",
"sapphire",
"cerulean",
"moss",
"denim",
"copper",
"peach",
"sand",
"pearl",
"mulberry",
"lemon",
"cream",
"ocher",
"brass",
"eggplant",
"cinnamon",
"mustard",
"rust",
"sienna",
"sepia",
"umber",
"limegreen",
"seagreen",
"forestgreen",
"dodgerblue",
"mediumslateblue",
"royalblue",
"firebrick",
"darkolivegreen",
"midnightblue",
"darkturquoise",
"lightcoral",
"palevioletred",
"hotpink",
"deeppink",
"darkkhaki",
"lightseagreen",
"darkslategray",
"slategray",
"lightsteelblue",
"skyblue",
"lightblue",
"powderblue",
"darkorange",
"lightsalmon",
"indianred",
"thistle",
"burlywood",
"mediumaquamarine",
"mediumorchid",
"mediumvioletred",
"papayawhip",
"moccasin",
"bisque",
"blanchedalmond",
"antiquewhite",
"mistyrose",
"lavenderblush",
"linen",
"snow",
"honeydew",
"palegreen",
"lightcyan",
"aliceblue",
"ghostwhite",
"whitesmoke",
"gainsboro",
]
latitude = 51.18292980165227
longitude = 13.11435805600463
gmap = GoogleMapPlotter(
latitude, longitude, 8, apikey="AIzaSyAPzFyMk3ZA0kL9TUlJ_kpV_IY56uBwdrc"
)
aggregated_distance = 0
distance_for_team = {}
for cluster, teamslist in clusters.items():
latitudes = []
longitudes = []
markers_text = []
color = some_colors.pop(0)
cluster_distance = 0
for team1 in teamslist:
distance_for_team[team1["MANNSCHAFT"]] = []
for team2 in teamslist:
distance = 0
if team1["MANNSCHAFT"] != team2["MANNSCHAFT"]:
distance = distance_between_teams[team1["MANNSCHAFT"]][team2["MANNSCHAFT"]]
cluster_distance += distance
aggregated_distance += distance
distance_for_team[team1["MANNSCHAFT"]].append(distance)
latitudes.append(team1["LATITUDE"])
longitudes.append(team1["LONGITUDE"])
markers_text.append(f"{team1['MANNSCHAFT']} @{team1['SPIELSTAETTE']}")
# Plot the points on the map
gmap.scatter(latitudes, longitudes, color=color, size=40, marker=False)
for (lat1, lon1), (lat2, lon2) in itertools.combinations(
zip(latitudes, longitudes), 2
):
gmap.plot([lat1, lat2], [lon1, lon2], color=color, edge_width=2)
for lat, lon, text in zip(latitudes, longitudes, markers_text):
gmap.marker(lat, lon, title=text.replace('"', ""), color=color)
print(cluster, len(teamslist), cluster_distance, aggregated_distance, color)
gmap.draw(f"kmeans/map_mip_{competition}.html")
# %%
""" DUMP THE COMPETITIONS """
from datetime import datetime, time, date
# from competitions import get_teams_from_staffel
from schluesselzahlen import get_schluesselzahlen
from rahmentermine import get_rahmentermine
from spielstaetten import get_venues
# staffel = "Brandible Stadtliga B"
# teams = get_teams_from_staffel(staffel)
def datetime_serializer(obj):
if isinstance(obj, datetime) or isinstance(obj, date) or isinstance(obj, time):
return obj.isoformat() # or use obj.strftime("%Y-%m-%d %H:%M:%S")
raise TypeError("Type not serializable")
staffeln = []
divisions = []
courts = []
court_names = []
venues = []
for competition, details in competition_details.items():
for cluster, cluster_teams in details['clusters'].items():
print(f"Processing {competition} {cluster}")
nTeams = len(cluster_teams) + len(cluster_teams) % 2
pattern, opponent = get_schluesselzahlen(nTeams)
teams = cluster_teams
ms_art = teams[0]['MS_ART']
if ms_art in ["A-Junioren","B-Junioren","C-Junioren","D-Junioren","D-Juniorinnen"]:
ms_art = "Junioren A-D"
elif ms_art in ["Herren Ü50"]:
ms_art = "Senioren Ü50"
elif ms_art in ["Herren Ü35"]:
ms_art = "Senioren Ü35"
rahmentermine = get_rahmentermine(ms_art, nTeams)
if not rahmentermine:
print("No rahmentermine for", competition, cluster, ms_art, nTeams)
continue
divisions.append({
"name": f"{competition[0]} {competition[1]} {cluster}",
"teams": teams,
"nTeams": nTeams,
"ms_art": ms_art,
"pattern": pattern,
"opponent": opponent,
"rahmentermine": rahmentermine
})
for t in teams:
if not t['SPIELSTAETTE'] in court_names:
# courts += [{"name":t['SPIELSTAETTE']}]
venues.append({
"SB_SPST_ID": len(venues)+1,
"SB_SPST_GEBIET_REF": len(venues)+1,
"SB_SPST_NAME": t['SPIELSTAETTE'],
"SB_SPST_TYP_REF": 1,
"SB_SPST_ZUSTAND_REF": 1,
"SB_SPST_PLATZ_NR": "(null)",
"SB_SPST_FLUTLICHT": "t",
"SB_SPST_SPIELE_PARALLEL_MAX": 3,
"SB_SPST_ANSTOSSZEIT_VON": "08:30:00",
"SB_SPST_ANSTOSSZEIT_BIS": "20:30:00",
"SB_SPST_ANZ_UMKLEIDEN": 5,
"SB_SPST_MITTAGSPAUSE_VON": "(null)",
"SB_SPST_MITTAGSPAUSE_BIS": "(null)",
"SB_SPST_GROESSE_REF": 1,
"SB_SPST_SPIELE_TAG_MAX": 20,
"SB_SPST_ANZ_TORE": 6,
"SB_SPST_SPIELE_ABSTAND": 0,
"latitude": t["LATITUDE"],
"longitude": t["LONGITUDE"],
})
court_names += [t['SPIELSTAETTE']]
""" dump json """
import json
with open("kmeans/competitions.json", "w", encoding="utf-8") as f:
json.dump({'divisions':divisions,'venues':venues}, f, default=datetime_serializer, ensure_ascii=False, indent=4)
# # %%
# %%
with open("kmeans/competitions.json", "r", encoding="utf-8") as f:
data = json.load(f)
from pulp import LpVariable, LpProblem, LpMinimize, lpSum, LpStatus, value, LpInteger, XPRESS
model = LpProblem("Spielplan", LpMinimize)
x = {}
home = {}
assignPattern = {}
divisions = data['divisions']
venues = data['venues']
max_rounds = 0
team_id = 0
for division_id, division in enumerate(divisions):
division['id'] = division_id
rahmentermine = division['rahmentermine']
teams = division['teams']
pattern = division['pattern']
opponent = division['opponent']
nTeams = division['nTeams']
ms_art = division['ms_art']
for t in teams:
t['id'] = team_id
team_id += 1
# %%
""" Create pulp model for solving a schedule for a given set of teams and rahmentermine """
rounds1 = list(range(1, len(rahmentermine)//2+1))
rounds2 = list(range(len(rahmentermine)//2+1, len(rahmentermine)+1))
rounds = rounds1 + rounds2
max_rounds = max(max_rounds, len(rounds1))
# %%
# Create a variable for each team and each rahmentermin
for team in teams:
for round in rounds:
x[(team['id'], round)] = LpVariable(
f"team_{team['id']}_{round}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
# Create home variables """
for team in teams:
for round in rounds:
home[(team['id'], round)] = LpVariable(
f"home_{team['id']}_{round}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
# Create pattern variables
for team in teams:
for p in pattern:
assignPattern[(team['id'], p)] = LpVariable(
f"pattern_{team['id']}_{p}",
lowBound=0,
upBound=1,
cat=LpInteger,
)
""" Each team exactly one pattern """
for team in teams:
model += (lpSum(assignPattern[(team['id'], p)] for p in pattern) == 1, f"team_{team['id']}_one_pattern")
# if team['SPIELSTAETTE'].strip() not in [venue['SB_SPST_NAME'] for venue in venues]:
# print(f"Venue {team['SPIELSTAETTE']} not found in venues")
# exit()
# else:
# print(f"Venue {team['SPIELSTAETTE']} found in venues")
""" Patterns cannot be used more than once """
for p in pattern:
model += (lpSum(assignPattern[(team['id'], p)] for team in teams) <= 1, f"pattern_{p}_used_once_in_division_{division['id']}")
""" Couple patterns with home variables """
for round in rounds1:
for team in teams:
model += (lpSum(assignPattern[(team['id'], p)] for p in pattern if pattern[p][round-1] == "H") == home[(team['id'], round)], f"coupling_pattern_home_{team['id']}_{round}")
model.solve(XPRESS(msg=1))
csv_file = open("kmeans/schedule.csv", "w")
csv_file.write("round,venue,day,division,hometeam,awayteam,homepattern,awaypattern,wunschtag,wunschzeit\n")
""" print patterns """
for round in range(1,max_rounds+1):
for venue in venues:
print(f"Round {round} at {venue['SB_SPST_NAME']}")
for division in divisions:
if division['nTeams'] <= round:
continue
for team in division['teams']:
if team['SPIELSTAETTE'] == venue['SB_SPST_NAME']:
if value(home.get((team['id'], round),0)) == 1:
p1 = [p for p in division['pattern'] if assignPattern[(team['id'], p)].varValue == 1][0]
p2 = 0
o = None
for t2 in division['teams']:
p2 = [p for p in division['pattern'] if assignPattern[(t2['id'], p)].varValue == 1][0]
# print(round,division['nTeams'])
if int(p2) == int(division['opponent'][p1][round-1]):
o = t2['MANNSCHAFT']
break
# print(f"{round} ({team['SPIELSTAETTE']}, {venue['SB_SPST_SPIELE_TAG_MAX']}): {division['name']} - {team['MANNSCHAFT']} - {p1} - {team['WUNSCH_TAG']} - {team['WUNSCH_ZEIT']} vs {o} - {p2}")
if o:
csv_file.write(f"{round},{team['SPIELSTAETTE'].replace(","," ")},{venue['SB_SPST_SPIELE_TAG_MAX']},{division['name']},{team['MANNSCHAFT']},{o},{p1},{p2},{team['WUNSCH_TAG']},{team['WUNSCH_ZEIT']}\n")
csv_file.close()

48
dfbnet/rahmentermine.py Normal file
View File

@ -0,0 +1,48 @@
import pandas as pd
def get_rahmentermine(MS_ART, nTeams):
""" LOAD RAHMENTERMINPLAN FROM CSV"""
rahmentermine = pd.read_csv("data/rahmentermine.csv")
cols = list(rahmentermine.columns)
# %%
rahmentermine['From'] = pd.to_datetime(rahmentermine['From'], format='%d/%m').dt.date
rahmentermine['From'] = rahmentermine['From'].apply(lambda x: x.replace(year=2025) if x.month < 7 else x.replace(year=2024))
rahmentermine['To'] = pd.to_datetime(rahmentermine['To'], format='%d/%m').dt.date
rahmentermine['To'] = rahmentermine['To'].apply(lambda x: x.replace(year=2025) if x.month < 7 else x.replace(year=2024))
# %%
""" REMOVE ALL ENTRIES THAT ARE NO REAL MATCHDAYS"""
for col in cols:
if col in ['From', 'To']:
continue
rahmentermine[col] = pd.to_numeric(rahmentermine[col], errors='coerce')
rahmentermine[col] = rahmentermine[col].apply(lambda x: int(x) if pd.notna(x) and x == int(x) else '')
# %%
""" CREATE DICTIONARY WITH MATCHDAYS"""
termine = {}
for col in cols:
colname = col.split('.')[0]
if col in ['From', 'To']:
continue
if rahmentermine[col][0] != nTeams:
continue
termine[(colname,nTeams)] = {}
for row in rahmentermine.iterrows():
if row[0] == 0:
continue
if row[1][col] != "":
termine[(colname,nTeams)][row[1][col]] = [row[1]['From'], row[1]['To']]
return termine.get((MS_ART,nTeams), [])

136
dfbnet/schluesselzahlen.py Normal file
View File

@ -0,0 +1,136 @@
rahmenplan = {}
rahmenplan[4] = [
[(1,3),(1,2),(2,3)],
[(4,2),(3,4),(4,1)],
]
rahmenplan[6] = [
[(1,5), (2,6), (1,3), (1,2), (2,5)],
[(3,2), (4,1), (4,2), (3,6), (4,3)],
[(6,4), (5,3), (6,5), (5,4), (6,1)],
]
rahmenplan[8] = [
[(1,7), (2,3), (1,5), (2,6), (1,3), (1,2), (2,7)],
[(3,4), (4,8), (3,7), (4,1), (4,2), (3,8), (4,5)],
[(5,2), (6,1), (6,4), (5,3), (6,7), (5,6), (6,3)],
[(8,6), (7,5), (8,2), (7,8), (8,5), (7,4), (8,1)],
]
rahmenplan[10] = [
[(1,9), (2,5), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,9)],
[(3,6), (4,3), (3,2), (4,8), (3,7), (4,1), (4,2), (3,10), (4,7)],
[(5,4), (6,10), (5,9), (6,1), (6,4), (5,3), (6,9), (5,8), (6,5)],
[(7,2), (8,1), (8,6), (7,5), (8,2), (7,10), (8,7), (7,6), (8,3)],
[(10,8), (9,7), (10,4), (9,3), (10,9), (9,8), (10,5), (9,4), (10,1)],
]
rahmenplan[12] = [
[(1,11), (2,7), (1,9), (2,3), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,11)],
[(3,8), (4,5), (3,4), (4,12), (3,11), (4,8), (3,7), (4,1), (4,2), (3,12), (4,9)],
[(5,6), (6,3), (5,2), (6,10), (5,9), (6,1), (6,4), (5,3), (6,11), (5,10), (6,7)],
[(7,4), (8,12), (7,11), (8,1), (8,6), (7,5), (8,2), (7,12), (8,9), (7,8), (8,5)],
[(9,2), (10,1), (10,8), (9,7), (10,4), (9,3), (10,11), (9,10), (10,7), (9,6), (10,3)],
[(12,10), (11,9), (12,6), (11,5), (12,2), (11,12), (12,9), (11,8), (12,5), (11,4), (12,1)],
]
rahmenplan[14] = [
[(1,13), (2,9), (1,11), (2,5), (1,9), (2,14), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,13)],
[(3,10), (4,7), (3,6), (4,3), (3,2), (4,12), (3,11), (4,8), (3,7), (4,1), (4,2), (3,14), (4,11)],
[(5,8), (6,5), (5,4), (6,14), (5,13), (6,10), (5,9), (6,1), (6,4), (5,3), (6,13), (5,12), (6,9)],
[(7,6), (8,3), (7,2), (8,12), (7,11), (8,1), (8,6), (7,5), (8,2), (7,14), (8,11), (7,10), (8,7)],
[(9,4), (10,14), (9,13), (10,1), (10,8), (9,7), (10,4), (9,3), (10,13), (9,12), (10,9), (9,8), (10,5)],
[(11,2), (12,1), (12,10), (11,9), (12,6), (11,5), (12,2), (11,14), (12,11), (11,10), (12,7), (11,6), (12,3)],
[(14,12), (13,11), (14,8), (13,7), (14,4), (13,3), (14,13), (13,12), (14,9), (13,8), (14,5), (13,4), (14,1)],
]
rahmenplan[16] = [
[(1,15), (2,11), (1,13), (2,7), (1,11), (2,3), (1,9), (2,14), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,15)],
[(3,12), (4,9), (3,8), (4,5), (3,4), (4,16), (3,15), (4,12), (3,11), (4,8), (3,7), (4,1), (4,2), (3,16), (4,13)],
[(5,10), (6,7), (5,6), (6,3), (5,2), (6,14), (5,13), (6,10), (5,9), (6,1), (6,4), (5,3), (6,15), (5,14), (6,11)],
[(7,8), (8,5), (7,4), (8,16), (7,15), (8,12), (7,11), (8,1), (8,6), (7,5), (8,2), (7,16), (8,13), (7,12), (8,9)],
[(9,6), (10,3), (9,2), (10,14), (9,13), (10,1), (10,8), (9,7), (10,4), (9,3), (10,15), (9,14), (10,11), (9,10), (10,7)],
[(11,4), (12,16), (11,15), (12,1), (12,10), (11,9), (12,6), (11,5), (12,2), (11,16), (12,13), (11,12), (12,9), (11,8), (12,5)],
[(13,2), (14,1), (14,12), (13,11), (14,8), (13,7), (14,4), (13,3), (14,15), (13,14), (14,11), (13,10), (14,7), (13,6), (14,3)],
[(16,14), (15,13), (16,10), (15,9), (16,6), (15,5), (16,2), (15,16), (16,13), (15,12), (16,9), (15,8), (16,5), (15,4), (16,1)],
]
rahmenplan[18] = [
[(1,17), (2,13), (1,15), (2,9), (1,13), (2,5), (1,11), (2,18), (1,9), (2,14), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,17)],
[(3,14), (4,11), (3,10), (4,7), (3,6), (4,3), (3,2), (4,16), (3,15), (4,12), (3,11), (4,8), (3,7), (4,1), (4,2), (3,18), (4,15)],
[(5,12), (6,9), (5,8), (6,5), (5,4), (6,18), (5,17), (6,14), (5,13), (6,10), (5,9), (6,1), (6,4), (5,3), (6,17), (5,16), (6,13)],
[(7,10), (8,7), (7,6), (8,3), (7,2), (8,16), (7,15), (8,12), (7,11), (8,1), (8,6), (7,5), (8,2), (7,18), (8,15), (7,14), (8,11)],
[(9,8), (10,5), (9,4), (10,18), (9,17), (10,14), (9,13), (10,1), (10,8), (9,7), (10,4), (9,3), (10,17), (9,16), (10,13), (9,12), (10,9)],
[(11,6), (12,3), (11,2), (12,16), (11,15), (12,1), (12,10), (11,9), (12,6), (11,5), (12,2), (11,18), (12,15), (11,14), (12,11), (11,10), (12,7)],
[(13,4), (14,18), (13,17), (14,1), (14,12), (13,11), (14,8), (13,7), (14,4), (13,3), (14,17), (13,16), (14,13), (13,12), (14,9), (13,8), (14,5)],
[(15,2), (16,1), (16,14), (15,13), (16,10), (15,9), (16,6), (15,5), (16,2), (15,18), (16,15), (15,14), (16,11), (15,10), (16,7), (15,6), (16,3)],
[(18,16), (17,15), (18,12), (17,11), (18,8), (17,7), (18,4), (17,3), (18,17), (17,16), (18,13), (17,12), (18,9), (17,8), (18,5), (17,4), (18,1)],
]
rahmenplan[20] = [
[(1,19), (2,15), (1,17), (2,11), (1,15), (2,7), (1,13), (2,3), (1,11), (2,18), (1,9), (2,14), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,19)],
[(3,16), (4,13), (3,12), (4,9), (3,8), (4,5), (3,4), (4,20), (3,19), (4,16), (3,15), (4,12), (3,11), (4,8), (3,7), (4,1), (4,2), (3,20), (4,17)],
[(5,14), (6,11), (5,10), (6,7), (5,6), (6,3), (5,2), (6,18), (5,17), (6,14), (5,13), (6,10), (5,9), (6,1), (6,4), (5,3), (6,19), (5,18), (6,15)],
[(7,12), (8,9), (7,8), (8,5), (7,4), (8,20), (7,19), (8,16), (7,15), (8,12), (7,11), (8,1), (8,6), (7,5), (8,2), (7,20), (8,17), (7,16), (8,13)],
[(9,10), (10,7), (9,6), (10,3), (9,2), (10,18), (9,17), (10,14), (9,13), (10,1), (10,8), (9,7), (10,4), (9,3), (10,19), (9,18), (10,15), (9,14), (10,11)],
[(11,8), (12,5), (11,4), (12,20), (11,19), (12,16), (11,15), (12,1), (12,10), (11,9), (12,6), (11,5), (12,2), (11,20), (12,17), (11,16), (12,13), (11,12), (12,9)],
[(13,6), (14,3), (13,2), (14,18), (13,17), (14,1), (14,12), (13,11), (14,8), (13,7), (14,4), (13,3), (14,19), (13,18), (14,15), (13,14), (14,11), (13,10), (14,7)],
[(15,4), (16,20), (15,19), (16,1), (16,14), (15,13), (16,10), (15,9), (16,6), (15,5), (16,2), (15,20), (16,17), (15,16), (16,13), (15,12), (16,9), (15,8), (16,5)],
[(17,2), (18,1), (18,16), (17,15), (18,12), (17,11), (18,8), (17,7), (18,4), (17,3), (18,19), (17,18), (18,15), (17,14), (18,11), (17,10), (18,7), (17,6), (18,3)],
[(20,18), (19,17), (20,14), (19,13), (20,10), (19,9), (20,6), (19,5), (20,2), (19,20), (20,17), (19,16), (20,13), (19,12), (20,9), (19,8), (20,5), (19,4), (20,1)],
]
rahmenplan[22] = [
[(1,21), (2,17), (1,19), (2,13), (1,17), (2,9), (1,15), (2,5), (1,13), (2,22), (1,11), (2,18), (1,9), (2,14), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,21)],
[(3,18), (4,15), (3,14), (4,11), (3,10), (4,7), (3,6), (4,3), (3,2), (4,20), (3,19), (4,16), (3,15), (4,12), (3,11), (4,8), (3,7), (4,1), (4,2), (3,22), (4,19)],
[(5,16), (6,13), (5,12), (6,9), (5,8), (6,5), (5,4), (6,22), (5,21), (6,18), (5,17), (6,14), (5,13), (6,10), (5,9), (6,1), (6,4), (5,3), (6,21), (5,20), (6,17)],
[(7,14), (8,11), (7,10), (8,7), (7,6), (8,3), (7,2), (8,20), (7,19), (8,16), (7,15), (8,12), (7,11), (8,1), (8,6), (7,5), (8,2), (7,22), (8,19), (7,18), (8,15)],
[(9,12), (10,9), (9,8), (10,5), (9,4), (10,22), (9,21), (10,18), (9,17), (10,14), (9,13), (10,1), (10,8), (9,7), (10,4), (9,3), (10,21), (9,20), (10,17), (9,16), (10,13)],
[(11,10), (12,7), (11,6), (12,3), (11,2), (12,20), (11,19), (12,16), (11,15), (12,1), (12,10), (11,9), (12,6), (11,5), (12,2), (11,22), (12,19), (11,18), (12,15), (11,14), (12,11)],
[(13,8), (14,5), (13,4), (14,22), (13,21), (14,18), (13,17), (14,1), (14,12), (13,11), (14,8), (13,7), (14,4), (13,3), (14,21), (13,20), (14,17), (13,16), (14,13), (13,12), (14,9)],
[(15,6), (16,3), (15,2), (16,20), (15,19), (16,1), (16,14), (15,13), (16,10), (15,9), (16,6), (15,5), (16,2), (15,22), (16,19), (15,18), (16,15), (15,14), (16,11), (15,10), (16,7)],
[(17,4), (18,22), (17,21), (18,1), (18,16), (17,15), (18,12), (17,11), (18,8), (17,7), (18,4), (17,3), (18,21), (17,20), (18,17), (17,16), (18,13), (17,12), (18,9), (17,8), (18,5)],
[(19,2), (20,1), (20,18), (19,17), (20,14), (19,13), (20,10), (19,9), (20,6), (19,5), (20,2), (19,22), (20,19), (19,18), (20,15), (19,14), (20,11), (19,10), (20,7), (19,6), (20,3)],
[(22,20), (21,19), (22,16), (21,15), (22,12), (21,11), (22,8), (21,7), (22,4), (21,3), (22,21), (21,20), (22,17), (21,16), (22,13), (21,12), (22,9), (21,8), (22,5), (21,4), (22,1)],
]
rahmenplan[24] = [
[(1,23), (2,19), (1,21), (2,15), (1,19), (2,11), (1,17), (2,7), (1,15), (2,3), (1,13), (2,22), (1,11), (2,18), (1,9), (2,14), (1,7), (2,10), (1,5), (2,6), (1,3), (1,2), (2,23)],
[(3,20), (4,17), (3,16), (4,13), (3,12), (4,9), (3,8), (4,5), (3,4), (4,24), (3,23), (4,20), (3,19), (4,16), (3,15), (4,12), (3,11), (4,8), (3,7), (4,1), (4,2), (3,24), (4,21)],
[(5,18), (6,15), (5,14), (6,11), (5,10), (6,7), (5,6), (6,3), (5,2), (6,22), (5,21), (6,18), (5,17), (6,14), (5,13), (6,10), (5,9), (6,1), (6,4), (5,3), (6,23), (5,22), (6,19)],
[(7,16), (8,13), (7,12), (8,9), (7,8), (8,5), (7,4), (8,24), (7,23), (8,20), (7,19), (8,16), (7,15), (8,12), (7,11), (8,1), (8,6), (7,5), (8,2), (7,24), (8,21), (7,20), (8,17)],
[(9,14), (10,11), (9,10), (10,7), (9,6), (10,3), (9,2), (10,22), (9,21), (10,18), (9,17), (10,14), (9,13), (10,1), (10,8), (9,7), (10,4), (9,3), (10,23), (9,22), (10,19), (9,18), (10,15)],
[(11,12), (12,9), (11,8), (12,5), (11,4), (12,24), (11,23), (12,20), (11,19), (12,16), (11,15), (12,1), (12,10), (11,9), (12,6), (11,5), (12,2), (11,24), (12,21), (11,20), (12,17), (11,16), (12,13)],
[(13,10), (14,7), (13,6), (14,3), (13,2), (14,22), (13,21), (14,18), (13,17), (14,1), (14,12), (13,11), (14,8), (13,7), (14,4), (13,3), (14,23), (13,22), (14,19), (13,18), (14,15), (13,14), (14,11)],
[(15,8), (16,5), (15,4), (16,24), (15,23), (16,20), (15,19), (16,1), (16,14), (15,13), (16,10), (15,9), (16,6), (15,5), (16,2), (15,24), (16,21), (15,20), (16,17), (15,16), (16,13), (15,12), (16,9)],
[(17,6), (18,3), (17,2), (18,22), (17,21), (18,1), (18,16), (17,15), (18,12), (17,11), (18,8), (17,7), (18,4), (17,3), (18,23), (17,22), (18,19), (17,18), (18,15), (17,14), (18,11), (17,10), (18,7)],
[(19,4), (20,24), (19,23), (20,1), (20,18), (19,17), (20,14), (19,13), (20,10), (19,9), (20,6), (19,5), (20,2), (19,24), (20,21), (19,20), (20,17), (19,16), (20,13), (19,12), (20,9), (19,8), (20,5)],
[(21,2), (22,1), (22,20), (21,19), (22,16), (21,15), (22,12), (21,11), (22,8), (21,7), (22,4), (21,3), (22,23), (21,22), (22,19), (21,18), (22,15), (21,14), (22,11), (21,10), (22,7), (21,6), (22,3)],
[(24,22), (23,21), (24,18), (23,17), (24,14), (23,13), (24,10), (23,9), (24,6), (23,5), (24,2), (23,24), (24,21), (23,20), (24,17), (23,16), (24,13), (23,12), (24,9), (23,8), (24,5), (23,4), (24,1)],
]
def get_schluesselzahlen(nTeams):
schluessel = {md:[] for md in range(1,nTeams)}
for row in rahmenplan[nTeams]:
for md,(t1,t2) in enumerate(row):
schluessel[nTeams-(md+1)].append((t1,t2))
pattern = {
i: [] for i in range(1,nTeams+1)
}
opponent = {
i: [] for i in range(1,nTeams+1)
}
for md in schluessel:
for (t1,t2) in schluessel[md]:
pattern[t1].append("H")
pattern[t2].append("A")
opponent[t1].append(t2)
opponent[t2].append(t1)
return pattern, opponent

23
dfbnet/serializers.py Normal file
View File

@ -0,0 +1,23 @@
from rest_framework import serializers
""" DATA SERIALIZERS """
class TeamsSerializer(serializers.Serializer):
id = serializers.CharField()
name = serializers.CharField()
division = serializers.IntegerField()
class DivisionsSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
teams = TeamsSerializer(many=True)
nTeams = serializers.IntegerField()
ms_art = serializers.CharField()
patterns = serializers.DictField()
opponents = serializers.DictField()
class ProblemSerializer(serializers.Serializer):
divisions = DivisionsSerializer(many=True)

View File

@ -0,0 +1,143 @@
# %%
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
import time
# %%
with open("data/previous_stats_road_distance.json", "r", encoding="utf-8") as f:
stats_previous_road = json.load(f)
stats_previous_road = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_previous_road.items()}
with open("data/previous_stats_road_duration.json", "r", encoding="utf-8") as f:
stats_previous_duration = json.load(f)
stats_previous_duration = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_previous_duration.items()}
with open("data/new_stats_road_distance.json", "r", encoding="utf-8") as f:
stats_new_road = json.load(f)
stats_new_road = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_new_road.items()}
with open("data/new_stats_road_duration.json", "r", encoding="utf-8") as f:
stats_new_duration = json.load(f)
stats_new_duration = {ast.literal_eval(k) if k != "overall" else k: v for k, v in stats_new_duration.items()}
# %%
print(stats_previous_road['overall'])
print(stats_previous_duration['overall'])
print(stats_new_road['overall'])
print(stats_new_duration['overall'])
overall_stats = {
"previous": {
"road": stats_previous_road['overall'],
"duration": stats_previous_duration['overall']
},
"new": {
"road": stats_new_road['overall'],
"duration": stats_new_duration['overall']
}
}
# %%
import plotly.graph_objects as go
# %%
""" create bar plot for overall stats """
fig = go.Figure()
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["previous"]["road"]['total_distance'], overall_stats["previous"]["duration"]['total_distance']],
name="Previous",
marker_color='rgb(55, 83, 109)'
))
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["new"]["road"]['total_distance'], overall_stats["new"]["duration"]['total_distance']],
name="New",
marker_color='rgb(26, 118, 255)'
))
fig.update_layout(
title="Total Distances",
xaxis_title="",
yaxis_title="Distance in km / Time in m",
barmode='group'
)
fig.show()
# %%
""" create bar plot for overall stats """
fig = go.Figure()
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["previous"]["road"]['average_distance'], overall_stats["previous"]["duration"]['average_distance']],
name="Previous",
marker_color='rgb(55, 83, 109)'
))
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["new"]["road"]['average_distance'], overall_stats["new"]["duration"]['average_distance']],
name="New",
marker_color='rgb(26, 118, 255)'
))
fig.update_layout(
title="Average Distances per Team",
xaxis_title="",
yaxis_title="Distance in km / Time in m",
barmode='group'
)
fig.show()
# %%
""" create bar plot for overall stats """
fig = go.Figure()
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["previous"]["road"]['average_group_distance'], overall_stats["previous"]["duration"]['average_group_distance']],
name="Previous",
marker_color='rgb(55, 83, 109)'
))
fig.add_trace(go.Bar(
x=["Distance", "Duration"],
y=[overall_stats["new"]["road"]['average_group_distance'], overall_stats["new"]["duration"]['average_group_distance']],
name="New",
marker_color='rgb(26, 118, 255)'
))
fig.update_layout(
title="Average Distance per Group",
xaxis_title="",
yaxis_title="Distance in km / Time in m",
barmode='group'
)
fig.show()
# %%

30
dfbnet/spielstaetten.py Normal file
View File

@ -0,0 +1,30 @@
# %%
import pandas as pd
import googlemaps
gmaps = googlemaps.Client(key='AIzaSyB76EhR4OqjdXHQUiTkHZC0Svx_7cPGqyU')
""" LOAD SPIELSTAETTEN FROM CSV"""
def get_venues():
venues = pd.read_excel("data/beispieldaten_spielstaetten.xlsx")
venues = venues.to_dict(orient='records')
for v in venues:
geocode_result = gmaps.geocode("Dresden "+v['SB_SPST_NAME'])
latitude =0
longitude =0
if len(geocode_result)>0:
location = geocode_result[0]['geometry']['location']
latitude = location['lat']
longitude = location['lng']
v['latitude'] = latitude
v['longitude'] = longitude
return venues
# %%

106
dfbnet/stats_sachsen.py Normal file
View File

@ -0,0 +1,106 @@
# %%
from pulp import (
LpVariable,
LpProblem,
LpMinimize,
lpSum,
LpStatus,
value,
LpInteger,
LpContinuous,
XPRESS,
)
import googlemaps
from gmplot import GoogleMapPlotter
import json
import pandas as pd
import ast
import random
import itertools
import time
with open("data/sachsen.json", "r", encoding="utf-8") as f:
competitions = json.load(f)
competitions = {ast.literal_eval(k): v for k, v in competitions.items()}
# region
# STAFFELN PRO ART UND KLASSE
# ('Herren', 'Landesliga') 1
# ('Herren', 'Landesklasse') 3
# ('Frauen', 'Landesliga') 1
# ('Frauen', 'Landesklasse') 3
# ('A-Junioren', 'Landesliga') 1
# ('A-Junioren', 'Landesklasse') 4
# ('Herren', 'Kreisoberliga') 13
# ('Herren', '1.Kreisliga (A)') 19
# ('Herren', '2.Kreisliga (B)') 8
# ('Herren', '3.Kreisliga (C)') 1
# ('Herren', '1.Kreisklasse') 21
# ('Herren', '2.Kreisklasse') 9
# ('A-Junioren', 'Kreisoberliga') 10
# ('A-Junioren', '1.Kreisliga (A)') 6
# ('Frauen', 'Kreisoberliga') 4
# ('Frauen', '1.Kreisliga (A)') 1
# ('Frauen', '1.Kreisklasse') 3
# ('B-Junioren', 'Landesliga') 1
# ('B-Junioren', 'Landesklasse') 4
# ('B-Junioren', 'Kreisoberliga') 13
# ('B-Junioren', '1.Kreisliga (A)') 13
# ('B-Junioren', '1.Kreisklasse') 1
# ('C-Junioren', 'Landesliga') 1
# ('C-Junioren', 'Landesklasse') 4
# ('C-Junioren', 'Kreisoberliga') 16
# ('C-Junioren', '1.Kreisliga (A)') 15
# ('C-Junioren', '1.Kreisklasse') 9
# ('D-Junioren', 'Landesliga') 1
# ('D-Junioren', 'Landesklasse') 6
# ('D-Junioren', 'Kreisoberliga') 16
# ('D-Junioren', '1.Kreisliga (A)') 24
# ('D-Junioren', '2.Kreisliga (B)') 8
# ('D-Junioren', '3.Kreisliga (C)') 2
# ('D-Junioren', '1.Kreisklasse') 33
# ('D-Junioren', '2.Kreisklasse') 10
# ('B-Juniorinnen', 'Landesliga') 1
# ('B-Juniorinnen', 'Landesklasse') 2
# ('C-Juniorinnen', 'Landesklasse') 3
# ('D-Juniorinnen', 'Kreisoberliga') 1
# ('Herren Ü35', 'Kreisoberliga') 4
# ('Herren Ü35', '1.Kreisliga (A)') 3
# ('Herren Ü35', '1.Kreisklasse') 3
# ('Herren Ü35', '2.Kreisklasse') 1
# ('Herren Ü40', '1.Kreisliga (A)') 5
# ('Herren Ü40', '1.Kreisklasse') 1
# ('Herren Ü50', '1.Kreisliga (A)') 1
# ('Herren Ü50', '1.Kreisklasse') 1
# ('Freizeitsport', '1.Kreisliga (A)') 3
# ('Freizeitsport', '1.Kreisklasse') 2
# endregion
previous_statistics = {}
competition_details = {}
color = None
for staffel, attr in competitions.items():
competitions[staffel]["distance"] = []
if (staffel[0], staffel[1]) not in competition_details:
competition_details[(staffel[0], staffel[1])] = {
"nStaffeln": 1,
"nTeams": 0,
"previous_distances": [],
"teams": [],
}
else:
competition_details[(staffel[0], staffel[1])]["nStaffeln"] += 1
for key, val in competitions.items():
print(key, val)
# %%

6
fastapi/mock/Dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM python:3.13
WORKDIR /www
COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir --upgrade -r requirements.txt
COPY app api
CMD ["uvicorn", "api.main:app", "--host", "0.0.0.0", "--port", "4321", "--reload"]

View File

89
fastapi/mock/app/main.py Normal file
View File

@ -0,0 +1,89 @@
from fastapi import FastAPI, Depends, HTTPException, Security
from fastapi.security.api_key import APIKeyHeader
from fastapi.openapi.models import APIKey
from fastapi.openapi.utils import get_openapi
from pydantic import BaseModel
from typing import Dict
app = FastAPI()
API_KEY = "your-secret-api-key"
API_KEY_NAME = "X-API-Key"
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
def get_api_key(api_key: str = Depends(api_key_header)):
if api_key == API_KEY:
return api_key
else:
raise HTTPException(
status_code=403,
detail="Invalid or missing API Key",
)
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="FastAPI with API Key Authentication",
version="1.0.0",
description="API documentation with API Key authentication",
routes=app.routes,
)
openapi_schema["components"]["securitySchemes"] = {
"APIKeyHeader": {
"type": "apiKey",
"name": API_KEY_NAME,
"in": "header",
}
}
openapi_schema["security"] = [{"APIKeyHeader": []}]
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
# In-memory "database"
items_db: Dict[int, dict] = {}
class Item(BaseModel):
name: str
description: str = None
price: float
@app.get("/items3/{item_id}", response_model=Item, dependencies=[Depends(get_api_key)])
async def get_item(item_id: int):
item = items_db.get(item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item
@app.post("/items/", response_model=Item, status_code=201)
async def create_item(item_id: int, item: Item):
if item_id in items_db:
raise HTTPException(status_code=400, detail="Item ID already exists")
items_db[item_id] = item.dict()
return item
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: int, item: Item):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
items_db[item_id] = item.dict()
return item
@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
del items_db[item_id]

View File

@ -0,0 +1,14 @@
version: "3"
services:
api:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
container_name: fastapi-mock
ports:
- "4321:4321"
user: "1002:1002"
environment:
- WATCHFILES_FORCE_POLLING=true # This is needed for hot reloading to work on windows (fix watch feature) for python
- WATCHPACK_POLLING=true # This is needed for hot reloading to work on windows (fix watch feature)