{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4d2a8b6c",
   "metadata": {},
   "source": [
    "#### Database"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "7be9eeff",
   "metadata": {},
   "outputs": [],
   "source": [
    "PROJECT_PATH = '/home/md/Work/ligalytics/leagues_stable/'\n",
    "import os, sys\n",
    "sys.path.insert(0, PROJECT_PATH)\n",
    "os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"leagues.settings\")\n",
    "\n",
    "\n",
    "# XPRESS ENVIRONMENT\n",
    "os.environ['XPRESSDIR'] = \"/opt/xpressmp_8.4\"\n",
    "os.environ['XPRESS'] = \"/opt/xpressmp_8.4/bin\"\n",
    "os.environ['LD_LIBRARY_PATH'] = os.environ['XPRESSDIR']+\"/lib:\"+os.environ['LD_LIBRARY_PATH']\n",
    "os.environ['DYLD_LIBRARY_PATH'] = os.environ['XPRESSDIR']+\"/lib:\"\n",
    "os.environ['SHLIB_PATH'] = os.environ['XPRESSDIR']+\"/lib:\"\n",
    "os.environ['LIBPATH'] = os.environ['XPRESSDIR']+\"/lib:\"\n",
    "os.environ['PYTHONPATH'] =os.environ['XPRESSDIR']+\"/lib:\"+os.environ['PYTHONPATH']\n",
    "os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+\"/lib/xprs.jar:\"\n",
    "os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+\"/lib/xprb.jar:\"+os.environ['CLASSPATH']\n",
    "os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+\"/lib/xprm.jar:\"+os.environ['CLASSPATH']\n",
    "os.environ['PATH'] =os.environ['XPRESSDIR']+\"/bin:\"+os.environ['PATH']\n",
    "\n",
    "\n",
    "from leagues import settings\n",
    "settings.DATABASES['default']['NAME'] = PROJECT_PATH+'/db.sqlite3'\n",
    "\n",
    "import django\n",
    "django.setup()\n",
    "\n",
    "from scheduler.models import *\n",
    "import operator\n",
    "from scheduler.solver.functions import *\n",
    "from common.functions import distanceInKmByGPS\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "from django.db.models import Count, F, Value\n",
    "from pulp import *\n",
    "\n",
    "\n",
    "def makeIntVar(v):\n",
    "    if type(v) != int :\n",
    "        v.cat=pulp.LpInteger\n",
    "        # print (\"int var \", v)\n",
    "\n",
    "def setStart(v,vl):\n",
    "    if type(v) != int :\n",
    "        vl=float(vl)\n",
    "        v.start = vl\n",
    "\n",
    "def setLB(v,vl):\n",
    "    if type(v) != int :\n",
    "        vl=float(vl)\n",
    "        v.lowBound = vl\n",
    "        # print (\"set lb \", v , \" = \", vl)\n",
    "\n",
    "\n",
    "def setUB(v,vl):\n",
    "    if type(v) != int :\n",
    "        vl=float(vl)\n",
    "        v.upBound = vl\n",
    "        # print (\"set ub \", v , \" = \", vl)\n",
    "\n",
    "def getVal(v):\n",
    "    if type(v) == int :\n",
    "        return v\n",
    "    else: \n",
    "        return v.value()\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "user_is_staff = True\n",
    "runMode = 'New'\n",
    "localsearch_time = 0\n",
    "solver = '?'\n",
    "\n",
    "s2 = 2\n",
    "thisScenario = Scenario.objects.get(id = s2)\n",
    "thisSeason = thisScenario.season\n",
    "thisLeague = thisSeason.league\n",
    "\n",
    "\n",
    "mathModelName=thisLeague.name\n",
    "if thisSeason.optimizationParameters!=\"\":\n",
    "    mathModelName=thisSeason.optimizationParameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "ca4ac4b8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Teams :  [10, 3, 17, 14, 15, 13, 12, 6, 9, 5, 8, 18, 19, 20, 11, 7, 4, 16]\n",
      "VIP Teams :  []\n",
      "TOP Teams :  []\n",
      "[]\n",
      "objectivePrio: Breaks\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "otherScenGames=[]\n",
    "if thisSeason.improvementObjective!=\"--\":\n",
    "    for otherScenario in Scenario.objects.filter(season=thisSeason):\n",
    "        impObjWeight = 5\n",
    "        if thisSeason.improvementObjective==\"stick to old solutions\":\n",
    "            impObjWeight = -5\n",
    "        if otherScenario.solutionlist() != [['']]:\n",
    "            for g in otherScenario.solutionlist():                \n",
    "                otherScenGames.append((int(g[1]),int(g[2]),int(g[3]), impObjWeight ))\n",
    "\n",
    "getGlobalCountry={ gc['uefa']: gc['id']  for gc in GlobalCountry.objects.values() }\n",
    "\n",
    "teamObjects = Team.objects.filter(season=thisSeason,active=True).order_by('position').values()\n",
    "teams=[]\n",
    "realteams=[]\n",
    "faketeams=[]\n",
    "importantteams=[]\n",
    "shortteams=[]\n",
    "t_pos={}\n",
    "t_pot={}\n",
    "t_color={}\n",
    "t_country={}\n",
    "t_globalCountry={}\n",
    "t_stadium = {}\n",
    "t_shortname = {}\n",
    "t_usePhases = {}\n",
    "t_lon = {}\n",
    "t_lat = {}\n",
    "t_attractivity ={}\n",
    "getTeamById={}\n",
    "getTeamIdByName={}\n",
    "getTeamIdByShortName={}\n",
    "getTeamByName={}\n",
    "getStadiumById={}\n",
    "teamByShort={}\n",
    "top4 = []\n",
    "for t in teamObjects:\n",
    "    # print (t['name'],  t['id'])\n",
    "    teams.append(t['id'])\n",
    "    shortteams.append(t['shortname'])\n",
    "    teamByShort[t['shortname']]= t['name']\n",
    "    getTeamIdByShortName[t['shortname']]=t['id']\n",
    "    if t['name']!='-':\n",
    "        realteams.append(t['id'])\n",
    "    else:\n",
    "        faketeams.append(t['id'])\n",
    "    if t['very_important']:\n",
    "        importantteams.append(t['id'])\n",
    "    t_country[t['id']]= t['country']\n",
    "    t_globalCountry[t['id']]= t['globalCountry_id']\n",
    "    if not t_globalCountry[t['id']] and t_country[t['id']] in getGlobalCountry.keys()  :\n",
    "        t_globalCountry[t['id']] = getGlobalCountry[t_country[t['id']]]\n",
    "    t_pos[t['name']]= t['position']\n",
    "    t_color[t['name']]=\"lightyellow\"\n",
    "    t_pot[t['id']]= t['pot']\n",
    "    t_stadium[t['id']]=t['stadium']\n",
    "    t_attractivity[t['id']]=t['attractivity']\n",
    "    t_lon[t['id']]=t['longitude']\n",
    "    t_lat[t['id']]=t['latitude']\n",
    "    t_shortname[t['id']]=t['shortname']\n",
    "    t_usePhases[t['id']]= thisScenario.usePhases\n",
    "    getTeamById[t['id']]=t['name']\n",
    "    getStadiumById[t['id']]=t['stadium']\n",
    "    getTeamIdByName[t['name']]=t['id']\n",
    "    getTeamByName[t['name']]=t\n",
    "    if t['attractivity'] >= thisSeason.topTeamMinCoefficient :\n",
    "        top4.append(t['id'])\n",
    "\n",
    "inactive_teams=[]\n",
    "for t in  Team.objects.filter(season=thisSeason,active=False).order_by('position').values():\n",
    "    inactive_teams.append(t['id'])\n",
    "    t_attractivity[t['id']]=t['attractivity']\n",
    "    t_lon[t['id']]=t['longitude']\n",
    "    t_lat[t['id']]=t['latitude']\n",
    "    t_country[t['id']]= t['country']\n",
    "    getTeamById[t['id']]=t['name']\n",
    "\n",
    "\n",
    "lowerBoundFound = False\n",
    "distance ={}\n",
    "attractivity ={}\n",
    "distanceById={}\n",
    "distanceInDaysById={}\n",
    "stadium_names = set([t['stadium'] for t in teamObjects if t['stadium']!=''])\n",
    "stadium_id={}\n",
    "stadium_name={}\n",
    "sid=0\n",
    "for stadium in stadium_names :\n",
    "    stadium_name[sid]=stadium\n",
    "    stadium_id[stadium]=sid\n",
    "    sid+=1\n",
    "stadiums = list(stadium_name.keys())\n",
    "teamsOfStadium ={ st:[] for st in stadiums }\n",
    "\n",
    "travelDict= thisSeason.travelDict()\n",
    "for t1 in teamObjects:\n",
    "    if t1['stadium']!='':\n",
    "        teamsOfStadium[stadium_id[t1['stadium']]].append(t1['id'])\n",
    "    for t2 in teamObjects:\n",
    "        # distance[t1['name'],t2['name']] = distanceInKm(t1,t2)\n",
    "        # print (t1['name'],t2['name'],distance[t1['name'],t2['name']] ,\" -> \", travelDict[t1['id']][t2['id']]['distance'] )\n",
    "        distance[t1['name'],t2['name']] = travelDict[t1['id']][t2['id']]['distance'] \n",
    "        distanceById[t1['id'],t2['id']] = distance[t1['name'],t2['name']]\n",
    "        distanceInDaysById[t1['id'],t2['id']] = int(distance[t1['name'],t2['name']]/350 +0.99)\n",
    "        attractivity[t1['id'],t2['id']] = int(100*t1['attractivity']*t2['attractivity'])\n",
    "\n",
    "\n",
    "dayObjects = Day.objects.filter(season=thisSeason).values()\n",
    "days = [ d['id'] for d in dayObjects if d['round']>0 ]\n",
    "\n",
    "higherSeasons = thisSeason.higherSeasons()\n",
    "higherGames = thisSeason.higherGames()\n",
    "\n",
    "this_season_team_names = list(getTeamByName.keys())\n",
    "\n",
    "higherTeamObjects = Team.objects.filter(season__in=higherSeasons,active=True).values()\n",
    "higherDayObjects = Day.objects.filter(season__in=higherSeasons,maxGames__gte=1).values()\n",
    "higherTeams = [ t['id'] for t in higherTeamObjects] \n",
    "higherTeamsOf={ t: [] for t in teams}\n",
    "for t in higherTeamObjects:\n",
    "    getTeamById[t['id']]=t['name']\n",
    "    t_country[t['id']]=t['country']\n",
    "    if t['name'] in this_season_team_names:\n",
    "        higherTeamsOf[getTeamIdByName[t['name']]].append(t['id'])\n",
    "\n",
    "print (\"Teams : \" , teams)\n",
    "print (\"VIP Teams : \" , importantteams)\n",
    "print (\"TOP Teams : \" , top4)\n",
    "\n",
    "allConferences = Conference.objects.filter(scenario=s2)\n",
    "sharedStadiums = False\n",
    "for ff in  thisSeason.federationmember.all():\n",
    "    sharedStadiums= ff.federation.sharedStadiums\n",
    "\n",
    "# print (\"\\nGroups : \")\n",
    "t_conference = {t : 0 for t in teams }\n",
    "conf_teams={}\n",
    "for c in Conference.objects.filter(scenario=s2,regional=False).order_by('name'):\n",
    "    # print (\"A\" , c)\n",
    "    cteams = c.teams.filter(active=True)\n",
    "    conf_teams[c.name]=[]\n",
    "    for t in cteams:\n",
    "        conf_teams[c.name].append(t.id)\n",
    "        # print (\"group for \", t)\n",
    "        # if t_conference[t.id]!=0:\n",
    "            # print (getTeamById[t.id] , \" in several groups\")\n",
    "        if t_conference[t.id]==0 or len(cteams)< len(t_conference[t.id].teams.filter(active=True)):\n",
    "            t_conference[t.id]=c\n",
    "            # print  (\" is \" , c )\n",
    "# for t in set([t for t in teams if t_conference[t]==0 ]):\n",
    "    # print (\" no group \" ,  getTeamById[ t])\n",
    "# return ''\n",
    "\n",
    "prioVal ={'A': 25 , 'B': 5 , 'C': 1, 'Hard' : 1000}\n",
    "\n",
    "sw_prio = {}\n",
    "sw_float1 = {}\n",
    "sw_float2 = {}\n",
    "sw_int1 = {}\n",
    "sw_int2 = {}\n",
    "sw_text = {}\n",
    "for sw in SpecialWish.objects.filter(scenario=s2):\n",
    "    sw_prio[sw.name]=prioVal[sw.prio] if sw.active else 0\n",
    "    sw_float1[sw.name]=sw.float1\n",
    "    sw_float2[sw.name]=sw.float2\n",
    "    sw_int1[sw.name]=sw.int1\n",
    "    sw_int2[sw.name]=sw.int2\n",
    "    sw_text[sw.name]=sw.text\n",
    "special_wishes=list(sw_prio.keys())\n",
    "special_wishes_active = [ sw for sw in special_wishes if sw_prio[sw]>0 ]\n",
    "\n",
    "print (special_wishes_active)\n",
    "\n",
    "noBreakLimitTeams = []\n",
    "if \"allowManyBreaksInRow\" in special_wishes_active :\n",
    "    noBreakLimitTeams = sw_text[\"allowManyBreaksInRow\"].split(\",\")\n",
    "    print (noBreakLimitTeams)\n",
    "    noBreakLimitTeams = [t for t in teams if getTeamById[t] in noBreakLimitTeams]\n",
    "    print (noBreakLimitTeams)                \n",
    "\n",
    "pairings_tmp = Pairing.objects.filter(scenario=s2, active=True).values()\n",
    "pairings_tmp2 = Pairing.objects.filter(scenario__is_published=True, team1__id__in=teams, team2__id__in=higherTeams, active=True).values()\n",
    "pairings_tmp3 = Pairing.objects.filter(scenario__is_published=True, team2__id__in=teams, team1__id__in=higherTeams, active=True).values()\n",
    "pairings = []\n",
    "for p in  [ p for p in pairings_tmp if p['team1_id'] in teams and p['team2_id'] in teams+higherTeams ] + [ p for p in pairings_tmp2 ] + [ p for p in pairings_tmp3]:\n",
    "    if p not in pairings:\n",
    "        pairings.append(p) \n",
    "breaks = Break.objects.filter(season=thisSeason).values()\n",
    "blockings_tmp = Blocking.objects.filter(scenario=s2,active=True).values()\n",
    "blockings = [ bl for bl in blockings_tmp if  bl['team_id'] in teams ]\n",
    "\n",
    "# times = ['Early', 'Late']\n",
    "timeObjects = TimeSlot.objects.filter(season=thisSeason).values()\n",
    "times = [ str(t['id']) for t in timeObjects]\n",
    "getTimeById = {str(t['id']):t['name'] for t in timeObjects}\n",
    "getIdByTime = {t['name']:str(t['id']) for t in timeObjects}\n",
    "\n",
    "blocked_arena = {(t,d, tm) : False for t in teams for d in days for tm in [\"----\"] + times  }\n",
    "hidden_arena = {(t,d, tm) : False for t in teams for d in days for tm in [\"----\"] + times  }\n",
    "nBlockingHome=0\n",
    "nBlockingAway=0\n",
    "\n",
    "for bl in blockings:\n",
    "    if bl['type'] in [\"Hide\"]:\n",
    "        hidden_arena[(bl['team_id'],bl['day_id'], bl['time'] )]=True\n",
    "\n",
    "    if bl['type'] in [\"Home\",\"Hide\"]:\n",
    "        nBlockingHome+=1\n",
    "        blocked_arena[(bl['team_id'],bl['day_id'], bl['time'] )]=True\n",
    "    else:\n",
    "        nBlockingAway+=1\n",
    "\n",
    "nTeams=len(teams)\n",
    "nPhases =thisSeason.numPhases\n",
    "groupView = thisSeason.groupBased\n",
    "\n",
    "gameCntr={(t1,t2) : 0 for t1 in teams for t2 in teams}\n",
    "undirectedGameCntr={(t1,t2) : 0 for t1 in teams for t2 in teams}\n",
    "\n",
    "if thisSeason.useFeatureOpponentMatrix:\n",
    "    gameRequirements = GameRequirement.objects.filter(season=thisSeason, team1__active=True, team2__active=True) \n",
    "    # print (len(gameRequirements))\n",
    "    for gm in gameRequirements:\n",
    "        if thisSeason.undirectedGames:\n",
    "            undirectedGameCntr[(gm.team1.id,gm.team2.id)]+=gm.number\n",
    "            undirectedGameCntr[(gm.team2.id,gm.team1.id)]+=gm.number\n",
    "            # print ( \"found undirected  game \" , (gm.team1.id,gm.team2.id) , gm.number )\n",
    "        else:\n",
    "            gameCntr[(gm.team1.id,gm.team2.id)]+=gm.number\n",
    "            # print ( \"found directed  game \" , (gm.team1.id,gm.team2.id) , gm.number )\n",
    "else:\n",
    "    for t1 in teams:\n",
    "        for t2 in teams:\n",
    "            if t1!=t2:\n",
    "                gameCntr[(t1,t2)]+=nPhases/2\n",
    "\n",
    "    for c in Conference.objects.filter(scenario=s2):\n",
    "        cteams = c.teams.filter(active=True)\n",
    "        for t1 in cteams:\n",
    "            for t2 in cteams:\n",
    "                if t1!=t2:\n",
    "                    gameCntr[(t1.id,t2.id)]+=c.deltaGames/2\n",
    "\n",
    "    for (t1,t2) in gameCntr.keys():\n",
    "        if gameCntr[(t1,t2)]%1!=0 :\n",
    "            undirectedGameCntr[(t1,t2)]=1\n",
    "        gameCntr[(t1,t2)] = int(gameCntr[(t1,t2)])\n",
    "\n",
    "games = [ gm for gm in gameCntr.keys() if gameCntr[gm]+undirectedGameCntr[gm]>0]\n",
    "\n",
    "objectiveFunctionWeights = ObjectiveFunctionWeight.objects.filter(scenario=s2).values()\n",
    "\n",
    "gew={}\n",
    "gew['Trips'] = 5\n",
    "gew['Derbies'] = 5\n",
    "gew['Pairings'] = 5\n",
    "for ow in objectiveFunctionWeights:\n",
    "    gew[ow['name']]= ow['use'] * ow['prio']\n",
    "\n",
    "objectivePrio = 'Breaks'\n",
    "if  gew['Trips']>gew['Breaks'] :\n",
    "    objectivePrio = 'Trips'\n",
    "\n",
    "print(\"objectivePrio:\", objectivePrio)\n",
    "\n",
    "specialGameControl= mathModelName in [\"Florida State League\"]\n",
    "\n",
    "if len(games)==0:\n",
    "    games = [ (t1,t2) for t1 in teams for t2 in teams if t1!=t2 ]\n",
    "    specialGameControl=True\n",
    "    \n",
    "realgames= [(t1,t2) for (t1,t2) in games if getTeamById[t1]!=\"-\" and getTeamById[t2]!=\"-\"]\n",
    "\n",
    "# nur wenn die solutionlist alle spiele enthält, wird dieser modus aktiviert\n",
    "evalRun = ( runMode == \"Improve\" and localsearch_time==0 \n",
    "            and len(thisScenario.solutionlist())==sum([gameCntr[gm]+0.5*undirectedGameCntr[gm] for gm in realgames]) )\n",
    "\n",
    "opponents = { t: set([]) for t in realteams}\n",
    "\n",
    "for (t1,t2) in games:\n",
    "    if t1 in realteams and t2 in realteams:\n",
    "        opponents[t1].add(t2)\n",
    "        opponents[t2].add(t1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "1c51e80c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gmClusters range(1, 2)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "model7 = pulp.LpProblem(\"FindGameClusters_\"+str(thisScenario.id), pulp.LpMinimize)\n",
    "gmClusters = range(1,nTeams+2)\n",
    "\n",
    "if nPhases>0 and not thisSeason.useFeatureOpponentMatrix and not specialGameControl:\n",
    "    gmClusters = range(1,2)    \n",
    "\n",
    "print (\"gmClusters\", gmClusters)\n",
    "x7 = {(t,c) : pulp.LpVariable('x7_'+str(t)+'_'+str(c), lowBound = 0, upBound = 1,  cat = pulp.LpInteger) for t in teams for c in gmClusters}\n",
    "\n",
    "for t in realteams: \n",
    "    model7+= lpSum([ x7[(t,c)] for c in gmClusters]) ==1 \n",
    "\n",
    "for (t1,t2) in gameCntr.keys():\n",
    "    if gameCntr[(t1,t2)]+undirectedGameCntr[(t1,t2)]==0 and t1!=t2:\n",
    "        for c in gmClusters:\n",
    "            model7+= x7[(t1,c)]+ x7[(t2,c)]  <=1\n",
    "\n",
    "model7+= lpSum([ c*x7[(t,c)] for (t,c) in x7.keys()]) \n",
    "\n",
    "if solver == \"CBC\":\n",
    "    model7.solve(PULP_CBC_CMD(threads = 8,msg=1))\n",
    "elif solver == \"Gurobi\":\n",
    "    model7.solve(GUROBI(MIPGap=0.01,msg=1))\n",
    "else:\n",
    "    model7.solve(XPRESS(msg=1, targetGap=0.01, maxSeconds = 100, keepFiles=True))\n",
    "\n",
    "\n",
    "gameClusterTeams = { c : [ t for t in teams if x7[(t,c)].value()>0.01] for c in gmClusters }\n",
    "gameClusters=[ c for c in gameClusterTeams.keys() if len(gameClusterTeams[c])>0]\n",
    "biggestGroupSize = max([len(gameClusterTeams[c]) for c in gameClusters])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "a446fffc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "nRounds  34\n",
      "nPhases  2\n",
      "nRoundsPerPhase  17\n",
      "defaultGameRepetions  1\n",
      "tripStartHeuristicGroupsize  1\n"
     ]
    }
   ],
   "source": [
    "nPhases = min( [  gameCntr[(t1,t2)]+ gameCntr[(t2,t1)] + undirectedGameCntr[(t1,t2)] for (t1,t2) in realgames ])\n",
    "tripStartHeuristicGroupsize = 1 if thisSeason.tripStartHeuristicGroupsize==\"None\" else int(thisSeason.tripStartHeuristicGroupsize)\n",
    "defaultGameRepetions = 1 if not mathModelName in [\"NHL\", \"NBA\"] else 2\n",
    "nPhases=max(1,int(nPhases/defaultGameRepetions))\n",
    "phases = range(nPhases)\n",
    "\n",
    "useBasicGames = nPhases>0\n",
    "\n",
    "if not useBasicGames:\n",
    "    print ('no basic games but biggest group size ', biggestGroupSize, ' in nPhases ' , nPhases )\n",
    "\n",
    "nRounds = thisSeason.nRounds\n",
    "rounds=range(1,nRounds+1)\n",
    "\n",
    "# nRoundsPerPhase= 1\n",
    "# if nPhases>0:\n",
    "nRoundsPerPhase= int(nRounds/nPhases)\n",
    "\n",
    "print ('nRounds ',nRounds)\n",
    "print ('nPhases ',nPhases)\n",
    "print ('nRoundsPerPhase ',nRoundsPerPhase)\n",
    "print ('defaultGameRepetions ',defaultGameRepetions)\n",
    "print ('tripStartHeuristicGroupsize ',tripStartHeuristicGroupsize)\n",
    "\n",
    "getPhaseOfRound = { r : min( nPhases-1 , int((r-1)/nRoundsPerPhase)) for r in rounds }\n",
    "getDaysOfPhase = { p : [] for p in phases }\n",
    "getDays = { r : [] for r in rounds}\n",
    "roundGamesMax = { r : 0 for r in rounds}\n",
    "roundGamesMin = { r : 0 for r in rounds}\n",
    "getDayById = { d['id'] : d for d in dayObjects}\n",
    "getDayByDateTime = { }\n",
    "getNiceDayRaw = { d['id'] : d['day']  for d in dayObjects}\n",
    "getNiceDay = { d['id'] : d['day']  for d in dayObjects}\n",
    "getWeekDay = { d['id'] : '' for d in dayObjects}\n",
    "getDateTimeDay = { d['id'] : '' for d in dayObjects}\n",
    "getRoundByDay = { d['id'] : d['round'] for d in dayObjects if d['round']>0 }\n",
    "getDayMinGames = { d['id'] : d['minGames'] for d in dayObjects if d['round']>0 }\n",
    "getDayMaxGames = { d['id'] : d['maxGames'] for d in dayObjects if d['round']>0 }\n",
    "getRoundsByDay = { d['id'] : [] for d in dayObjects }\n",
    "nDerbies = { d['id'] : [d['nDerbies']] for d in dayObjects}\n",
    "roundDays =[]\n",
    "roundDaysMin ={ }\n",
    "roundDaysMax ={ }\n",
    "wds= {0:'Mon', 1:'Tue', 2:'Wed', 3:'Thu', 4:'Fri', 5:'Sat', 6:'Sun'}\n",
    "for d in dayObjects:\n",
    "    if d['round']>0 :\n",
    "        getRoundsByDay[d['id']].append(d['round'])\n",
    "        getDays[d['round']].append(d['id'])\n",
    "        roundDays.append((d['round'],d['id']))\n",
    "        roundDaysMin[(d['round'],d['id'])]=d['minGames']\n",
    "        roundDaysMax[(d['round'],d['id'])]=d['maxGames']\n",
    "        roundGamesMax[d['round']]=min(nTeams/2, roundGamesMax[d['round']]+d['maxGames'] )\n",
    "        roundGamesMin[d['round']]+=d['minGames']\n",
    "        ph = getPhaseOfRound[d['round']]\n",
    "        getDaysOfPhase[ph].append(d['id'])\n",
    "    if d['round2']>0:\n",
    "        getRoundsByDay[d['id']].append(d['round2'])\n",
    "        getDays[d['round2']].append(d['id'])\n",
    "        roundDays.append((d['round2'],d['id']))\n",
    "        roundDaysMin[(d['round2'],d['id'])]=d['minGames2']\n",
    "        roundDaysMax[(d['round2'],d['id'])]=d['maxGames2']\n",
    "        roundGamesMax[d['round2']]=min(nTeams/2, roundGamesMax[d['round2']]+d['maxGames2'] )\n",
    "        roundGamesMin[d['round2']]+=d['minGames2']\n",
    "\n",
    "    dt = parse(d['day'])\n",
    "    getDateTimeDay[d['id']] = dt\n",
    "    getDayByDateTime[dt] = d['id']\n",
    "    getNiceDay[d['id']] = str(dt.strftime('%a, %b %d, %Y'))\n",
    "    getWeekDay[d['id']] = str(wds[dt.weekday()])\n",
    "\n",
    "countries = list(set( [t_country[t] for t in teams ]))  \n",
    "\n",
    "getWeekDaysPerRound = { r : [ getWeekDay[d] for d in getDays[r]] for r in rounds}\n",
    "\n",
    "wd = {\"Mondays\":0 , \"Tuesdays\":1 , \"Wednesdays\":2 , \"Thursdays\":3 , \"Fridays\":4 , \"Saturdays\":5 , \"Sundays\":6}\n",
    "t_site_bestTimeSlots = { (t,d): [] for t in realteams for d in days }\n",
    "prio_weight = { \"A\" : 0 , \"B\" : 50 , \"C\" : 100}\n",
    "\n",
    "\n",
    "toTime=False\n",
    "\n",
    "\n",
    "earliestDay={r: getDays[r][0] for r in rounds }\n",
    "latestDay={r: getDays[r][0] for r in rounds }\n",
    "for r in rounds :\n",
    "    for d in getDays[r]:\n",
    "        dt=getDateTimeDay[d]\n",
    "        if dt>getDateTimeDay[latestDay[r]]:\n",
    "            latestDay[r]=d\n",
    "        if dt<getDateTimeDay[earliestDay[r]]:\n",
    "            earliestDay[r]=d\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "df5d6ff1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "nPhases  2\n",
      "nGameClusters  1\n",
      "nBasicTeamsPerCluster  18\n",
      "nBasicTeams  18\n",
      "nBasicRounds  34\n",
      "nRounds1  17\n"
     ]
    }
   ],
   "source": [
    "basicTeams = teams\n",
    "basicGames = [ (t1,t2) for (t1,t2) in games if t1 in basicTeams and t2 in basicTeams ]\n",
    "\n",
    "nBasicTeams = len (basicTeams)\n",
    "nBasicTeamsPerCluster = int(nBasicTeams/len(gameClusters)+0.9)\n",
    "if nBasicTeamsPerCluster%2==1:\n",
    "    nBasicTeamsPerCluster+=1\n",
    "\n",
    "\n",
    "\n",
    "nRounds1 = nBasicTeamsPerCluster-1\n",
    "nBasicRounds= nPhases * (nBasicTeamsPerCluster-1)\n",
    "\n",
    "if nBasicTeamsPerCluster==1 or nBasicRounds==1:\n",
    "    nBasicRounds=nRounds\n",
    "    nRounds1=nRounds\n",
    "\n",
    "basicRounds = range (1,nBasicRounds+1)\n",
    "\n",
    "print (\"nPhases \", nPhases)\n",
    "print (\"nGameClusters \", len(gameClusters))\n",
    "print (\"nBasicTeamsPerCluster \", nBasicTeamsPerCluster)\n",
    "print (\"nBasicTeams \", nBasicTeams)\n",
    "print (\"nBasicRounds \", nBasicRounds)\n",
    "print (\"nRounds1 \", nRounds1)\n",
    "\n",
    "\n",
    "\n",
    "stretch = nRounds/nBasicRounds\n",
    "# print (\"regionalPatternUse \" , regionalPatternUse)\n",
    "rounds1=range(1,nRounds1+1)\n",
    "nGames=nTeams*nRounds1\n",
    "\n",
    "\n",
    "getBasicRound={ r : int ((r-1)/stretch)+1  for r in rounds}\n",
    "getRealRounds = { br : [ r for r in rounds if getBasicRound[r]==br ] for br in basicRounds}\n",
    "# print (\"stretch : \" , stretch)\n",
    "\n",
    "getBasicDays= {r : [] for r in basicRounds}\n",
    "for r in rounds:\n",
    "    getBasicDays[getBasicRound[r]]+=(getDays[r])\n",
    "\n",
    "\n",
    "getRoundDaysByDay = {d: [ rd for rd in roundDays if rd[1]==d ] for d in days}\n",
    "getRoundDaysByRound = {r: [rd for rd in roundDays if rd[0]==r ] for r in rounds}\n",
    "\n",
    "daysSorted =[]\n",
    "for dt in sorted([ getDateTimeDay[d] for d in days]):\n",
    "    daysSorted.append (  getDayByDateTime[dt] )\n",
    "\n",
    "minRest = { (t,s1,s2) : thisSeason.minDaysBetweenGames for t in teams for s1 in ['A','H'] for s2 in ['A','H']}\n",
    "for t in teamObjects:\n",
    "    minRest[(t['id'],'H','H')] = max(minRest[(t['id'],'H','H')], t['minRest_HH'] )\n",
    "    minRest[(t['id'],'H','A')] = max(minRest[(t['id'],'H','A')], t['minRest_HA'] )\n",
    "    minRest[(t['id'],'A','H')] = max(minRest[(t['id'],'A','H')], t['minRest_AH'] )\n",
    "    minRest[(t['id'],'A','A')] = max(minRest[(t['id'],'A','A')], t['minRest_AA'] )\n",
    "    \n",
    "maxMinRest = { t : max( [ minRest[(t,s1,s2)] for s1 in ['A','H'] for s2 in ['A','H'] ] )  for t in teams }\n",
    "\n",
    "excessGames = { rd: -roundDaysMax[rd] for rd in roundDays }\n",
    "deficientGames = { rd: roundDaysMin[rd] for rd in roundDays }\n",
    "\n",
    "excessGames = { rd:  max(0,excessGames[rd])  for rd in roundDays }\n",
    "deficientGames = { rd: max(0,deficientGames[rd])  for rd in roundDays }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "0d1f6342",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "processing encounters\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "allowed_weekdays={'--' : [0,1,2,3,4,5,6], 'Mondays':[0], 'Tuesdays':[1], 'Wednesdays':[2], 'Thursdays':[3], 'Fridays':[4], 'Saturdays':[5],'Sundays':[6], 'Weekdays':[0,1,2,3,4], 'Weekends':[5,6], 'Mon.-Thu.':[0,1,2,3] , 'Fri.-Sun.':[4,5,6] } \n",
    "\n",
    "hawishes = HAWish.objects.filter(scenario=s2,active=True).order_by('reason').values()\n",
    "hawTeams = {}\n",
    "hawDays = {}\n",
    "hawTimes = {}\n",
    "hawRounds = {}\n",
    "hawRoundsString = {}\n",
    "for c in HAWish.objects.filter(scenario=s2):\n",
    "    # print ()\n",
    "    # print (c.reason   )\n",
    "    hawDays[c.id] = []\n",
    "    hawTeams[c.id] = []\n",
    "    hawTimes[c.id]= [ str(dd.id) for dd in c.timeslots.all() ]\n",
    "    if c.selection_mode == 0:\n",
    "        for t in c.teams.filter(active=True):\n",
    "            hawTeams[c.id].append(t.id)\n",
    "    elif c.selection_mode == 1:\n",
    "        for t in c.groups.all():\n",
    "            for t2 in t.teams.filter(active=True):\n",
    "                hawTeams[c.id].append(t2.id)\n",
    "    elif c.selection_mode == 2:\n",
    "        cids  = [ cn.id for cn in c.countries.all()]\n",
    "        for t in teams:\n",
    "            if t_globalCountry[t] in cids:\n",
    "                hawTeams[c.id].append(t)\n",
    "\n",
    "    if c.multidate:\n",
    "        hawDays[c.id]= [ dd.id for dd in c.dates.all() ]\n",
    "        # print (\"multidate\")\n",
    "    else:\n",
    "        if c.day and not c.day2:\n",
    "            hawDays[c.id].append(c.day.id)\n",
    "            # print('+ ',getDayById[e['day_id']])\n",
    "\n",
    "        if not c.day and c.day2:\n",
    "            hawDays[c.id].append(c.day2.id)\n",
    "            # print('+ ',getDayById[e['day2_id']])\n",
    "\n",
    "        if not c.day and not c.day2:\n",
    "            for d in days:\n",
    "                dt =  getDateTimeDay[d] \n",
    "                if dt.weekday() in allowed_weekdays[c.weekdays]:\n",
    "                    # print (hawDays[e['id']])\n",
    "                    hawDays[c.id].append(d)\n",
    "                    # print('+ ',getDayById[d])\n",
    "\n",
    "        if c.day and c.day2:\n",
    "            day1= getDateTimeDay[c.day.id] \n",
    "            day2= getDateTimeDay[c.day2.id] \n",
    "            for d in days:\n",
    "                dt = getDateTimeDay[d] \n",
    "                # print (day1, \"<=\" , dt , \"<=\", day2 , \" \" ,  day1<=dt and dt<=day2 )\n",
    "                if day1<=dt and dt<=day2 and dt.weekday() in allowed_weekdays[c.weekdays]:\n",
    "                    # print (day1, \"<=\" , dt , \"<=\", day2 , \" \" ,  day1<=dt and dt<=day2 )\n",
    "                    hawDays[c.id].append(d)\n",
    "                    # print('+ ',getDayById[d])\n",
    "\n",
    "print (\"processing encounters\")\n",
    "encwishes = EncWish.objects.filter(scenario=s2,active=True).values()\n",
    "encTeams1 = {}\n",
    "encTeams2 = {}\n",
    "encGroups = {}\n",
    "encGames = {}\n",
    "encTeams1String = {}\n",
    "encTeams2String = {}\n",
    "encDays =  { e['id'] : [] for e in encwishes}\n",
    "encDaySets={}\n",
    "encTimes = {}\n",
    "encRounds =  { e['id'] : [] for e in encwishes}\n",
    "encRoundsString =  { e['id'] : \"\" for e in encwishes}\n",
    "for c in EncWish.objects.filter(scenario=s2,active=True):\n",
    "    encTimes[c.id]= [ str(dd.id) for dd in c.timeslots.all() ]\n",
    "    encTeams1[c.id] = []\n",
    "    encTeams2[c.id] = []\n",
    "    encGroups[c.id] = []\n",
    "    encTeams1String[c.id] = ''\n",
    "    encTeams2String[c.id] = ''\n",
    "    for t in c.encounterGroups.all():\n",
    "        encGroups[c.id].append(t)\n",
    "    if c.useGroups:\n",
    "        for gr in c.teams1_groups.all():\n",
    "            for t in gr.teams.filter(active=True):\n",
    "                encTeams1[c.id].append(t.id)\n",
    "                encTeams1String[c.id]+=gr.name +', '\n",
    "        for gr in c.teams2_groups.all():\n",
    "            for t in gr.teams.filter(active=True):\n",
    "                encTeams2[c.id].append(t.id)\n",
    "                encTeams2String[c.id]+=t.name +', '\n",
    "    else:\n",
    "        for t in c.teams1.filter(active=True):\n",
    "            encTeams1[c.id].append(t.id)\n",
    "            encTeams1String[c.id]+=t.name +', '\n",
    "        for t in c.teams2.filter(active=True):\n",
    "            encTeams2[c.id].append(t.id)\n",
    "            encTeams2String[c.id]+=t.name +', '\n",
    "    encTeams1String[c.id] = encTeams1String[c.id][:-2]\n",
    "    encTeams2String[c.id] = encTeams2String[c.id][:-2]\n",
    "\n",
    "    if c.useEncounterGroups:\n",
    "        tmp_games = [(t1.id,t2.id) for eg in encGroups[c.id] for ec in eg.encounter_set.all() for t1 in ec.homeTeams.all() for t2 in ec.awayTeams.all() if t1!=t2 ]\n",
    "        tmp_games += [(t2.id,t1.id) for eg in encGroups[c.id] for ec in eg.encounter_set.all() for t1 in ec.homeTeams.all()  for t2 in ec.awayTeams.all() if t1!=t2  and ec.symmetry]\n",
    "        encGames[c.id] = [tmp_games]\n",
    "    else:\n",
    "        elemHomeTeams= [encTeams1[c.id]]\n",
    "        if c.forEachTeam1:\n",
    "            elemHomeTeams= [ [t] for t in encTeams1[c.id]]\n",
    "        elemAwayTeams= [ encTeams2[c.id]]\n",
    "        if c.forEachTeam2:\n",
    "            elemAwayTeams= [ [t] for t in encTeams2[c.id]]\n",
    "        encGames[c.id]= []\n",
    "        # print (\"NEW ENC \", elemHomeTeams,elemAwayTeams)\n",
    "        for elh in elemHomeTeams:\n",
    "            for ela in elemAwayTeams:\n",
    "                # print (\" ---  ENC \", elh,ela)\n",
    "                tmp_games= [(t1,t2) for t1 in elh for t2 in ela if t1!=t2 ] \n",
    "                if c.symmetry:\n",
    "                    tmp_games += [(t1,t2) for t1 in ela for t2 in elh if t1!=t2 ] \n",
    "                encGames[c.id].append( tmp_games)\n",
    "\n",
    "    if c.multidate:\n",
    "        encDays[c.id]= [ dd.id for dd in c.dates.all() ]\n",
    "    else:\n",
    "        if c.day:\n",
    "            day1= getDateTimeDay[c.day.id] \n",
    "\n",
    "        if c.day and not c.day2:\n",
    "            encDays[c.id].append(c.day.id)\n",
    "\n",
    "        if not c.day and c.day2:\n",
    "            encDays[c.id].append(c.day2.id)\n",
    "\n",
    "        if not c.day and not c.day2:\n",
    "            for d in days:\n",
    "                dt= getDateTimeDay[d] \n",
    "                # dt = parse(getDayById[d]['day'])\n",
    "                if dt.weekday() in allowed_weekdays[c.weekdays]:\n",
    "                    encDays[c.id].append(d)\n",
    "\n",
    "        if c.day and c.day2:\n",
    "            # day1= parse(c.day.day)\n",
    "            # day2= parse(c.day2.day)\n",
    "            day1= getDateTimeDay[c.day.id] \n",
    "            day2= getDateTimeDay[c.day2.id] \n",
    "            for d in days:\n",
    "                dt= getDateTimeDay[d] \n",
    "                # dt = parse(getDayById[d]['day'])\n",
    "                if day1<=dt and dt<=day2 and dt.weekday() in allowed_weekdays[c.weekdays]:\n",
    "                    encDays[c.id].append(d)\n",
    "\n",
    "    encDaySets[c.id]=[]\n",
    "    elemDays= [ encDays[c.id]]\n",
    "    if c.forEachDay or c.forOneDay:\n",
    "        elemDays= [ [d] for d in encDays[c.id]]\n",
    "\n",
    "    lastDaySet=[]\n",
    "    for d in elemDays :\n",
    "        tmpDays=d\n",
    "        if (c.forEachDay or c.forOneDay) and c.timeframe!=0:\n",
    "            tmpDays = []\n",
    "            # day1= parse(getDayById[d[0]]['day'])\n",
    "            day1= getDateTimeDay[d[0]] \n",
    "            if  c.timeframe>0:\n",
    "                day2= day1 + datetime.timedelta(days=c.timeframe-1)\n",
    "                # print (e)\n",
    "                # print (day1, day2)\n",
    "                for d3 in days:\n",
    "                    dt= getDateTimeDay[d3] \n",
    "                    # dt = parse(getDayById[d3]['day'])\n",
    "                    if day1<=dt and dt<=day2 :\n",
    "                        tmpDays.append(d3) \n",
    "            else:                    \n",
    "                r1 = getDayById[d[0]]['round']\n",
    "                for d3 in days:\n",
    "                    # dt = parse(getDayById[d3]['day'])\n",
    "                    dt= getDateTimeDay[d3] \n",
    "                    if day1<=dt and getRoundByDay[d3]< r1 + (-c.timeframe) :\n",
    "                        tmpDays.append(d3)\n",
    "                # print (\" ROUNDWISH \", e, elemEncWishDays[cntr], e['timeframe'])\n",
    "                # for d4 in elemEncWishDays[cntr]:\n",
    "                #     print (\" - \" ,getDayById[d4]['day'])\n",
    "\n",
    "        if len([ d for  d in  tmpDays if d not in lastDaySet ])>0:\n",
    "            encDaySets[c.id].append(tmpDays)\n",
    "            lastDaySet = tmpDays\n",
    "            # print(\"-.--- NEW DAYS\", tmpDays)\n",
    "\n",
    "    for ds in encDaySets[c.id]:\n",
    "        for d3 in ds:\n",
    "            encRounds[c.id]+= getRoundsByDay[d3]\n",
    "    encRounds[c.id]=sorted(list(set(encRounds[c.id])))\n",
    "    for r in encRounds[c.id]:\n",
    "        encRoundsString[c.id]+= str(r)+\"_\"\n",
    "    if encRoundsString[c.id]!=\"\":\n",
    "        encRoundsString[c.id]=encRoundsString[c.id][:-1]\n",
    "    # print (encRoundsString[c.id] , \" # \" ,c.affected_rounds ,  encRoundsString[c.id] != c.affected_rounds)\n",
    "\n",
    "\n",
    "onlyEarlyDays= []\n",
    "onlyLateDays= []\n",
    "\n",
    "\n",
    "alwaysConsiderAllGames = not mathModelName in [\"Florida State League\" , \"UEFA NL\"]\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "elemEncWishes ={e['id'] : [] for e in encwishes}\n",
    "elemEncWishGames={}\n",
    "elemEncWishDays={}\n",
    "\n",
    "cntr=0\n",
    "for e in encwishes:\n",
    "    for eg in encGames[e['id']] :\n",
    "        for ed in encDaySets[e['id']]:\n",
    "            if len(eg)>0 and len(ed)>0:\n",
    "                cntr+=1\n",
    "                elemEncWishes[e['id']].append(cntr)\n",
    "                elemEncWishGames[cntr] = eg\n",
    "                elemEncWishDays[cntr] = ed\n",
    "\n",
    "\n",
    "# print (elemEncWishGames)\n",
    "# print (elemEncWishDays)\n",
    "\n",
    "\n",
    "encRelRoundsMin  = { el :[] for enc in encwishes for el in elemEncWishes[enc['id']]}\n",
    "encRelRoundsMax  = { el :[] for enc in encwishes for el in elemEncWishes[enc['id']]}\n",
    "\n",
    "for enc in encwishes:\n",
    "    # print (e)\n",
    "    # print (\"ENC !! \"  , enc['reason'] )\n",
    "    for el in elemEncWishes[enc['id']]:\n",
    "        relDaysSet = set(elemEncWishDays[el])\n",
    "        for r in basicRounds :\n",
    "            if len(relDaysSet.intersection(set(getBasicDays[r])))>0:\n",
    "                frac = len(relDaysSet.intersection(set(getBasicDays[r]))) / len (getBasicDays[r])\n",
    "                # print (len(relDaysSet.intersection(set(getBasicDays[r]))) , len (getBasicDays[r]) , frac)\n",
    "                if frac>0:\n",
    "                    encRelRoundsMin[el].append(r)\n",
    "                if frac==1:\n",
    "                    encRelRoundsMax[el].append(r)\n",
    "\n",
    "nElemEncWishes = sum ([len(elemEncWishes[enc['id']]) for enc in encwishes]) \n",
    "\n",
    "# print (encRelRoundsMin)\n",
    "\n",
    "elemHaWishes ={e['id'] : [] for e in hawishes}\n",
    "elemHaWishTeams={}\n",
    "elemHaWishDays ={}\n",
    "elemHaWishFirstDay ={}\n",
    "\n",
    "cntr =1\n",
    "for e in hawishes:\n",
    "    elemTeams= [ hawTeams[e['id']]]\n",
    "    if e['forEachTeam']:\n",
    "        elemTeams= [ [t] for t in hawTeams[e['id']]]\n",
    "\n",
    "    elemDays= [ hawDays[e['id']]]\n",
    "    if e['forEachDay'] or e['forOneDay'] :\n",
    "        elemDays= [ [d] for d in hawDays[e['id']]]\n",
    "\n",
    "    elemHaWishes[e['id']]=[]\n",
    "    allElemDays=[]\n",
    "    thisDaySet=[]\n",
    "    lastDaySet = []\n",
    "    for d in elemDays :\n",
    "        # print (e)\n",
    "        if (e['forEachDay'] or e['forOneDay']) and e['timeframe']!=1:\n",
    "            thisDaySet=[]\n",
    "            # day1=  parse(getDayById[d[0]]['day'])\n",
    "            day1= getDateTimeDay[d[0]]\n",
    "            if  e['timeframe']>1:\n",
    "                day2=day1 + datetime.timedelta(days=e['timeframe']-1)\n",
    "                for d3 in days:\n",
    "                    # dt = parse(getDayById[d3]['day'])\n",
    "                    dt= getDateTimeDay[d3]\n",
    "                    if day1<=dt and dt<=day2 :\n",
    "                        thisDaySet.append(d3)\n",
    "            else:\n",
    "                r1 = getDayById[d[0]]['round']\n",
    "                for d3 in days:\n",
    "                    dt= getDateTimeDay[d3]\n",
    "                    # dt = parse(getDayById[d3]['day'])\n",
    "                    if day1<=dt and getRoundByDay[d3]< r1 + (-e['timeframe']) :\n",
    "                        thisDaySet.append(d3)\n",
    "                print (\" ROUND HA WISH \", e, thisDaySet, e['timeframe'])\n",
    "        else:\n",
    "            thisDaySet=d\n",
    "\n",
    "        # only create wish id new day set is superset\n",
    "        if len([d for  d in thisDaySet if d not in lastDaySet])>0:\n",
    "            for t in elemTeams:\n",
    "                cntr+=1\n",
    "                elemHaWishes[e['id']].append(cntr)\n",
    "                elemHaWishTeams[cntr]=t\n",
    "                elemHaWishDays[cntr]=thisDaySet.copy()\n",
    "                elemHaWishFirstDay[cntr]=d[0]\n",
    "            lastDaySet = thisDaySet.copy()\n",
    "            allElemDays+= thisDaySet  \n",
    "\n",
    "    hawRounds[e['id']]=[]\n",
    "    for d3 in set(allElemDays):\n",
    "        hawRounds[e['id']]+=getRoundsByDay[d3]\n",
    "    hawRounds[e['id']]=sorted(list(set(hawRounds[e['id']])))\n",
    "    hawRoundsString[e['id']]= \"\"\n",
    "    for r in hawRounds[e['id']]:\n",
    "        hawRoundsString[e['id']]+= str(r)+\"_\"\n",
    "    if hawRoundsString[e['id']]!=\"\":\n",
    "        hawRoundsString[e['id']]=hawRoundsString[e['id']][:-1]\n",
    "        \n",
    "nElemHaWishes = sum ([len(elemHaWishes[enc['id']]) for enc in hawishes]) \n",
    "\n",
    "gameAttractivity = { (t1,t2) : attractivity[t1,t2] for (t1,t2) in games if t1<t2 }\n",
    "\n",
    "\n",
    "sorted_gameAttractivity = sorted(gameAttractivity.items(), key=operator.itemgetter(1))\n",
    "\n",
    "nGames= len(games)\n",
    "topGames = [ sorted_gameAttractivity[i] for i in range(int(0.8*0.5*nGames), min( len(sorted_gameAttractivity) , int(0.5*nGames)) ) ]\n",
    "goodGames = [ sorted_gameAttractivity[i] for i in range(int(0.6*0.5*nGames), min( len(sorted_gameAttractivity) , int(0.5*nGames))) ]\n",
    "\n",
    "broadcastingwishes = BroadcastingWish.objects.filter(scenario=s2)\n",
    "\n",
    "\n",
    "nonBlocked = { (t,r) : len(getBasicDays[r]) for r in basicRounds for t in teams}\n",
    "travelDays = { (t,r) : len(getBasicDays[r]) for r in basicRounds for t in teams}\n",
    "hideDays = { (t,r) : len(getBasicDays[r]) for r in basicRounds for t in teams}\n",
    "nonBlockedRounds = { (t,r) : len(getDays[r]) for r in rounds for t in teams}\n",
    "travelRounds = { (t,r) : len(getDays[r]) for r in rounds for t in teams}\n",
    "hideRounds = { (t,r) : len(getDays[r]) for r in rounds for t in teams}\n",
    "\n",
    "for bl in blockings:\n",
    "    bl_val =1.0 if bl['time']==\"----\" else 0.5\n",
    "    # if  bl['time']!=\"----\":\n",
    "    # print (bl, bl_val , getDayById[bl['day_id']] ,getDayById[bl['day_id']]['round'] ,getDayById[bl['day_id']]['round'] !=0 )\n",
    "    if getDayById[bl['day_id']]['round'] !=0:\n",
    "        if bl['type'] in [\"Home\",\"Hide\"]:\n",
    "            nonBlocked[( bl['team_id'], getBasicRound[getDayById[bl['day_id']]['round']])]-=bl_val\n",
    "            nonBlockedRounds[( bl['team_id'], getDayById[bl['day_id']]['round'])]-=bl_val\n",
    "            # print ('FOUND HOME BLOCKING ', bl)\n",
    "        if bl['type'] in [\"Away\",\"Hide\"]:\n",
    "            travelDays[( bl['team_id'], getBasicRound[getDayById[bl['day_id']]['round']])]-=bl_val\n",
    "            travelRounds[( bl['team_id'], getDayById[bl['day_id']]['round'])]-=bl_val\n",
    "            # print ('FOUND AWAY BLOCKING ', bl)\n",
    "        if bl['type'] in [\"Hide\"]:\n",
    "            hideDays[( bl['team_id'], getBasicRound[getDayById[bl['day_id']]['round']])]-=bl_val\n",
    "            hideRounds[( bl['team_id'], getDayById[bl['day_id']]['round'])]-=bl_val\n",
    "            # print ('FOUND AWAY BLOCKING ', bl)\n",
    "\n",
    "noPlayRounds = { t:  [ r for r in rounds if nonBlockedRounds[(t,r)] + travelRounds[(t,r)]==0 ] for t in teams }\n",
    "noHomeRounds = { t:  [ r for r in rounds if nonBlockedRounds[(t,r)] ==0 ] for t in teams }\n",
    "# print(\"nonBlockedRounds\" ,nonBlockedRounds)\n",
    "# print(\"noHomeRounds\" ,noHomeRounds)\n",
    "# print(\"noPlayRounds\" ,noPlayRounds)\n",
    "\n",
    "playRounds = { t:  sorted([ r for r in rounds if  hideRounds[(t,r)]>0 ]) for t in teams }\n",
    "\n",
    "\n",
    "# scale blocking of basic round to [0,1]\n",
    "for t in teams: \n",
    "    for r in basicRounds:\n",
    "        if len(getBasicDays[r])>0:\n",
    "            nonBlocked[(t,r)]/=len(getBasicDays[r])\n",
    "            travelDays[(t,r)]/=len(getBasicDays[r])\n",
    "            if getTeamById[t]==\"AX Armani Exchange Milan\":\n",
    "                print (r , getRealRounds[r] , nonBlocked[(t,r)] , nonBlocked[(t,r)] )\n",
    "\n",
    "for t in teams:\n",
    "    if mathModelName==\"UEFA NL\" and noPlayRounds[t] in [ [3,4] ]:\n",
    "        t_usePhases[t]= False\n",
    "        print (\"No need for phases  \" , getTeamById[t])\n",
    "        for t2 in opponents[t]:\n",
    "            t_usePhases[t2]= False\n",
    "            print (\" -- also no need for phases  \" , getTeamById[t2])\n",
    "    if getTeamById[t]==\"AX Armani Exchange Milan\":\n",
    "        t_usePhases[t]= False\n",
    "        print (\"setting t_usePhases of \", getTeamById[t],t_usePhases[t] )\n",
    "\n",
    "runPatternAssignmentFirst=False\n",
    "\n",
    "\n",
    "chosenGames = [ ]\n",
    "\n",
    "useFullModel1= False\n",
    "\n",
    "usePatterns= not mathModelName in [\"NHL\", \"LNR\"]\n",
    "usePatterns= not mathModelName in [\"NHL\"]\n",
    "\n",
    "\n",
    "balanceBreaks =  mathModelName==\"LNR\" \n",
    "haSymmetric = not mathModelName in [\"NHL\" , \"LNR\"]\n",
    "haSymmetric = not mathModelName in [\"NHL\", \"Ligue 1\", \"Costa Rica Premier League\"]\n",
    "haSymmetric = not mathModelName in [\"NHL\"]\n",
    "# haSymmetric = not mathModelName in [\"NHL\" , \"LNR\"]\n",
    "if thisSeason.symmetry:\n",
    "    haSymmetric=True\n",
    "\n",
    "use2BreakPatterns = False\n",
    "\n",
    "if thisSeason.initBreaks>=2  :\n",
    "    use2BreakPatterns = True\n",
    "\n",
    "half_symmetry_offset = 0 if thisSeason.symmetry or use2BreakPatterns or not thisSeason.startWithBreakAllowed or not thisSeason.endWithBreakAllowed  else 1\n",
    "\n",
    "\n",
    "prev_mirror_round= { r :  0 if r<=nRounds1 else r-nRounds1+half_symmetry_offset for r in basicRounds }\n",
    "\n",
    "\n",
    "for r in basicRounds:\n",
    "    if r>nRounds1 and int((r-1)/nRounds1) == int((prev_mirror_round[r]-1)/nRounds1):\n",
    "        prev_mirror_round[r] = int((r-1)/nRounds1-1)*nRounds1 +half_symmetry_offset+1-prev_mirror_round[r]%nRounds1\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "getPhaseOfBasicRound={ br : getPhaseOfRound[getRealRounds[br][0]] for br in basicRounds}\n",
    "getBasicRoundsOfPhase={ ph : [ br for br in basicRounds if getPhaseOfBasicRound[br]==ph ] for ph in phases }\n",
    "\n",
    "if use2BreakPatterns or nTeams >200 or not usePatterns or thisSeason.minRoundsBetweenGameOfSameTeams>0 :\n",
    "    useFullModel1=True\n",
    "useFullModel1=True  \n",
    "\n",
    "\n",
    "preplan_phases = phases\n",
    "preplan_phases= [0]\n",
    "if not haSymmetric:\n",
    "    preplan_phases = phases\n",
    "\n",
    "fixedGamesRounds=[]\n",
    "\n",
    "starweight={t : 0.0 for t in teams}\n",
    "for t1 in teams :\n",
    "    for t2 in teams :\n",
    "        starweight[t1]+=distance[getTeamById[t1],getTeamById[t2]]\n",
    "\n",
    "available_days={ (t,d) : 1 for t in teams for d in days}\n",
    "\n",
    "\n",
    "for bl in blockings:\n",
    "    if bl['type'] in [\"Home\",\"Hide\"] and bl['time']=='----':\n",
    "        available_days[ bl['team_id'],bl['day_id']] =0\n",
    "\n",
    "t_blocked_at={ (t,r) : sum([available_days[t,d] for d in getDays[r]])==0 for t in teams for r in rounds}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "a11f1f0a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  .... got rid of  3006  +  0   vars\n"
     ]
    }
   ],
   "source": [
    "\n",
    "model2 = pulp.LpProblem(\"League_Scheduling_Model_--_Schedule_Games_\"+str(thisScenario.id), pulp.LpMinimize)\n",
    "\n",
    "\n",
    "nextDay = { d : -1 for d in days  }\n",
    "previousDay = { d : -1 for d in days}\n",
    "\n",
    "# cntr=0\n",
    "lastDay=parse(\"01.01.1990\")\n",
    "for thisDay in sorted(getDayByDateTime.keys()):\n",
    "    if thisDay-lastDay==datetime.timedelta(days=1):\n",
    "        # cntr+=1\n",
    "        d1 = getDayByDateTime[lastDay]\n",
    "        d2 = getDayByDateTime[thisDay]\n",
    "        nextDay[d1]=d2\n",
    "        previousDay[d2]=d1\n",
    "        # print (\"next day of \" ,  getNiceDay[d1] , ' ' , getNiceDay[d2])\n",
    "    lastDay=thisDay\n",
    "\n",
    "# print (cntr)\n",
    "# cntr=0\n",
    "# for d in days+higherLeagueDayIds :\n",
    "#     for d2 in days+higherLeagueDayIds:\n",
    "#         if  getDateTimeDay[d2]-getDateTimeDay[d]==datetime.timedelta(days=1):\n",
    "#             cntr+=1\n",
    "#             nextDay[d]=d2\n",
    "#             previousDay[d2]=d\n",
    "#             print (\"next day of \" ,  getNiceDay[d] , ' ' , getNiceDay[d2])\n",
    "# print (cntr)\n",
    "\n",
    "\n",
    "cntr= 0\n",
    "cntr2= 0\n",
    "\n",
    "# print (\"games\",games)\n",
    "# print (roundDays)\n",
    "\n",
    "seedTV = []\n",
    "dontPlay = []\n",
    "dontPlayGames = []\n",
    "\n",
    "for bl in blockings:\n",
    "    if bl['type']==\"Hide\":\n",
    "        dontPlay += [(bl['team_id'],bl['day_id']) ]\n",
    "\n",
    "dontPlay += [(t,d)  for d in days for t in teams  if getDayMaxGames[d]==0 ] \n",
    "\n",
    "# for t1 in teams: \n",
    "#     for d in days:\n",
    "#         if blocked_arena[(t1,d, \"----\" )] :\n",
    "#             dontPlayGames += [(t1,t2,d) for t2 in teams]\n",
    "\n",
    "\n",
    "# print()\n",
    "# print(len(dontPlayGames) , \" \", len (days)*len(teams)*(len(teams)-1))\n",
    "\n",
    "\n",
    "if mathModelName==\"NBA\":\n",
    "    for t1 in teams: \n",
    "        for d in days:\n",
    "            if blocked_arena[(t1,d, \"----\" )] :\n",
    "                dontPlayGames += [(t1,t2,d) for t2 in teams]\n",
    "    # print (len(dontPlayGames), \"BLOKING \" )\n",
    "\n",
    "    for enc in encwishes:\n",
    "        # print (\"ENC \" ,enc , enc['reason'][:11],enc['reason'][:11]== 'TV Network:')    \n",
    "        if enc['reason'][:11]== 'TV Network:':                \n",
    "            print (\"ENC \" ,  encGames[enc['id']] ,encDays[enc['id']] )\n",
    "            seedTV += [ (t1,t2,d,enc['reason'][12:]) for (t1,t2) in encGames[enc['id']] for d in encDays[enc['id']]]\n",
    "        if enc['maxGames']== 0 and not enc['time'] in [getIdByTime[\"Early\"], getIdByTime[\"Late\"]]:\n",
    "            dontPlayGames += [ (t1,t2,d) for (t1,t2) in encGames[enc['id']] for d in encDays[enc['id']]]\n",
    "\n",
    "    #         print (len(dontPlayGames),encGames[enc['id']] , encDays[enc['id']] , enc['reason'] )\n",
    "    \n",
    "\n",
    "    usingRDs={t: [] for t in teams}\n",
    "    for (t1,t2,d,channel) in  seedTV:\n",
    "        # print (t1,t2,d , gameCntr[(t1,t2)], gameCntr[(t1,t2)]==1)\n",
    "        usingRDs[t1]+=getRoundDaysByDay[d]\n",
    "        usingRDs[t2]+=getRoundDaysByDay[d]\n",
    "        for d2 in [ previousDay[d], nextDay[d]]:\n",
    "            if channel==\"ABC\" and d2!=-1:\n",
    "                dontPlay+=[(t1,d2), (t2,d2)]\n",
    "\n",
    "    TVteams = [t for t in teams if len (usingRDs[t])>=10 ]\n",
    "\n",
    "#     for t in teams:          \n",
    "#         print (t, getTeamById[t], [ r for (r,d) in usingRDs[t]], t_conference[t])    \n",
    "\n",
    "#     print (TVteams)\n",
    "# print (\"CHECKPOINT \", 5)\n",
    "\n",
    "# print (seedTV)\n",
    "# print (dontPlay)\n",
    "\n",
    "x= {(t1,t2,rd) : 0 for t1 in teams for t2 in teams for rd in roundDays}\n",
    "for (t1,t2) in games:\n",
    "    for rd in roundDays:\n",
    "        x[(t1,t2,rd)]=1\n",
    "\n",
    "for (t1,t2,rd) in x.keys():\n",
    "    if blocked_arena[(t1,rd[1],\"----\")] and runMode=='Improve' and not thisSeason.allowBlockingViosInImprove:\n",
    "        # cntr +=1\n",
    "        # print (\"FORBIDDING\")\n",
    "        \n",
    "        x[(t1,t2,rd)]=0\n",
    "\n",
    "for (t1,t2,d,channel) in seedTV:\n",
    "    if gameCntr[(t1,t2)]==1:\n",
    "        for (r,d2) in roundDays:\n",
    "            if d != d2 :\n",
    "                # cntr +=1\n",
    "                x[(t1,t2,(r,d2))]=0\n",
    "\n",
    "    for t3 in teams:                \n",
    "        if not t3 in [t1,t2]:\n",
    "            for rd in getRoundDaysByDay[d]:\n",
    "                x[(t1,t3,rd)]=0\n",
    "                x[(t2,t3,rd)]=0\n",
    "                x[(t3,t1,rd)]=0\n",
    "                x[(t3,t2,rd)]=0\n",
    "                # cntr +=4\n",
    "\n",
    "for (t,d) in dontPlay:\n",
    "    for t3 in teams:\n",
    "        if not t3 in [t1,t2]:\n",
    "            for rd in getRoundDaysByDay[d]:\n",
    "                x[(t,t3,rd)]=0\n",
    "                x[(t3,t,rd)]=0\n",
    "\n",
    "for (t1,t2,d) in dontPlayGames:\n",
    "    for rd in getRoundDaysByDay[d]:\n",
    "        x[(t1,t2,rd)]=0\n",
    "\n",
    "attendance = { (t1,t2,d) : 0 for (t1,t2) in games for d in days }\n",
    "if thisSeason.useFeaturePrediction:\n",
    "    # learner = AttendanceLearner(thisSeason.id)\n",
    "    # attendance = learner.predict_games(attendance.keys())\n",
    "    oldestTrainingGame= parse(\"2015-01-01\")\n",
    "    oldestTrainingGame=oldestTrainingGame.date()\n",
    "    games_train = Game.objects.filter(season=thisSeason).exclude(historic_season=None)\n",
    "    print ( \" all games\",len(games_train), )\n",
    "    games_train = [g for g in games_train if g.date>oldestTrainingGame ]\n",
    "    print ( \" important games\",len(games_train), )\n",
    "\n",
    "    games_predict = attendance.keys()\n",
    "\n",
    "    X= []\n",
    "    y =[]\n",
    "    homeAttendances = {t: [] for t in teams+inactive_teams}\n",
    "    for game in games_train:\n",
    "        if game.attendance>0:\n",
    "            homeAttendances[game.homeTeam.id].append(game.attendance)\n",
    "    for t in teams+inactive_teams:\n",
    "        homeAttendances[t]=sorted(homeAttendances[t])\n",
    "        # print (getTeamById[t], homeAttendances[t])\n",
    "        if len(homeAttendances[t])==0:\n",
    "            homeAttendances[t]=[7500]\n",
    "    maxAttendance = {t: homeAttendances[t][-1] for t in teams+inactive_teams }\n",
    "    # maxAttendance = {t: homeAttendances[t][int(0.9*(len(homeAttendances[t])))] for t in teams+inactive_teams }\n",
    "    medianAttendance = {t: homeAttendances[t][int(0.5*(len(homeAttendances[t])))] for t in teams+inactive_teams }\n",
    "    loyalty = {t: medianAttendance[t]/maxAttendance[t] for t in teams+inactive_teams }\n",
    "\n",
    "    for t in teams+inactive_teams: \n",
    "        print (getTeamById[t], loyalty[t] , medianAttendance[t] , maxAttendance[t] , )\n",
    "\n",
    "# x= {(t1,t2,rd) : 0 for t1 in teams for t2 in teams for rd in roundDays}\n",
    "x_round= {(t1,t2,r) : 0 for t1 in teams for t2 in teams for r in rounds}\n",
    "x_time= {(t1,t2,rd,tm) : 0 for t1 in teams for t2 in teams for rd in roundDays for tm in times}\n",
    "\n",
    "\n",
    "for (t1,t2) in games:\n",
    "    for rd in roundDays:\n",
    "        # print (\"make var \" ,t1,t2,rd)\n",
    "        if x[(t1,t2,rd)]==1 :\n",
    "        # if not blocked_arena[(t1,rd[1],\"----\")] or runMode=='New':\n",
    "            if not evalRun:\n",
    "                x[(t1,t2,rd)] =  pulp.LpVariable('x_'+str(t1)+'_'+str(t2)+'_'+str(rd[0])+'_'+str(rd[1]), lowBound = 0, upBound = 1, cat = pulp.LpContinuous) \n",
    "                cntr +=1\n",
    "                if thisSeason.useFeatureKickOffTime:\n",
    "                    for tm in times:\n",
    "                        x_time[(t1,t2,rd,tm)] = pulp.LpVariable('x_'+str(t1)+'_'+str(t2)+'_'+str(rd[0])+'_'+str(rd[1])+'_'+tm , lowBound = 0, upBound = 1, cat = pulp.LpContinuous) \n",
    "            else: \n",
    "                cntr+=len(roundDays)\n",
    "\n",
    "    for r in rounds:\n",
    "        x_round[(t1,t2,r)]= pulp.LpVariable('x_round_'+str(t1)+'_'+str(t2)+'_'+str(r), lowBound = 0, upBound = 1, cat = pulp.LpContinuous)\n",
    "        model2+= x_round[(t1,t2,r)] == sum([x[(t1,t2,rd)] for rd in getRoundDaysByRound[r]])\n",
    "\n",
    "\n",
    "t_prev_mirror_round ={(t,r) : 0 for t in teams for r in rounds}\n",
    "\n",
    "for t1 in teams: \n",
    "    phaseLength= int(len(playRounds[t1])/nPhases+0.5)\n",
    "    # print (\"phaseLength\", phaseLength)\n",
    "    for i in range(len(playRounds[t1])):\n",
    "        # print (i, phaseLength)\n",
    "        if i >=phaseLength:\n",
    "            # print (\"setting \" , playRounds[t1], playRounds[t1][i-phaseLength])\n",
    "            # print (\"setting \" , playRounds[t1][i], \"->\",playRounds[t1][i-phaseLength])\n",
    "            t_prev_mirror_round[(t1,playRounds[t1][i])]=playRounds[t1][i-phaseLength]\n",
    "            # t_prev_mirror_round[(t,playRounds[t1][i])]=5\n",
    "\n",
    "\n",
    "# for tr in t_prev_mirror_round.keys():\n",
    "#     print(  \"t_prev_mirror_round \"  ,tr ,  t_prev_mirror_round[tr])\n",
    "\n",
    "# print (prev_mirror_round)\n",
    "\n",
    "\n",
    "if not evalRun:\n",
    "    for (t1,t2) in games:\n",
    "        for r in rounds:\n",
    "            if r > nRounds1 and thisSeason.symmetry : \n",
    "                prev_round =prev_mirror_round[r]       \n",
    "                if thisSeason.groupBased and len(noPlayRounds[t1])>0 and noPlayRounds[t1]==noPlayRounds[t2]:\n",
    "                    prev_round=t_prev_mirror_round[t1,r]\n",
    "\n",
    "                if prev_round>0:\n",
    "                    model2+= x_round[(t1,t2,r)] == x_round[(t2,t1,prev_round)]\n",
    "                # print (\"x_round[(\",t1,\",\",t2,\",\",r,\")] == x_round[(\",t2,t1,prev_mirror_round[r],\")]\")\n",
    "                # print ( t1,t2,r , \" -> \",prev_mirror_round[r])\n",
    "\n",
    "\n",
    "    \n",
    "\n",
    "for (t1,t2,(r,d)) in x.keys():\n",
    "    if thisSeason.useFeatureKickOffTime:\n",
    "        for tm in times :\n",
    "            if blocked_arena[(t1,d,tm)] and runMode=='Improve':\n",
    "                x_time[(t1,t2,(r,d), tm)]=0\n",
    "                # print (\"forbidding tm \", t1,t2,r,d, tm)\n",
    "                cntr2+=1\n",
    "                # print (\"FORBIDDING\")\n",
    "                \n",
    "\n",
    "print (\"  .... got rid of \" , len(x.keys())-cntr, \" + \" ,cntr2, \"  vars\") \n",
    "\n",
    "\n",
    "\n",
    "\n",
    "for (t1,t2,d,channel) in  seedTV:\n",
    "    print (t1,t2,d , gameCntr[(t1,t2)], gameCntr[(t1,t2)]==1)\n",
    "    model2+= sum([x[(t1,t2,rd)]  for rd in getRoundDaysByDay[d] ])==1\n",
    "\n",
    "#    model2+= lpSum( x[rr] for rr in x.keys() )\n",
    "#    model2.solve(XPRESS(msg=1,maxSeconds = 25, keepFiles=True))\n",
    "#    return \"\"\n",
    "\n",
    "homeInRound= {(t1,r) : pulp.LpVariable('homeInRound_'+str(t1)+'_'+str(r), lowBound = 0, upBound = 1, cat = pulp.LpContinuous) for t1 in teams for r in rounds}\n",
    "awayInRound= {(t1,r) : pulp.LpVariable('awayInRound_'+str(t1)+'_'+str(r), lowBound = 0, upBound = 1, cat = pulp.LpContinuous) for t1 in teams for r in rounds}\n",
    "gameInBasicRound= {(t1,t2,r) : pulp.LpVariable('gameInBasicRound_'+str(t1)+'_'+str(t2)+'_'+str(r), lowBound = 0, upBound = defaultGameRepetions, cat = pulp.LpContinuous) for (t1,t2) in games for r in basicRounds}\n",
    "homeInBasicRound= {(t1,r) : pulp.LpVariable('homeInBasicRound_'+str(t1)+'_'+str(r), lowBound = 0,  cat = pulp.LpContinuous) for t1 in teams for r in basicRounds}\n",
    "awayInBasicRound= {(t1,r) : pulp.LpVariable('awayInBasicRound_'+str(t1)+'_'+str(r), lowBound = 0,  cat = pulp.LpContinuous) for t1 in teams for r in basicRounds}\n",
    "break3InRound= {(t1,r) : pulp.LpVariable('break3InRound_'+str(t1)+'_'+str(r), lowBound = 0, cat = pulp.LpContinuous) for t1 in teams for r in rounds}\n",
    "# breakInRound= {(t1,r) : pulp.LpVariable('breakInRound_'+str(t1)+'_'+str(r), lowBound = 0, upBound = 1, cat = pulp.LpContinuous) for t1 in teams for r in rounds}\n",
    "# breaksTotal =  pulp.LpVariable('breaksTotal', lowBound = 0, cat = pulp.LpContinuous)\n",
    "tooManyTop4InRow= {(t1,r) : pulp.LpVariable('tooManyTop4InRow_'+str(t1)+'_'+str(r), lowBound = 0, upBound = 1, cat = pulp.LpContinuous) for t1 in teams for r in rounds}\n",
    "\n",
    "\n",
    "tooManyHomesInStadium= {(stadium,d) : pulp.LpVariable('tooManyHomesInStadium_'+str(stadium)+'_'+str(d), lowBound = 0, cat = pulp.LpContinuous) for stadium in stadiums for d in days}\n",
    "pairingVio= {(pair['id'],d) : pulp.LpVariable('pairingVio_'+str(pair['id'])+'_'+str(d), lowBound = 0, cat = pulp.LpContinuous) for pair in pairings for d in days}\n",
    "derbyMissing= {d : pulp.LpVariable('derbyMissing_'+str(d), lowBound = 0, cat = pulp.LpContinuous) for d in days}\n",
    "\n",
    "breakVio =  { (br['id'],t) : pulp.LpVariable('breakVio_'+ str(br['id'])+'_'+str(t) , lowBound = 0, cat = pulp.LpContinuous) for br in breaks for t in teams}\n",
    "blockingVio =  { bl['id'] : pulp.LpVariable('blockingVio_'+ str(bl['id']) , lowBound = 0, cat = pulp.LpContinuous) for bl in blockings}\n",
    "# blockingVioTotal =  pulp.LpVariable('blockingVioTotal', lowBound = 0, cat = pulp.LpContinuous)\n",
    "\n",
    "hawVio =  { haw['id'] : pulp.LpVariable('hawVio'+ str(haw['id']) , lowBound = 0, cat = pulp.LpContinuous) for haw in hawishes}\n",
    "HawVioTooLess =  { el : pulp.LpVariable('havviotooless_'+ str(el) , lowBound = 0, cat = pulp.LpContinuous) for haw in hawishes for el in elemHaWishes[haw['id']] }\n",
    "HawVioTooMuch =  { el : pulp.LpVariable('havviotoomuch_'+ str(el) , lowBound = 0, cat = pulp.LpContinuous) for haw in hawishes for el in elemHaWishes[haw['id']] }\n",
    "# HawVioTotal =  pulp.LpVariable('HawVioTotal', lowBound = 0, cat = pulp.LpContinuous)\n",
    "encVio =  { enc['id'] : pulp.LpVariable('encVio'+ str(enc['id']) , lowBound = 0, cat = pulp.LpContinuous) for enc in encwishes}\n",
    "encVioTooLess =  { el : pulp.LpVariable('encViotooless'+ str(enc['id'])+\"_\"+str(el) , lowBound = 0, cat = pulp.LpContinuous) for enc in encwishes for el in elemEncWishes[enc['id']]}\n",
    "encVioTooMuch =  { el : pulp.LpVariable('encViotoomuch'+ str(enc['id'])+\"_\"+str(el) , lowBound = 0, cat = pulp.LpContinuous) for enc in encwishes for el in elemEncWishes[enc['id']]}\n",
    "# encVioTotal =  pulp.LpVariable('encVioTotal', lowBound = 0, cat = pulp.LpContinuous)\n",
    "# broadVio =  { d : pulp.LpVariable('broadVio'+ str(d) , lowBound = 0, cat = pulp.LpContinuous) for d in days}\n",
    "broadVioTm =  { (b.id, r): pulp.LpVariable('broadVioTm_'+ str(b.id) +\"_\"+ str(r) , lowBound = 0, cat = pulp.LpContinuous) for b in broadcastingwishes for r in rounds}\n",
    "gamesTooClose2 =  { (t,r) : pulp.LpVariable('gamesTooClose2_'+ str(t) +'_'+ str(r) , lowBound = 0, cat = pulp.LpContinuous) for t in teams for r in rounds }\n",
    "missingGamesVio =  { (t1,t2)  : pulp.LpVariable('missingGamesVio_'+ str(t1) +'_'+str(t2) , lowBound = 0, cat = pulp.LpContinuous) for (t1,t2) in games}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "7bc64f33",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rounds  range(1, 35) 34\n",
      "realteams  [10, 3, 17, 14, 15, 13, 12, 6, 9, 5, 8, 18, 19, 20, 11, 7, 4, 16]\n",
      "taking care of right numbers of games ... \n",
      "0 17 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [4, 13, 16] ratiopharm ulm\n",
      "1 17 3 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [19, 30, 34] ratiopharm ulm\n",
      "0 17 6 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [1, 5, 9, 10, 12, 16] Basketball Löwen Braunschweig\n",
      "1 17 9 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [18, 19, 20, 22, 23, 26, 29, 30, 33] Basketball Löwen Braunschweig\n",
      "0 17 2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [7, 14] MHP RIESEN Ludwigsburg\n",
      "1 17 6 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [18, 21, 23, 25, 28, 29] MHP RIESEN Ludwigsburg\n",
      "0 17 4 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [2, 9, 11, 12] BG Göttingen\n",
      "1 17 5 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [19, 21, 29, 31, 33] BG Göttingen\n",
      "0 17 0 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [] SYNTAINICS MBC\n",
      "1 17 4 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [19, 21, 29, 31] SYNTAINICS MBC\n",
      "0 17 6 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [1, 4, 9, 12, 13, 16] FRAPORT SKYLINERS\n",
      "1 17 8 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [19, 21, 23, 25, 27, 29, 31, 33] FRAPORT SKYLINERS\n",
      "0 17 5 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [2, 4, 11, 13, 16] FC Bayern München\n",
      "1 17 5 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [20, 23, 27, 32, 34] FC Bayern München\n",
      "0 17 2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [4, 17] Brose Bamberg\n",
      "1 17 1 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [23] Brose Bamberg\n",
      "0 17 3 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [1, 5, 6] ALBA BERLIN\n",
      "1 17 5 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [18, 19, 21, 31, 33] ALBA BERLIN\n",
      "0 17 6 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [2, 3, 5, 10, 15, 17] Hamburg Towers\n",
      "1 17 4 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [19, 21, 24, 30] Hamburg Towers\n",
      "0 17 6 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [5, 6, 11, 12, 16, 17] EWE Baskets Oldenburg\n",
      "1 17 5 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [19, 20, 22, 25, 28] EWE Baskets Oldenburg\n",
      "0 17 6 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [2, 4, 9, 13, 15, 17] HAKRO Merlins Crailsheim\n",
      "1 17 3 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [23, 29, 31] HAKRO Merlins Crailsheim\n",
      "0 17 7 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [2, 5, 6, 9, 10, 13, 15] medi bayreuth\n",
      "1 17 2 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [24, 27] medi bayreuth\n",
      "0 17 4 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [6, 9, 15, 17] NINERS Chemnitz\n",
      "1 17 7 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [18, 19, 23, 24, 31, 33, 34] NINERS Chemnitz\n",
      "0 17 0 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [] MLP Academics Heidelberg\n",
      "1 17 2 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [21, 29] MLP Academics Heidelberg\n",
      "0 17 2 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [11, 12] JobStairs Gießen 46ers\n",
      "1 17 4 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [18, 21, 31, 33] JobStairs Gießen 46ers\n",
      "0 17 4 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [1, 4, 7, 10] Telekom Baskets Bonn\n",
      "1 17 4 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [19, 20, 22, 25] Telekom Baskets Bonn\n",
      "0 17 5 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] [6, 14, 15, 16, 17] s.Oliver Würzburg\n",
      "1 17 3 [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] [23, 25, 34] s.Oliver Würzburg\n",
      "check 1\n",
      "check 2\n",
      "- mindestens ein Heimspiel in den letzten zwei Wochen vor Weihnachten\n",
      "ALBA hat nur Option\n",
      "ALBA sollte nicht am Freitag Auswärts spielen\n",
      "ALBA sollte nicht am Freitag Heim spielen\n",
      "ALBA sollte nicht am Samstag Auswärts  spielen\n",
      "ALBA sollte nicht am Samstag Heim spielen\n",
      "BBL 21 unbedingt Heimspiel am 14. 02.\n",
      "BBL10 hätten wir gerne ein Heimspiel, da an BBL9, BBL11 + BBL12 die Arena leider nicht zur Verfügung steht.\n",
      "Bitte Heimspiele aufgrund möglicher Verlegung durch Kollision mit EL-Spielen\n",
      "Christmas Day on Dec 11, 2022 at 15:00\n",
      "Da wir die reguläre Saison 21/22 mit drei Auswärtsspielen beenden, bitte ich um ein Heimspiel bei BBL 34 (BBL 33 ist nicht verfügbar).\n",
      "Easter Special on April 08,2023 at 20: 30\n",
      "Gerne Heimspiele an den Spieltagen 11, 12 und 13, da wir dort die Halle stehen lassen können.\n",
      "In February 2023  (National Team Window) we need at least one home game, otherwise team is absent for interactions with fans for too long.\n",
      "In November 2022 (National Team Window) we need at least one home game, otherwise team is absent for interactions with fans for too long.\n",
      "gerne ein Heimspiel \"zwischen den Jahren\"\n",
      "check 3\n",
      "{'id': 12, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': True, 'forEachTeam2': False, 'day_id': 100, 'day2_id': 198, 'multidate': False, 'timeframe': -7, 'forEachDay': True, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': -1, 'maxGames': 1, 'prio': 'B', 'reason': 'die Heimspiele gegen Bayern und Alba zeitlich gerne so weit wie möglich auseinander\\r\\nx', 'violation': 'Fri, Mar 10, 2023 <br>HAKRO Merlins Crailsheim<br>  1 too many<br><br>Fri, Mar 17, 2023 <br>HAKRO Merlins Crailsheim<br>  1 too many<br><br>Fri, Mar 24, 2023 <br>HAKRO Merlins Crailsheim<br>  1 too many<br><br>Fri, Mar 31, 2023 <br>HAKRO Merlins Crailsheim<br>  1 too many<br><br>Sat, Apr 08, 2023 <br>HAKRO Merlins Crailsheim<br>  1 too many<br><br>Fri, Apr 14, 2023 <br>HAKRO Merlins Crailsheim<br>  1 too many<br><br>', 'active': True, 'scenario_id': 2, 'affected_rounds': '1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16_17_18_19_20_21_22_23_24_25_26_27_28_29_30_31_32_33_34'}\n",
      "{'id': 13, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 100, 'day2_id': None, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 1, 'maxGames': -1, 'prio': 'B', 'reason': 'Seed Game', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '1'}\n",
      "{'id': 14, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 100, 'day2_id': 109, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 1, 'maxGames': -1, 'prio': 'B', 'reason': 'das Derby gegen Ludwigsburg als erstes Heimspiel', 'violation': ' 1 too few<br><br>', 'active': True, 'scenario_id': 2, 'affected_rounds': '1_2_3'}\n",
      "{'id': 15, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 123, 'day2_id': None, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 1, 'maxGames': -1, 'prio': 'B', 'reason': 'Seed Game', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '7'}\n",
      "{'id': 16, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 135, 'day2_id': None, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 1, 'maxGames': -1, 'prio': 'B', 'reason': 'Verlegung Spieltag 10 oder 11 auf den 23.12.2022! Prio 1 ist ein Weihnachtsspiel am 23.12.2022 :-). Bitte keine europäische Mannschaft zwecks Verschiebungen.', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '11'}\n",
      "{'id': 17, 'useEncounterGroups': True, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 135, 'day2_id': 136, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 9, 'maxGames': -1, 'prio': 'B', 'reason': 'Derbyspieltag', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '11'}\n",
      "{'id': 18, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 137, 'day2_id': 138, 'multidate': True, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 4, 'maxGames': -1, 'prio': 'B', 'reason': 'Bitte Heimspiele aufgrund möglicher Verlegung durch Kollision mit EL-Spielen:BBL München 12 (29./30.12.)BBL 18 (31.01./01.02)BBL 29 (18./19.04.)BBL 31 (25./26.04.) Diese\\r\\nHeimspiele wenn möglich gegen nicht europäisch spielende Mannschaften, so dass eine Verlegung problemlos möglich ist.', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '12_18_29_31'}\n",
      "{'id': 19, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 154, 'day2_id': 157, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': True, 'weekdays': '--', 'time': '----', 'minGames': -1, 'maxGames': 0, 'prio': 'B', 'reason': 'Spieltag 18 nicht gegen München, Berlin und Giessen', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '18'}\n",
      "{'id': 20, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 166, 'day2_id': None, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 1, 'maxGames': -1, 'prio': 'B', 'reason': 'Seed Game', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '22'}\n",
      "{'id': 21, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 179, 'day2_id': None, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 1, 'maxGames': -1, 'prio': 'B', 'reason': 'Seed Game', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '27'}\n",
      "{'id': 22, 'useEncounterGroups': False, 'useGroups': False, 'forEachTeam1': False, 'forEachTeam2': False, 'day_id': 184, 'day2_id': None, 'multidate': False, 'timeframe': 1, 'forEachDay': False, 'forOneDay': False, 'forOneDayNum': 1, 'symmetry': False, 'weekdays': '--', 'time': '----', 'minGames': 1, 'maxGames': -1, 'prio': 'B', 'reason': 'Seed Game', 'violation': '', 'active': True, 'scenario_id': 2, 'affected_rounds': '28'}\n",
      "check 4\n"
     ]
    }
   ],
   "source": [
    "\n",
    "home={}\n",
    "away={}\n",
    "home_time={}\n",
    "away_time={}\n",
    "away_in_cluster={}\n",
    "away_in_cluster_day={}\n",
    "\n",
    "getRoundDaysByBasicRound={br : [rd for r in getRealRounds[br] for rd in getRoundDaysByRound[r]] for br in basicRounds}\n",
    "getMaxGameOnRoundDaysByBasicRound={br : sum ( roundDaysMax[rd]  for r in getRealRounds[br] for rd in getRoundDaysByRound[r]) for br in basicRounds}\n",
    "\n",
    "for t in realteams:\n",
    "    for d in days:\n",
    "        away[t,d] = lpSum([x[(t2,t,rd)] for t2 in opponents[t] for rd in getRoundDaysByDay[d]]) or pulp.LpVariable('away'+str(t)+'_'+str(d), lowBound = 0, upBound = 0, cat = pulp.LpContinuous)\n",
    "        home[t,d] = lpSum([x[(t,t2,rd)] for t2 in opponents[t] for rd in getRoundDaysByDay[d]]) or pulp.LpVariable('home'+str(t)+'_'+str(d), lowBound = 0, upBound = 0, cat = pulp.LpContinuous)\n",
    "        if thisSeason.useFeatureKickOffTime:\n",
    "            for t2 in opponents[t]:\n",
    "                # print (t,t2,getTeamById[t], getTeamById[t2], (t,t2) in games )\n",
    "                # if (t,t2) in games:\n",
    "                for rd in getRoundDaysByDay[d]:\n",
    "                    # TODO: HOTFIX\n",
    "                    if (x[(t2,t,rd)] != 0):     \n",
    "                        model2+= x[(t2,t,rd)] ==  lpSum([ x_time[(t2,t,rd,tm)] for tm in times])\n",
    "            for tm in times:\n",
    "                away_time[t,d,tm] = lpSum([x_time[(t2,t,rd,tm)] for t2 in opponents[t] for rd in getRoundDaysByDay[d] ])\n",
    "                home_time[t,d,tm] = lpSum([x_time[(t,t2,rd,tm)] for t2 in opponents[t] for rd in getRoundDaysByDay[d] ])\n",
    "\n",
    "for t in realteams:\n",
    "    for r in rounds:\n",
    "        # awayInRound[t,r] = lpSum([x[(t2,t,dr)] for t2 in teams for dr in getRoundDaysByRound[r]])\n",
    "        # homeInRound[t,r] = lpSum([x[(t,t2,dr)] for t2 in teams for dr in getRoundDaysByRound[r]])\n",
    "        \n",
    "        if thisSeason.gamesPerRound==\"one day\":\n",
    "            # model2 += homeInRound[(t,r)]     == lpSum([home[(t,d)] for (r2,d) in getRoundDaysByRound[r]])\n",
    "            model2 += homeInRound[(t,r)]     == lpSum([x[(t,t2,rd)] for t2 in opponents[t] for rd in getRoundDaysByRound[r]])\n",
    "            model2 += awayInRound[(t,r)]     == lpSum([x[(t2,t,rd)] for t2 in opponents[t] for rd in getRoundDaysByRound[r]])\n",
    "        else:\n",
    "            model2 += homeInRound[(t,r)] == lpSum([x[(t,t2,getRoundDaysByRound[r][0])] for t2 in opponents[t] ])\n",
    "            model2 += awayInRound[(t,r)] == lpSum([x[(t2,t,getRoundDaysByRound[r][0])] for t2 in opponents[t] ])\n",
    "\n",
    "        model2 += homeInRound[(t,r)] + awayInRound[(t,r)] <= 1\n",
    "        if r>=3:\n",
    "            model2 += break3InRound[(t,r)] +2>= homeInRound[(t,r-2)]+homeInRound[(t,r-1)]+homeInRound[(t,r)] \n",
    "            model2 += break3InRound[(t,r)] +2>= awayInRound[(t,r-2)]+awayInRound[(t,r-1)]+awayInRound[(t,r)] \n",
    "\n",
    "        if r>1:\n",
    "            if thisSeason.gamesPerRound==\"one day\":\n",
    "                model2 += lpSum([x[(t,t4,d)]+x[(t4,t,d)] for t4 in top4 for d in (getRoundDaysByRound[r-1]+getRoundDaysByRound[r]) if t4 in opponents[t] ]) <= 1+tooManyTop4InRow[(t,r)]\n",
    "            else:\n",
    "                model2 += lpSum([x[(t,t4,d)]+x[(t4,t,d)] for t4 in top4 for d in [getRoundDaysByRound[r-1][0],getRoundDaysByRound[r][0]] if t4 in opponents[t] ]) <= 1+tooManyTop4InRow[(t,r)]\n",
    "\n",
    "\n",
    "breakVioBalance = { t : pulp.LpVariable('breakVioBalance_'+str(t) , lowBound = 0, cat = pulp.LpContinuous) for t in realteams }\n",
    "numBreaks = { t :  lpSum([  breakVio[(bl['id'],t)]  for bl in breaks ]) for t in realteams }\n",
    "\n",
    "\n",
    "succBreaks = [ bl for bl in breaks if  bl['round1']+1==bl['round2']  ]\n",
    "\n",
    "if thisSeason.forbidDoubleBreaks:\n",
    "    for bl1 in succBreaks:\n",
    "        for bl2 in succBreaks:\n",
    "            if bl1['round2']+1==bl2['round1']:\n",
    "                for t in realteams:\n",
    "                    model2+=  homeInRound[t,bl1['round1']] + homeInRound[t,bl1['round2']]  +  awayInRound[t,bl2['round1']] + awayInRound[t,bl2['round2']]   <=  3 +  0.2* breakVio[(bl1['id'],t)] \n",
    "                    model2+=  awayInRound[t,bl1['round1']] + awayInRound[t,bl1['round2']]  +  homeInRound[t,bl2['round1']] + homeInRound[t,bl2['round2']]   <=  3 +  0.2* breakVio[(bl1['id'],t)] \n",
    "\n",
    "for t in realteams:\n",
    "    for bl in breaks:\n",
    "        # print (getTeamById[t], realteams, getRoundDaysByRound[bl['round1']]+getRoundDaysByRound[bl['round2']] )\n",
    "        # model2+=  breakVio[(bl['id'],t)] + 1 >=  lpSum([x[(t,t2,rd)] for t2 in realteams for rd in getRoundDaysByRound[bl['round1']]+getRoundDaysByRound[bl['round2']] ])\n",
    "        # model2+=  breakVio[(bl['id'],t)] + 1 >=  lpSum([x[(t2,t,rd)] for t2 in realteams for rd in getRoundDaysByRound[bl['round1']]+getRoundDaysByRound[bl['round2']] ])\n",
    "        model2+=  breakVio[(bl['id'],t)] + 1 >= homeInRound[t,bl['round1']] + homeInRound[t,bl['round2']]\n",
    "        model2+=  breakVio[(bl['id'],t)] + 1 >= awayInRound[t,bl['round1']] + awayInRound[t,bl['round2']]\n",
    "        if bl['round2'] + 1 <= nRounds:\n",
    "            model2+=  0.2+breakVio[(bl['id'],t)] + 2 >= homeInRound[t,bl['round1']] + homeInRound[t,bl['round2']] + homeInRound[t,bl['round2']+1] \n",
    "            model2+=  0.2+breakVio[(bl['id'],t)] + 2 >= awayInRound[t,bl['round1']] + awayInRound[t,bl['round2']] + awayInRound[t,bl['round2']+1]         \n",
    "\n",
    "    if balanceBreaks:\n",
    "        model2+=  numBreaks[t] <= lpSum([ numBreaks[t2] for t2 in realteams])/len(realteams)  + breakVioBalance[t]\n",
    "        for t2 in teams: \n",
    "            if half_symmetry_offset==0:\n",
    "                model2+=  numBreaks[t2] <= 4\n",
    "                model2+=  numBreaks[t2] >= 3\n",
    "            else:\n",
    "                model2+=  numBreaks[t2] <= 5\n",
    "                model2+=  numBreaks[t2] >= 1\n",
    "        print (\"balancing Breaks \")\n",
    "\n",
    "        for bl in breaks:\n",
    "            model2+=  breakVio[(bl['id'],t)] <= 2 - homeInRound[t,bl['round1']] - awayInRound[t,bl['round2']] \n",
    "            model2+=  breakVio[(bl['id'],t)] <= 2 - awayInRound[t,bl['round1']] - homeInRound[t,bl['round2']]\n",
    "\n",
    "        \n",
    "\n",
    "    if not evalRun:\n",
    "        if not thisSeason.minBreaks and len(breaks)>0 and not balanceBreaks:\n",
    "            for p in phases:\n",
    "                # print (\"Don't allow breaks for  \",t, \" in phase \", p)\n",
    "                model2+= lpSum([breakVio[(bl['id'],t)] for bl in breaks if getPhaseOfRound[bl['round1']]==p and getPhaseOfRound[bl['round2']]==p]) <= thisSeason.initBreaks\n",
    "\n",
    "        if mathModelName in [ \"Ligue 1\"]:\n",
    "            model2+= lpSum([breakVio[(bl['id'],t)] for bl in breaks ]) <= 4\n",
    "\n",
    "        if not thisSeason.startWithBreakAllowed:\n",
    "            model2+= homeInRound[t,1] + homeInRound[t,2]  <= 1\n",
    "            model2+= awayInRound[t,1] + awayInRound[t,2]  <= 1\n",
    "        if not thisSeason.endWithBreakAllowed:\n",
    "            print (\"NO BREAK FOR \" , t , \" in rounds \" ,nRounds-1, nRounds)\n",
    "            model2+= homeInRound[t,nRounds-1] + homeInRound[t,nRounds]  <= 1\n",
    "            model2+= awayInRound[t,nRounds-1] + awayInRound[t,nRounds]  <= 1\n",
    "\n",
    "\n",
    "\n",
    "getDays[0] =[]\n",
    "\n",
    "\n",
    "if thisSeason.useFeaturePairings:\n",
    "    for pair in pairings:\n",
    "        # print (pair)\n",
    "        pDaysSets = []\n",
    "        for d in days:\n",
    "            pTeams = [pair['team1_id'], pair['team2_id']]\n",
    "            pDays = [d]\n",
    "            if pair['dist'] ==1 and nextDay[d]!=-1:\n",
    "                pDays.append(nextDay[d])\n",
    "            if pair['dist'] in [3,7]:\n",
    "                pDays=getDays[getRoundByDay[d]]\n",
    "                # print ( getNiceDay[d],  getRoundByDay[d],  getDays[getRoundByDay[d]] , \" +\" ,getHigherDaysByRound[getRoundByDay[d]])\n",
    "            factor = 1.0\n",
    "            if len(pDays)>0 and pDays not in pDaysSets:\n",
    "                pDaysSets.append(pDays.copy())\n",
    "            #     print (\"+++ \")\n",
    "            # else: \n",
    "            #     print (\"--- \")\n",
    "\n",
    "            # if pair['prio'] =='A':\n",
    "            #     factor = 0.1                        \n",
    "        for pDays in pDaysSets:\n",
    "            d= pDays[0]\n",
    "            print (\"Treating day set starting with day \" , getNiceDay[d], \" \", pDays)\n",
    "            if pair['dist'] in [2,6] and thisSeason.useFeatureKickOffTime:\n",
    "                for tm in times:\n",
    "                    if pair['type']== \"Home\":\n",
    "                        if pair['dist']<=3:\n",
    "                            model2 += lpSum([home_time[t,d,tm] for t in pTeams]) <= 1 + factor*pairingVio[(pair['id'],d)]\n",
    "                        else:\n",
    "                            model2 += home_time[pair['team1_id'],d,tm] -  home_time[pair['team2_id'],d,tm]  <= factor*pairingVio[(pair['id'],d)]\n",
    "                            model2 += home_time[pair['team2_id'],d,tm] -  home_time[pair['team1_id'],d,tm]  <= factor*pairingVio[(pair['id'],d)]\n",
    "                    else:\n",
    "                        if pair['dist']<=3:\n",
    "                            model2 += lpSum([home_time[t,d,tm] + away_time[t,d,tm] for t in pTeams]) <= 1 + pairingVio[(pair['id'],d)]\n",
    "                        else:\n",
    "                            model2 += home_time[pair['team1_id'],d,tm] + away_time[pair['team1_id'],d,tm] -  home_time[pair['team2_id'],d,tm]  -  away_time[pair['team2_id'],d,tm]  <= factor*pairingVio[(pair['id'],d)]\n",
    "                            model2 += home_time[pair['team2_id'],d,tm] + away_time[pair['team2_id'],d,tm] -  home_time[pair['team1_id'],d,tm]  -  away_time[pair['team1_id'],d,tm]  <= factor*pairingVio[(pair['id'],d)]\n",
    "\n",
    "            else:\n",
    "                if pair['type']== \"Home\":\n",
    "                    if pair['dist']<=3:\n",
    "                        model2 += lpSum([home[t,dd] for t in pTeams for dd in pDays]) <= 1 + factor*pairingVio[(pair['id'],d)]\n",
    "                    else:\n",
    "                        model2 += lpSum([home[pair['team1_id'],dd] - home[pair['team2_id'],dd] for dd in pDays]) <=  factor*pairingVio[(pair['id'],d)]\n",
    "                        model2 += lpSum([home[pair['team2_id'],dd] - home[pair['team1_id'],dd] for dd in pDays]) <=  factor*pairingVio[(pair['id'],d)]\n",
    "                else:\n",
    "                    if pair['dist']<=3:\n",
    "                        model2 += lpSum([home[t,dd] + away[t,dd] for t in pTeams for dd in pDays]) <= 1 + pairingVio[(pair['id'],d)]\n",
    "                    else:\n",
    "                        model2 += lpSum([home[pair['team1_id'],dd] + away[pair['team1_id'],dd] - home[pair['team2_id'],dd] - away[pair['team2_id'],dd] for dd in pDays]) <=  factor*pairingVio[(pair['id'],d)]\n",
    "                        model2 += lpSum([home[pair['team2_id'],dd] + away[pair['team2_id'],dd] - home[pair['team1_id'],dd] - away[pair['team1_id'],dd] for dd in pDays]) <=  factor*pairingVio[(pair['id'],d)]\n",
    "\n",
    "\n",
    "\n",
    "use_currentSolution= False\n",
    "\n",
    "currentGameCntr = { (t1,t2) :0 for t1 in teams for t2 in teams }\n",
    "\n",
    "\n",
    "# if use_currentSolution and len(currentSolution) !=len(fixedGames):    \n",
    "\n",
    "teamGameCntr = { (t,r) :0 for t in teams for r in rounds }\n",
    "\n",
    "\n",
    "\n",
    "# print (\"days \" , days)\n",
    "for d in days: \n",
    "    model2 += lpSum([x[(t1,t2,rd)] for (t1,t2) in games  for rd in getRoundDaysByDay[d] if distance[getTeamById[t1],getTeamById[t2]]<=thisSeason.maxDistanceDerby ]) >= nDerbies[d] - derbyMissing[d]\n",
    "    minG = sum([ roundDaysMin[rd] - deficientGames[rd]  for rd in getRoundDaysByDay[d]])        \n",
    "    maxG = sum([ roundDaysMax[rd] + excessGames[rd] for rd in getRoundDaysByDay[d]])        \n",
    "    # print (getNiceDay[d] , maxG)\n",
    "    # if minG<3 :\n",
    "    if minG>0 :\n",
    "        model2 += lpSum([ home[(t,d)] for t in realteams]) >= minG\n",
    "    model2 += lpSum([ home[(t,d)] for t in realteams]) <= maxG\n",
    "\n",
    "    if len(getRoundDaysByDay[d])>1:\n",
    "        for rd in getRoundDaysByDay[d]:\n",
    "            if roundDaysMin[rd]>0:\n",
    "                model2 += lpSum([x[(t1,t2,rd)] for (t1,t2) in games ] ) >= roundDaysMin[rd] - deficientGames[rd] \n",
    "                # model2 += lpSum([x[(t1,t2,rd)] for (t1,t2) in games ] ) >= roundDaysMin[rd] \n",
    "                print (\"At least \",roundDaysMin[rd] , \" on \" , rd)\n",
    "            model2 += lpSum([x[(t1,t2,rd)] for (t1,t2) in games ] ) <= roundDaysMax[rd] + excessGames[rd] \n",
    "\n",
    "print (\"rounds \" , rounds, nRounds)\n",
    "for r in rounds:\n",
    "    # one game a round for everyone\n",
    "    for t1 in realteams:\n",
    "        if thisSeason.gamesPerRound==\"one day\":\n",
    "            cnstr = lpSum([x[(t1,t2,rd)] + x[(t2,t1,rd)] for t2 in opponents[t1] for rd in getRoundDaysByRound[r]] )\n",
    "            if len(cnstr) > 0:\n",
    "                model2 += cnstr <= 1\n",
    "        else:\n",
    "            print ( getRoundDaysByRound[r], opponents[t1])\n",
    "            if len(getRoundDaysByRound[r])>0:\n",
    "                for rd in getRoundDaysByRound[r]:\n",
    "                    if rd==getRoundDaysByRound[r][0]:\n",
    "                        model2 += lpSum([x[(t1,t2,rd)] + x[(t2,t1,rd)] for t2 in opponents[t1]] ) <= 1\n",
    "                    else:\n",
    "                        for t2 in opponents[t1]:\n",
    "                            model2 += x[(t1,t2,rd)] == x[(t1,t2,getRoundDaysByRound[r][0])]\n",
    "\n",
    "# exit(0)\n",
    "print (\"realteams \" , realteams)\n",
    "\n",
    "print (\"taking care of right numbers of games ... \" ) \n",
    "cntr= 0\n",
    "\n",
    "if not specialGameControl:\n",
    "    for (t1,t2) in realgames: \n",
    "        if undirectedGameCntr[(t1,t2)]==0:\n",
    "            model2 += lpSum([x[(t1,t2,rd)] for rd in roundDays] ) == gameCntr[(t1,t2)] - missingGamesVio[(t1,t2)]\n",
    "        else:\n",
    "            # model2 += lpSum([x[(t1,t2,rd)] for rd in roundDays] ) == gameCntr[(t1,t2)]+undirectedGameCntr[(t1,t2)] - missingGamesVio[(t1,t2)]\n",
    "            model2 += missingGamesVio[(t1,t2)]  >= gameCntr[(t1,t2)] - lpSum([x[(t1,t2,rd)] for rd in roundDays] ) \n",
    "            model2 += missingGamesVio[(t1,t2)]  >= gameCntr[(t1,t2)]+gameCntr[(t2,t1)]+undirectedGameCntr[(t1,t2)] - lpSum([x[(t1,t2,rd)] + x[(t2,t1,rd)] for rd in roundDays] ) \n",
    "            model2 += lpSum([x[(t1,t2,rd)] + x[(t2,t1,rd)] for rd in roundDays] ) <= gameCntr[(t1,t2)]+gameCntr[(t2,t1)]+undirectedGameCntr[(t1,t2)] \n",
    "            \n",
    "        missingGamesVio[(t1,t2)].upBound= max(0,gameCntr[(t1,t2)]+undirectedGameCntr[(t1,t2)] - currentGameCntr[(t1,t2)])\n",
    "\n",
    "\n",
    "\n",
    "if not evalRun:\n",
    "    for (t1,t2) in realgames:\n",
    "        # every pair plays each other in each phase once\n",
    "        for p in phases:\n",
    "            if p<len(phases)-1 or True: # phase violation in last phase possible if it contained more rounds than necessary\n",
    "                if t_usePhases[t1] or t_usePhases[t2]:\n",
    "                    if thisSeason.groupBased and len(noPlayRounds[t1])>0 and noPlayRounds[t1]==noPlayRounds[t2]:\n",
    "                        relDays = []\n",
    "                        phaseLength= int(len(playRounds[t1])/nPhases+0.5)\n",
    "                        for rr in playRounds[t1][p*phaseLength:(p+1)*phaseLength]:\n",
    "                            relDays+=getDays[rr]\n",
    "                            print (\"adding days of round \", rr , \" to phase \", p)\n",
    "                    else:\n",
    "                        relDays = getDaysOfPhase[p]\n",
    "                    model2 += lpSum([x[(t1,t2,rd)] + x[(t2,t1,rd)]  for d in relDays for rd in getRoundDaysByDay[d] ] ) <= 1\n",
    "                    # print (len(relDays),\"reldays\")                        \n",
    "                    # print (getTeamById[t1],getTeamById[t2], sum([x[(t1,t2,rd)] + x[(t2,t1,rd)]  for d in relDays for rd in getRoundDaysByDay[d] ] ))\n",
    "\n",
    "if not evalRun :\n",
    "    for t in realteams:\n",
    "        if t_usePhases[t] and thisSeason.distributeHomeGamesEvenlyOverPhases:\n",
    "            for p in phases:\n",
    "                rds = [r for r in rounds if getPhaseOfRound[r]==p ]\n",
    "                nblocked =[ r for r in noHomeRounds[t] if r in rds]\n",
    "                print (p, len(rds), len(nblocked), rds,nblocked, getTeamById[t])\n",
    "            for p in phases:\n",
    "                phaseLength= int(len(playRounds[t])/nPhases+0.5)\n",
    "                model2 += lpSum([ home[(t,d)] for d in getDaysOfPhase[p] ] ) <= int(phaseLength/2+1)\n",
    "\n",
    "    if thisSeason.minRoundsBetweenGameOfSameTeams>0:\n",
    "        for r in rounds:\n",
    "            if r +thisSeason.minRoundsBetweenGameOfSameTeams <=nRounds:\n",
    "                rds = [ ]            \n",
    "                for r2 in range(r,r+thisSeason.minRoundsBetweenGameOfSameTeams+1):\n",
    "                    rds+=getRoundDaysByRound[r2]\n",
    "                rds2 = [ ]            \n",
    "                for r2 in range(r,r+int (0.5*thisSeason.minRoundsBetweenGameOfSameTeams)):\n",
    "                    rds2+=getRoundDaysByRound[r2]\n",
    "                # print (\"ONLY ONE IN \" , rds)\n",
    "                # print (\"ONLY ONE IN \" , rds2)\n",
    "                for t1 in realteams:\n",
    "                    for t2 in realteams:\n",
    "                        if t1<t2 and (gameCntr[(t1,t2)]*gameCntr[(t2,t1)]>0 or x_round[(t1,t2,r)]!=0) :\n",
    "                                # model2 +=  sum([ (x[(t1,t2, rd)]+x[(t2,t1,rd)]) for rd in rds ]) <= 1 + gamesTooClose2[t1,r]\n",
    "                                model2 +=  sum([ (x[(t1,t2, rd)]+x[(t2,t1,rd)]) for rd in rds ]) <= 1 \n",
    "\n",
    "    \n",
    "if not evalRun:\n",
    "    # max length home stands /trips\n",
    "    for r in range (1,nRounds-thisSeason.maxTourLength+1):\n",
    "        # print (\" at least one home and away in rounds \")\n",
    "        # for r3  in range(r,r+thisSeason.maxTourLength+1):\n",
    "        #     print (r3 )\n",
    "        for t in teams:\n",
    "            if t not in noBreakLimitTeams:\n",
    "                # model2 += lpSum([homeInRound[t,r2] for r2 in range(r,r+thisSeason.maxTourLength+1)]) >=1\n",
    "                model2 += lpSum([awayInRound[t,r2] for r2 in range(r,r+thisSeason.maxTourLength+1)]) <=thisSeason.maxTourLength\n",
    "                model2 += lpSum([homeInRound[t,r2] for r2 in range(r,r+thisSeason.maxTourLength+1)]) <=thisSeason.maxTourLength\n",
    "\n",
    "print (\"check 1\")\n",
    "\n",
    "    # blockings\n",
    "for bl in blockings:\n",
    "    if getDayById[bl['day_id']]['round'] !=0:\n",
    "        if thisSeason.useFeatureKickOffTime and bl['time']!='----':\n",
    "            if bl['type'] in [\"Home\", \"Hide\"]:\n",
    "                model2+=  blockingVio[bl['id']]== home_time[bl['team_id'], bl['day_id'],bl['time']]\n",
    "                # print ('FOUND HOME BLOCKING ', bl)\n",
    "            else:\n",
    "                model2+=  blockingVio[bl['id']]== away_time[bl['team_id'], bl['day_id'],bl['time']]\n",
    "                # print ('FOUND AWAY BLOCKING ', bl)\n",
    "        else:\n",
    "            if bl['type'] in [\"Home\", \"Hide\"]:\n",
    "                model2+=  blockingVio[bl['id']]== home[bl['team_id'], bl['day_id']]\n",
    "                # print ('FOUND HOME BLOCKING ', bl)\n",
    "            else:\n",
    "                model2+=  blockingVio[bl['id']]== away[bl['team_id'], bl['day_id']]\n",
    "                # print ('FOUND AWAY BLOCKING ', bl)\n",
    "\n",
    "\n",
    "print (\"check 2\")\n",
    "\n",
    "hawOneVio={}\n",
    "hawForOneNotViolated={}\n",
    "\n",
    "def getStringFromSet(ss):\n",
    "    s2 =\"\"\n",
    "    for s in ss: \n",
    "        s2+=str(s)+\"_\"\n",
    "    return s2[:-1]\n",
    "\n",
    "    # hawishes\n",
    "for haw in hawishes:\n",
    "    print (haw['reason'])\n",
    "    for el in elemHaWishes[haw['id']]:\n",
    "        if thisSeason.useFeatureKickOffTime and len(hawTimes[haw['id']])>0:\n",
    "            if haw['homeAway']=='Home':\n",
    "                relGames = lpSum([home_time[t,d,tm] for d in elemHaWishDays[el] for t in elemHaWishTeams[el] for tm in hawTimes[haw['id']]])\n",
    "                # print (haw['id'] ,\" haw : \", relGames, hawTimes[haw['id']])\n",
    "            elif haw['homeAway']=='Away':\n",
    "                relGames = lpSum([away_time[t,d,tm] for d in elemHaWishDays[el] for t in elemHaWishTeams[el] for tm in hawTimes[haw['id']]])\n",
    "            else :\n",
    "                # print(haw,el)\n",
    "                relGames = lpSum([home_time[t,d,tm] + away_time[t,d,tm] for d in elemHaWishDays[el] for t in elemHaWishTeams[el] for tm in hawTimes[haw['id']]]) \\\n",
    "                            - lpSum([x_time[(t1,t2,rd,tm)] for d in elemHaWishDays[el] for rd in getRoundDaysByDay[d] for tm in hawTimes[haw['id']] for t1 in elemHaWishTeams[el] for t2 in elemHaWishTeams[el] if (t1,t2,rd,tm) in x_time.keys()]) \n",
    "\n",
    "        else:\n",
    "            if haw['homeAway']=='Home':\n",
    "                relGames = lpSum([home[t,d] for d in elemHaWishDays[el] for t in elemHaWishTeams[el]  ])\n",
    "            elif haw['homeAway']=='Away':\n",
    "                relGames = lpSum([away[t,d] for d in elemHaWishDays[el] for t in elemHaWishTeams[el] ])\n",
    "            else :\n",
    "                relGames = lpSum([home[t,d] + away[t,d] for d in elemHaWishDays[el] for t in elemHaWishTeams[el]]) - lpSum([x[t1,t2,rd] for d in elemHaWishDays[el] for rd in getRoundDaysByDay[d] for t1 in elemHaWishTeams[el] for t2 in elemHaWishTeams[el] if (t1,t2,rd) in x.keys() ]) \n",
    "        if haw['minGames'] >0 :\n",
    "            model2+=  relGames  >= haw['minGames'] - HawVioTooLess[el]\n",
    "            # print (\"adding min ha constraint\")\n",
    "        if haw['maxGames'] >=0 :\n",
    "            model2+=  relGames  <= haw['maxGames'] + HawVioTooMuch[el]\n",
    "            # print (\"adding max ha constraint\")\n",
    "\n",
    "\n",
    "    usedConstraintNames = [\"\"]\n",
    "    if haw['forOneDay']:\n",
    "        # print (haw['forOneDay'], hawDays[haw['id']])\n",
    "        # print ([el for el in elemHaWishes[haw['id']] ])\n",
    "        # for el in elemHaWishes[haw['id']] :\n",
    "        #     print(\"- \" , elemHaWishDays[el]  , elemHaWishTeams[el] )\n",
    "        # print ([ (el, elemHaWishFirstDay[el])  for el in elemHaWishes[haw['id']] ])\n",
    "        relTeamString = { el : getStringFromSet(elemHaWishTeams[el]) for el in elemHaWishes[haw['id']] }\n",
    "        relTeams = set([ relTeamString[el] for el in elemHaWishes[haw['id']]])\n",
    "        # print (relTeams)\n",
    "        for rt in relTeams:\n",
    "            rtname = rt \n",
    "            if len(rt)>50 :\n",
    "                scname=\"\"\n",
    "                while scname in usedConstraintNames:\n",
    "                    ttt = bytes(rt+ ''.join(random.choice(string.ascii_lowercase) for i in range(10)), 'utf-8')\n",
    "                    scname = hashlib.sha224(ttt).hexdigest()\n",
    "                    rtname = scname[:10]\n",
    "                    usedConstraintNames.append(rtname)\n",
    "                    # print (\"use rtname to encode wishes \", rtname)\n",
    "\n",
    "            hawOneVio[(haw['id'], rt)]= pulp.LpVariable('hawOneVio_'+str(haw['id'])+\"_\"+rtname, cat=pulp.LpContinuous)\n",
    "            for fd in hawDays[haw['id']]:\n",
    "                relWishes = [el for el in elemHaWishes[haw['id']] if elemHaWishFirstDay[el]== fd]\n",
    "                # relWishes = [el for el in elemHaWishes[haw['id']] ]\n",
    "                # print (\" -\" , relWishes , [elemHaWishDays[el] for el in relWishes])\n",
    "                hawForOneNotViolated[(haw['id'], rt, fd)]= 0\n",
    "                if len(relWishes)>0:\n",
    "                    hawForOneNotViolated[(haw['id'], rt, fd)]= pulp.LpVariable('hawForOneViolated_'+str(haw['id'])+\"_\"+rtname+\"_\"+str(fd), cat=pulp.LpBinary)\n",
    "                    model2 +=  lpSum( HawVioTooMuch[el]+HawVioTooLess[el] for el in relWishes if relTeamString[el]==rt) <= 1000 *  (1-hawForOneNotViolated[(haw['id'],rt, fd)])\n",
    "            model2 += lpSum( hawForOneNotViolated[(haw['id'], rt, fd)] for fd in hawDays[haw['id']] ) >= haw['forOneDayNum']-hawOneVio[(haw['id'],rt)]\n",
    "        model2 += lpSum( hawOneVio[(haw['id'], rt)] for rt in relTeams ) == hawVio[haw['id']]\n",
    "    else:\n",
    "        # print (haw['forOneDay'], hawDays[haw['id']])\n",
    "        model2 += hawVio[haw['id']]== lpSum( HawVioTooMuch[el]+HawVioTooLess[el] for el in elemHaWishes[haw['id']]) \n",
    "\n",
    "\n",
    "    if haw['prio']==\"Hard\" and \"HardHAWishesNotBreakable\" in special_wishes_active:\n",
    "        model2+=  hawVio[haw['id']] ==0\n",
    "        print (\"WISH HARD\" ,haw['reason'])\n",
    "\n",
    "\n",
    "# HawVioTotal=lpSum([prioVal[haw['prio']] * (HawVioTooLess[el]+HawVioTooMuch[el]) for haw in hawishes for el in elemHaWishes[haw['id']]])\n",
    "# print (hawDays)\n",
    "# print (elemHaWishFirstDay) \n",
    "\n",
    "encOneVio={}\n",
    "encForOneNotViolated={}\n",
    "print (\"check 3\")\n",
    "# encwishes\n",
    "for enc in encwishes:\n",
    "    print (enc)\n",
    "    for el in elemEncWishes[enc['id']]:        \n",
    "        # print (enc)\n",
    "        # model2+=  encVio[enc['id']] == 1 - x[enc['team1_id'], enc['team2_id'], enc['day_id']]\n",
    "\n",
    "        if thisSeason.useFeatureKickOffTime and len(encTimes[enc['id']])>0:\n",
    "            if enc['minGames'] >0 :\n",
    "                model2+=  encVioTooLess[el] >= enc['minGames'] - sum([ x_time[(t1,t2,rd, tm )]  for d in elemEncWishDays[el] for rd in getRoundDaysByDay[d] for (t1,t2) in elemEncWishGames[el] for tm in encTimes[enc['id']] if (t1,t2) in games  ])\n",
    "            if enc['maxGames'] >=0 :\n",
    "                # if enc['maxGames']==0:\n",
    "                print (enc['reason'], '  ', elemEncWishDays[el], '  ',enc['time'], '  ', encTeams1[enc['id']] , '  ',encTeams2[enc['id']]   )\n",
    "                #     print (sum([x_time[(t1,t2,rd, enc['time'])]  for d in elemEncWishDays[el] for rd in getRoundDaysByDay[d] for (t1,t2) in elemEncWishGames[el] if (t1,t2) in games  ]))\n",
    "                model2+=  encVioTooMuch[el] >= -enc['maxGames'] + sum([x_time[(t1,t2,rd, tm )]  for d in elemEncWishDays[el] for rd in getRoundDaysByDay[d] for (t1,t2) in elemEncWishGames[el] for tm in encTimes[enc['id']] if (t1,t2) in games  ])\n",
    "        else:\n",
    "            if enc['minGames'] >0 :\n",
    "                model2+=  encVioTooLess[el] >= enc['minGames'] - sum([ x[t1,t2,rd]  for d in elemEncWishDays[el] for rd in getRoundDaysByDay[d] for (t1,t2) in elemEncWishGames[el] if (t1,t2) in games  ])\n",
    "            if enc['maxGames'] >=0 :\n",
    "                # if enc['maxGames']==0:\n",
    "                #     print (enc['reason'], '  ', encDays[enc['id']], '  ', encTeams1[enc['id']] , '  ',encTeams2[enc['id']]   )\n",
    "                model2+=  encVioTooMuch[el] >= -enc['maxGames'] + sum([ x[t1,t2,rd]  for d in elemEncWishDays[el] for rd in getRoundDaysByDay[d] for (t1,t2) in elemEncWishGames[el] if (t1,t2) in games  ])\n",
    "\n",
    "    if enc['forOneDay']:\n",
    "        for ed in encDaySets[enc['id']]:\n",
    "            if len(ed)>0:\n",
    "                relWishes = [ el for el in elemEncWishes[enc['id']] if elemEncWishDays[el] == ed ]\n",
    "                print (\"###\",ed, relWishes)                \n",
    "                encForOneNotViolated[(enc['id'], ed[0])]= pulp.LpVariable('encForOneNotViolated_'+str(enc['id'])+\"_\"+str(ed[0]), cat=pulp.LpBinary)\n",
    "                model2 +=  sum( encVioTooMuch[el]+encVioTooLess[el] for el in relWishes) <= 1000 * (1-encForOneNotViolated[(enc['id'], ed[0])])\n",
    "        model2 += sum( encForOneNotViolated[(enc['id'], ed[0])] for ed in encDaySets[enc['id']] if len(ed)>0 ) >= enc['forOneDayNum']-encVio[enc['id']]\n",
    "    else:\n",
    "        model2 += encVio[enc['id']]== sum( encVioTooMuch[el]+encVioTooLess[el] for el in elemEncWishes[enc['id']]) \n",
    "\n",
    "\n",
    "    if enc['prio']==\"Hard\" and \"HardEncWishesNotBreakable\" in special_wishes_active:\n",
    "        model2+=  encVio[enc['id']] ==0\n",
    "        print (\"WISH HARD\" ,enc['reason'])\n",
    "\n",
    "\n",
    "print (\"check 4\")\n",
    "    \n",
    "\n",
    "# return \"\"\n",
    "weekdayHomePref={}\n",
    "dayHomePref={}\n",
    "for t in teams:\n",
    "    tm = getTeamByName[getTeamById[t]]\n",
    "    weekdayHomePref[(t,'Mon')]=tm['home_pref_mo']\n",
    "    weekdayHomePref[(t,'Tue')]=tm['home_pref_tu']\n",
    "    weekdayHomePref[(t,'Wed')]=tm['home_pref_we']\n",
    "    weekdayHomePref[(t,'Thu')]=tm['home_pref_th']\n",
    "    weekdayHomePref[(t,'Fri')]=tm['home_pref_fr']\n",
    "    weekdayHomePref[(t,'Sat')]=tm['home_pref_sa']\n",
    "    weekdayHomePref[(t,'Sun')]=tm['home_pref_su']\n",
    "    for d in days :\n",
    "        dayHomePref[(t,d)]=weekdayHomePref[(t,getWeekDay[d])]\n",
    "\n",
    "\n",
    "maxTravelDistance =  max([ distanceById[t1,t2] for t1 in realteams for t2 in realteams ]) \n",
    "maxTravelDistance = max(1,maxTravelDistance)\n",
    "\n",
    "singleTripWeight=50\n",
    "breakImbalanceTotal= lpSum([ breakVioBalance[t] for t in realteams])\n",
    "# HawVioTotal=lpSum([prioVal[haw['prio']] * (HawVioTooLess[el]+HawVioTooMuch[el]) for haw in hawishes for el in elemHaWishes[haw['id']]])\n",
    "HawVioTotal=lpSum([prioVal[haw['prio']] * hawVio[haw['id']] for haw in hawishes])\n",
    "encVioTotal=lpSum([prioVal[enc['prio']] * encVio[enc['id']] for enc in encwishes])\n",
    "seedVioTotal=lpSum([100*prioVal[enc['prio']] * encVio[enc['id']] for enc in encwishes if enc['reason']==\"Seed Game\" ])\n",
    "# broadVioTotal=lpSum([ 10 * broadVio[d] for d in days]) + lpSum([ 10 * broadVioTm[b.id] for b in broadcastingwishes])\n",
    "broadVioTotal=lpSum([ 10 * broadVioTm[(b.id,r)] for b in broadcastingwishes for r in rounds])\n",
    "breakVioTotal=lpSum([prioVal[bl['prio']]*breakVio[(bl['id'],t)] for bl in breaks for t in realteams]) + 2*lpSum([prioVal[bl['prio']]*breakVio[(bl['id'],t)] for bl in breaks for t in importantteams])\n",
    "break3VioTotal=lpSum([2*break3InRound[t,r] for t in teams for r in rounds])\n",
    "tooManyTop4InRowTotal=lpSum([ 10*tooManyTop4InRow[(t,r)] for t in teams for r in rounds])\n",
    "pairingVioTotal=lpSum([ 5 *prioVal[pair['prio']] * pairingVio[(pair['id'],d)] for pair in pairings for d in days])\n",
    "# blockingVioTotal=lpSum([ 100 * blockingVio[bl['id']] for bl in blockings if bl['type']==\"Home\"])\n",
    "# print (blockings)    \n",
    "# for bl in blockings: \n",
    "    # if bl['type'] in [\"Home\"]:\n",
    "        # print (blocked_arena[(bl[\"team_id\"],bl[\"day_id\"],\"----\")], getTeamById[bl[\"team_id\"]], getNiceDay[bl[\"day_id\"]]  , bl )\n",
    "\n",
    "fulfBlocks =set([(bl[\"team_id\"], getRoundByDay[bl[\"day_id\"]]) for bl in blockings if bl['type'] in [\"Home\"] and blocked_arena[(bl[\"team_id\"],bl[\"day_id\"],\"----\")]]  )\n",
    "\n",
    "blockingVioTotal2=lpSum([ -30 * homeInRound[tr]  for tr in fulfBlocks if thisSeason.allowBlockingViosInImprove and runMode=='Improve'] ) \n",
    "blockingVioTotal=lpSum([ 100 * blockingVio[bl['id']] for bl in blockings if bl['type'] in [\"Home\", \"Hide\"]]) +blockingVioTotal2\n",
    "\n",
    "travelVioTotal=lpSum([ 100 * blockingVio[bl['id']] for bl in blockings if bl['type']==\"Away\"])\n",
    "derbiesMissingTotal=lpSum([ 30 * derbyMissing[d] for d in days])\n",
    "unpreferredTotal=lpSum([ home[t, d] for t in teams for d in days if dayHomePref[(t,d)]==0 ])\n",
    "# competitionVioTotal=lpSum([ 100 * competitionVio[(c,d,t)] for (c,d,t) in competitions])\n",
    "missingGamesVioTotal=lpSum([ 2000000 * missingGamesVio[(t1,t2)] for (t1,t2) in realgames])\n",
    "# TODO -  UNDO CHANGES: missingGamesVioTotal=lpSum([ 2000 * missingGamesVio[(t1,t2)] for (t1,t2) in games])\n",
    "oldScenGamesTotal=lpSum([ wg * x[t1,t2,rd] for (t1,t2,r,wg) in otherScenGames for rd in getRoundDaysByRound[r]] )\n",
    "totalAttendance=lpSum([ attendance[(t1,t2,d)] * x[t1,t2,(r,d)] for (t1,t2) in games for (r,d) in roundDays ] )\n",
    "\n",
    "specialObjectives = 0\n",
    "specialWishItems ={ sw:[] for sw in special_wishes_active}\n",
    "specialWishVio ={}\n",
    "\n",
    "\n",
    "optCameraMovement = \"Standard\"\n",
    "\n",
    "# tvkitproblem = {}\n",
    "move2 = {}\n",
    "newtrip2 = {}\n",
    "\n",
    "\n",
    "standardObjectives=1+gew['Home-/Away']*HawVioTotal\\\n",
    "        +gew['Home-/Away']*3*unpreferredTotal \\\n",
    "        +gew['Pairings']*pairingVioTotal \\\n",
    "        +gew['Blockings']*blockingVioTotal \\\n",
    "        +gew['Traveling']*travelVioTotal \\\n",
    "        +gew['Breaks']*breakVioTotal \\\n",
    "        +gew['Breaks']*2*break3VioTotal \\\n",
    "        +gew['Breaks']*1*breakImbalanceTotal \\\n",
    "        +5*gew['Encounters']*encVioTotal \\\n",
    "        +5*gew['Encounters']*seedVioTotal \\\n",
    "        +gew['Broadcasting']*broadVioTotal \\\n",
    "        +gew['Derbies']*derbiesMissingTotal \\\n",
    "        +1.0*tooManyTop4InRowTotal\\\n",
    "        +missingGamesVioTotal\\\n",
    "        +oldScenGamesTotal\\\n",
    "        -0.01*totalAttendance \n",
    "\n",
    "\n",
    "model2+= standardObjectives "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "952f1e44",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "script\n",
      "HOMEAWAY;1-17\n",
      "BASICGAME;1-17\n",
      "HOMEAWAY;18-34\n",
      "GAME;1-34\n",
      "[['HOMEAWAY', '1-17'], ['BASICGAME', '1-17'], ['HOMEAWAY', '18-34'], ['GAME', '1-34']]\n"
     ]
    }
   ],
   "source": [
    "script =thisSeason.optimizationScript\n",
    "if script == '' :\n",
    "    if useBasicGames:\n",
    "        script += \"HEURISTIC\\n\"\n",
    "        script += \"GAME;1-\"+str(nRounds)+\";0.1;200\\n\"\n",
    "    if thisSeason.groupBased:\n",
    "        for c in allConferences:\n",
    "            if not c.regional and c.teams.count() <= 12:           \n",
    "                script += \"GROUP;1-\"+str(nRounds)+\";0.05;30;\"+c.name+\"\\n\"\n",
    "                for cr in range(nPhases):\n",
    "                    minIntRound= min( (cr) * nRoundsPerPhase+1, nRounds)\n",
    "                    maxIntRound= min( (cr+1)*nRoundsPerPhase, nRounds)\n",
    "\n",
    "\n",
    "script=script.replace('\\r', '')\n",
    "print (\"script\")\n",
    "print (script)\n",
    "optSteps = [ st.split(';') for st in script.split(\"\\n\")]\n",
    "print (optSteps)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "c4dc8d33",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model built now solving .... \n",
      "True True False\n",
      " -  ['HOMEAWAY', '1-17']\n",
      "\n",
      "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]\n",
      "########################\n",
      "# SOLVING MODEL HOMEAWAY  FOR ROUNDS 1-17 USING GAP 0.0 and MAXTIME 300 #\n",
      "########################\n",
      "[10, 3, 17, 14, 15, 13, 12, 6, 9, 5, 8, 18, 19, 20, 11, 7, 4, 16]\n",
      "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]\n",
      "fixing home 5 : ratiopharm ulm  1.0\n",
      "fixing home 6 : ratiopharm ulm  1.0\n",
      "fixing home 8 : ratiopharm ulm  1.0\n",
      "fixing home 9 : ratiopharm ulm  1.0\n",
      "fixing home 11 : ratiopharm ulm  1.0\n",
      "fixing home 12 : ratiopharm ulm  1.0\n",
      "fixing home 15 : ratiopharm ulm  1.0\n",
      "fixing home 17 : ratiopharm ulm  1.0\n",
      "fixing home 2 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 3 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 4 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 6 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 8 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 11 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 13 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 15 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 17 : Basketball Löwen Braunschweig  1.0\n",
      "fixing home 2 : MHP RIESEN Ludwigsburg  1.0\n",
      "fixing home 4 : MHP RIESEN Ludwigsburg  1.0\n",
      "fixing home 6 : MHP RIESEN Ludwigsburg  1.0\n",
      "fixing home 10 : MHP RIESEN Ludwigsburg  1.0\n",
      "fixing home 13 : MHP RIESEN Ludwigsburg  1.0\n",
      "fixing home 17 : MHP RIESEN Ludwigsburg  1.0\n",
      "fixing home 4 : BG Göttingen  1.0\n",
      "fixing home 6 : BG Göttingen  1.0\n",
      "fixing home 8 : BG Göttingen  1.0\n",
      "fixing home 13 : BG Göttingen  1.0\n",
      "fixing home 15 : BG Göttingen  1.0\n",
      "fixing home 16 : BG Göttingen  1.0\n",
      "fixing home 2 : SYNTAINICS MBC  1.0\n",
      "fixing home 4 : SYNTAINICS MBC  1.0\n",
      "fixing home 6 : SYNTAINICS MBC  1.0\n",
      "fixing home 7 : SYNTAINICS MBC  1.0\n",
      "fixing home 9 : SYNTAINICS MBC  1.0\n",
      "fixing home 10 : SYNTAINICS MBC  1.0\n",
      "fixing home 14 : SYNTAINICS MBC  1.0\n",
      "fixing home 16 : SYNTAINICS MBC  1.0\n",
      "fixing home 2 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 5 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 6 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 8 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 11 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 14 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 15 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 17 : FRAPORT SKYLINERS  1.0\n",
      "fixing home 1 : FC Bayern München  1.0\n",
      "fixing home 3 : FC Bayern München  1.0\n",
      "fixing home 5 : FC Bayern München  1.0\n",
      "fixing home 8 : FC Bayern München  1.0\n",
      "fixing home 14 : FC Bayern München  1.0\n",
      "fixing home 17 : FC Bayern München  1.0\n",
      "fixing home 1 : Brose Bamberg  1.0\n",
      "fixing home 5 : Brose Bamberg  1.0\n",
      "fixing home 9 : Brose Bamberg  1.0\n",
      "fixing home 12 : Brose Bamberg  1.0\n",
      "fixing home 14 : Brose Bamberg  1.0\n",
      "fixing home 16 : Brose Bamberg  1.0\n",
      "fixing home 2 : ALBA BERLIN  1.0\n",
      "fixing home 4 : ALBA BERLIN  1.0\n",
      "fixing home 7 : ALBA BERLIN  1.0\n",
      "fixing home 9 : ALBA BERLIN  1.0\n",
      "fixing home 11 : ALBA BERLIN  1.0\n",
      "fixing home 13 : ALBA BERLIN  1.0\n",
      "fixing home 15 : ALBA BERLIN  1.0\n",
      "fixing home 17 : ALBA BERLIN  1.0\n",
      "fixing home 4 : Hamburg Towers  1.0\n",
      "fixing home 7 : Hamburg Towers  1.0\n",
      "fixing home 11 : Hamburg Towers  1.0\n",
      "fixing home 12 : Hamburg Towers  1.0\n",
      "fixing home 14 : Hamburg Towers  1.0\n",
      "fixing home 16 : Hamburg Towers  1.0\n",
      "fixing home 2 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 3 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 4 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 7 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 9 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 10 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 13 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 15 : EWE Baskets Oldenburg  1.0\n",
      "fixing home 3 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 5 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 8 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 10 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 11 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 12 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 14 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 16 : HAKRO Merlins Crailsheim  1.0\n",
      "fixing home 3 : medi bayreuth  1.0\n",
      "fixing home 4 : medi bayreuth  1.0\n",
      "fixing home 7 : medi bayreuth  1.0\n",
      "fixing home 11 : medi bayreuth  1.0\n",
      "fixing home 12 : medi bayreuth  1.0\n",
      "fixing home 16 : medi bayreuth  1.0\n",
      "fixing home 17 : medi bayreuth  1.0\n",
      "fixing home 1 : NINERS Chemnitz  1.0\n",
      "fixing home 2 : NINERS Chemnitz  1.0\n",
      "fixing home 5 : NINERS Chemnitz  1.0\n",
      "fixing home 7 : NINERS Chemnitz  1.0\n",
      "fixing home 8 : NINERS Chemnitz  1.0\n",
      "fixing home 14 : NINERS Chemnitz  1.0\n",
      "fixing home 16 : NINERS Chemnitz  1.0\n",
      "fixing home 1 : MLP Academics Heidelberg  1.0\n",
      "fixing home 3 : MLP Academics Heidelberg  1.0\n",
      "fixing home 7 : MLP Academics Heidelberg  1.0\n",
      "fixing home 9 : MLP Academics Heidelberg  1.0\n",
      "fixing home 11 : MLP Academics Heidelberg  1.0\n",
      "fixing home 13 : MLP Academics Heidelberg  1.0\n",
      "fixing home 15 : MLP Academics Heidelberg  1.0\n",
      "fixing home 17 : MLP Academics Heidelberg  1.0\n",
      "fixing home 1 : JobStairs Gießen 46ers  1.0\n",
      "fixing home 3 : JobStairs Gießen 46ers  1.0\n",
      "fixing home 5 : JobStairs Gießen 46ers  1.0\n",
      "fixing home 8 : JobStairs Gießen 46ers  1.0\n",
      "fixing home 10 : JobStairs Gießen 46ers  1.0\n",
      "fixing home 15 : JobStairs Gießen 46ers  1.0\n",
      "fixing home 17 : JobStairs Gießen 46ers  1.0\n",
      "fixing home 3 : Telekom Baskets Bonn  1.0\n",
      "fixing home 6 : Telekom Baskets Bonn  1.0\n",
      "fixing home 9 : Telekom Baskets Bonn  1.0\n",
      "fixing home 11 : Telekom Baskets Bonn  1.0\n",
      "fixing home 12 : Telekom Baskets Bonn  1.0\n",
      "fixing home 14 : Telekom Baskets Bonn  1.0\n",
      "fixing home 16 : Telekom Baskets Bonn  1.0\n",
      "fixing home 3 : s.Oliver Würzburg  1.0\n",
      "fixing home 5 : s.Oliver Würzburg  1.0\n",
      "fixing home 7 : s.Oliver Würzburg  1.0\n",
      "fixing home 9 : s.Oliver Würzburg  1.0\n",
      "fixing home 12 : s.Oliver Würzburg  1.0\n",
      "fixing home 13 : s.Oliver Würzburg  1.0\n",
      "fixing home 16 : s.Oliver Würzburg  1.0\n",
      " -  ['BASICGAME', '1-17']\n",
      "\n",
      "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]\n",
      "########################\n",
      "# SOLVING MODEL BASICGAME  FOR ROUNDS 1-17 USING GAP 0.0 and MAXTIME 300 #\n",
      "########################\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "'>' not supported between instances of 'NoneType' and 'float'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-29-44dbd5b21bed>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m    132\u001b[0m                 \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mt1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mt2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mgames\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    133\u001b[0m                     \u001b[0;32mif\u001b[0m \u001b[0mgetTeamById\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;34m\"-\"\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mgetTeamById\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m!=\u001b[0m\u001b[0;34m\"-\"\u001b[0m \u001b[0;32mand\u001b[0m  \u001b[0;34m(\u001b[0m\u001b[0mt1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mt2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mgameInBasicRound\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkeys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgameInBasicRound\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mt2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 134\u001b[0;31m                         \u001b[0;32mif\u001b[0m \u001b[0mgetVal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgameInBasicRound\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mt2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m>\u001b[0m\u001b[0;36m0.9\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    135\u001b[0m                             \u001b[0mgameInBasicRound\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mt2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlowBound\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    136\u001b[0m                         \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: '>' not supported between instances of 'NoneType' and 'float'"
     ]
    }
   ],
   "source": [
    "\n",
    "print(\"Model built now solving .... \")\n",
    "\n",
    "nRuns =1\n",
    "maxSolveTime = 300\n",
    "\n",
    "if thisSeason.groupBased:\n",
    "    maxSolveTime = 40\n",
    "\n",
    "mipgap=0.01\n",
    "\n",
    "# print (\"######## Testing\")\n",
    "# model2.solve(GUROBI(MIPGap=0.0, TimeLimit=120,msg=1))\n",
    "\n",
    "\n",
    "use_LP_heuristic= False\n",
    "print (runMode=='New' , useBasicGames , runPatternAssignmentFirst)\n",
    "\n",
    "nRuns=nPhases\n",
    "# maxSolveTime = 120\n",
    "mipgap=0.0\n",
    "# mipgap=0.95\n",
    "\n",
    "# nRuns =1\n",
    "onlyReopt= True\n",
    "onlyReopt= False\n",
    "onlyFewTrips= False\n",
    "singleTripWeight =10 \n",
    "\n",
    "if onlyReopt:\n",
    "    nRuns=0\n",
    "\n",
    "maxIntRound=nRounds\n",
    "\n",
    "\n",
    "missing_imp=[]    \n",
    "cntr =0\n",
    "\n",
    "for st in optSteps:\n",
    "    print (\" - \" ,st )\n",
    "    cntr +=1\n",
    "    if runMode=='New' and st[0] == \"HARDCONSTRAINTS\":\n",
    "        for bl in blockings:\n",
    "            blockingVio[bl['id']].upBound=0\n",
    "            print (\"blocking tightened : \" , getTeamById [bl['team_id']] , bl['day_id']  )                \n",
    "        for enc in encwishes:\n",
    "            if enc['reason']==\"Seed Game\":\n",
    "                encVio[enc['id']].upBound=0\n",
    "    \n",
    "    \n",
    "    if runMode=='New' and len(st)>=1 and st[0] in [\"PATTERNS\",\"HOMEAWAY\", \"BASICGAME\", \"GAME\",\"GAMES\", \"GROUP\", \"TRIPS\", \"LP-HEURISTIC\"]:\n",
    "        newRounds = []\n",
    "        print()            \n",
    "        optTarget = st[0]\n",
    "        if len(st)>1 :\n",
    "            for rf in  st[1].split(\",\"):\n",
    "                rr= rf.split(\"-\")\n",
    "                if len(rr)==1 :\n",
    "                    newRounds.append(min(nRounds,int(rr[0])))\n",
    "                else:\n",
    "                    for ii in range (int(rr[0]), min(nRounds, int(rr[1])+1)):\n",
    "                        newRounds.append(ii)                        \n",
    "            newRoundsString = st[1]\n",
    "        else : \n",
    "            newRounds = rounds\n",
    "            newRoundsString = \"1-\"+str(nRounds) \n",
    "\n",
    "        optsteptime = maxSolveTime\n",
    "        optstepgap = mipgap\n",
    "\n",
    "        if len(st)>=3 and st[2]!=\"\":\n",
    "            optstepgap = float(st[2])\n",
    "        if len(st)>=4 and st[3]!=\"\":\n",
    "            optsteptime = float(st[3])\n",
    "        \n",
    "        print (newRounds)\n",
    "\n",
    "        if st[0] == \"HOMEAWAY\":\n",
    "            for t in teams:\n",
    "                for r in newRounds:\n",
    "                    homeInRound[(t,r)].cat = pulp.LpInteger\n",
    "\n",
    "        if st[0] == \"BASICGAME\":\n",
    "            for (t1,t2) in games:\n",
    "                for r in newRounds:\n",
    "                    gameInBasicRound[(t1,t2,r)].cat = pulp.LpInteger\n",
    "\n",
    "        if st[0] in [\"GAME\",\"GAMES\"]:\n",
    "            for (t,t2) in games:\n",
    "                for r in newRounds:\n",
    "                    for rd in getRoundDaysByRound[r]:\n",
    "                        makeIntVar(x[(t,t2,rd)])\n",
    "\n",
    "\n",
    "\n",
    "        print ('########################')\n",
    "        print ('# SOLVING MODEL '+optTarget+'  FOR ROUNDS '+ newRoundsString+' USING GAP ' + str(optstepgap) + ' and MAXTIME ' + str(optsteptime) + ' #')\n",
    "        print ('########################')\n",
    "        \n",
    "        if solver == \"CBC\":\n",
    "            model2.solve(PULP_CBC_CMD(fracGap = optstepgap, maxSeconds = optsteptime, threads = 8,msg=1))\n",
    "        elif solver == \"Gurobi\":\n",
    "            model2.solve(GUROBI(MIPGap=optstepgap, TimeLimit=optsteptime,msg=1, Method=2,NodeMethod=2))\n",
    "        else:\n",
    "            # for debugging:    \n",
    "            # with open (\"model2.txt\", \"w\") as f:\n",
    "            #     f.write(model2.__repr__())\n",
    "            model2.solve(XPRESS(msg=1,targetGap=optstepgap, maxSeconds = optsteptime,  options=[\"THREADS=12,DETERMINISTIC=0,CUTSTRATEGY=0\"], keepFiles=True))\n",
    "\n",
    "        if model2.status<0:\n",
    "            print(\"Status: \" , model2.status)\n",
    "\n",
    "        if not lowerBoundFound:\n",
    "            lowerBoundFound=value(model2.objective)\n",
    "\n",
    "        cntr_rnd =0\n",
    "                   \n",
    "\n",
    "        if st[0] == \"HOMEAWAY\":\n",
    "            print (teams)\n",
    "            print (newRounds)\n",
    "            for t in teams:\n",
    "                for r in newRounds:\n",
    "                    if homeInRound[(t,r)].value() >0.9 :\n",
    "                        print ('fixing home '+ str(r) + ' : '+ getTeamById[t] +\"  \" + str(homeInRound[(t,r)].value()))\n",
    "                        homeInRound[(t,r)].lowBound = 1\n",
    "                    else:\n",
    "                        homeInRound[(t,r)].upBound = 0\n",
    "            \n",
    "\n",
    "        if st[0] == \"BASICGAME\":\n",
    "            for r in newRounds:\n",
    "                for (t1,t2) in games:\n",
    "                    if getTeamById[t1]!=\"-\" and getTeamById[t2]!=\"-\" and  (t1,t2,r) in gameInBasicRound.keys() and type(gameInBasicRound[(t1,t2,r)])!= int:\n",
    "                        if getVal(gameInBasicRound[(t1,t2,r)])>0.9:\n",
    "                            gameInBasicRound[(t1,t2,r)].lowBound = 1\n",
    "                        else:\n",
    "                            gameInBasicRound[(t1,t2,r)].upBound = 0\n",
    "\n",
    "        if st[0] in [\"GAME\",\"GAMES\"]:\n",
    "            feedback = \"Optimize games....\"\n",
    "            missing_imp=[]\n",
    "            \n",
    "            for (t1,t2) in realgames:\n",
    "                if missingGamesVio[(t1,t2)].value() >0.9:\n",
    "                    feedback += 'Game missing : '+getTeamById[t1] + ' - ' + getTeamById[t2] + ' ' + str(missingGamesVio[(t1,t2)].value()) + '<br> \\n'\n",
    "                    missing_imp += [(1,nRounds,[t1,t2], 50)] \n",
    "\n",
    "            print (missing_imp)\n",
    "            print (feedback)\n",
    "            print (\"number of assigned games : \" , sum([getVal(x[ttrd]) for ttrd in x.keys()]))\n",
    "\n",
    "            for (t1,t2) in games:\n",
    "                for r in newRounds:\n",
    "                    for rd in getRoundDaysByRound[r]:\n",
    "                        if getVal(x[(t1,t2,rd)])>0.9:\n",
    "                            setLB(x[(t1,t2,rd)],1) \n",
    "                            print (\"fixing \" ,t1,t2,rd, x[(t1,t2,rd)].lowBound )\n",
    "                        else:\n",
    "                            setUB(x[(t1,t2,rd)],0)\n",
    "\n",
    "        for r in basicRounds:\n",
    "            for t1 in realteams:\n",
    "                homeInBasicRound[(t1,r)].lowBound = 0\n",
    "                homeInBasicRound[(t1,r)].upBound  = 10\n",
    "                awayInBasicRound[(t1,r)].lowBound = 0\n",
    "                awayInBasicRound[(t1,r)].upBound  = 10\n",
    "                for t2 in opponents[t1]:\n",
    "                    if (t1,t2) in games:\n",
    "                        gameInBasicRound[(t1,t2,r)].cat = pulp.LpContinuous\n",
    "                        gameInBasicRound[(t1,t2,r)].lowBound = 0\n",
    "\n",
    "\n",
    "        for t in realteams:\n",
    "            for r in newRounds:\n",
    "                homeInRound[(t,r)].cat = pulp.LpContinuous\n",
    "                for t2 in opponents[t]:\n",
    "                    for rd in getRoundDaysByRound[r]:\n",
    "                        if (t,t2) in games and  getVal(x[(t,t2,rd)]) >0.9 :\n",
    "                            setLB(x[(t,t2,rd)], x[(t,t2,rd)].value())\n",
    "        currentSolution =[ (t1,t2,r,d) for (t1,t2) in realgames for (r,d) in roundDays if getVal(x[(t1,t2,(r,d))]) >0.9 ]\n",
    "\n",
    "\n",
    "\n",
    "for r in rounds:\n",
    "    for t in teams:\n",
    "        homeInRound[(t,r)].lowBound = 0\n",
    "        homeInRound[(t,r)].upBound = 1\n",
    "\n",
    "for (t1,t2) in games:\n",
    "    for r in rounds:\n",
    "        for rd in getRoundDaysByRound[r]:\n",
    "            makeIntVar(x[(t1,t2,rd)])\n",
    "\n",
    "print ('Solved Again')\n",
    "print ('NOW REOPT')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "496f8609",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.7.13 ('leagues')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  },
  "vscode": {
   "interpreter": {
    "hash": "a07b7f3079ca8c056705d3c757c4f3f92f9509f33eeab9ad5420dacec37bc01a"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
