def get_teamtable(userid_name, schedcol_name, div_age, div_gen, team_id, sched_cat): callback_name = request.query.callback #schedMaster = _routelogic_obj.schedmaster_map.get(userid_name) sindex_list = _routelogic_obj.sindexerMatch(userid_name, sched_cat) if sindex_list: sindex = sindex_list[0] schedMaster = _routelogic_obj.schedmaster_map_list[sindex]['schedmaster_obj'] if len(sindex_list) > 1: logging.warning("RouteProcess:get_teamtable, multiple indexermatch=%s" % (sindex_list,)) else: schedMaster = None #raise CodeLogicError("RouteProcess:get_teamtable, No indexermatch with routelogic=%s" # % (_routelogic_obj.schedmaster_map_list,)) if schedMaster is None: dbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, sched_cat) param = dbInterface.getschedule_param() schedMaster = SchedMaster(mongoClient, userid_name, param['divdb_type'], param['divcol_name'], param['fieldcol_name'], schedcol_name, prefcol_name=param['prefcol_name'], conflictcol_name=param['conflictcol_name']) if not schedMaster.error_code: # save schedMaster to global obj to reuse on get_schedule #_routelogic_obj.schedmaster_map[userid_name] = schedMaster _routelogic_obj.schedmaster_map_list.append( {"userid_name":userid_name, "sched_cat":sched_cat, "schedmaster_obj":schedMaster}) if schedMaster.schedcol_name == schedcol_name: return schedMaster.getHTMLTeamTable(div_age, div_gen, team_id) else: return_dict = {} a = json.dumps(return_dict) return callback_name+'('+a+')'
def get_schedule(userid_name, schedcol_name, idproperty, propid, sched_cat): callback_name = request.query.callback #schedMaster = _routelogic_obj.schedmaster_map.get(userid_name) sindex_list = _routelogic_obj.sindexerMatch(userid_name, sched_cat) if sindex_list: sindex = sindex_list[0] schedMaster = _routelogic_obj.schedmaster_map_list[sindex][ 'schedmaster_obj'] if len(sindex_list) > 1: logging.warning( "RouteProcess:get_schedule, multiple indexermatch=%s" % (sindex_list, )) else: schedMaster = None #else: # raise CodeLogicError("RouteProcess:get_schedule, No indexermatch with routelogic=%s" # % (_routelogic_obj.schedmaster_map_list,)) if schedMaster is None: dbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, sched_cat) param = dbInterface.getschedule_param() schedMaster = SchedMaster(mongoClient, userid_name, param['divdb_type'], param['divcol_name'], param['fieldcol_name'], schedcol_name, prefcol_name=param['prefcol_name'], conflictcol_name=param['conflictcol_name']) if not schedMaster.error_code: # save schedMaster to global obj to reuse on get_schedule #_routelogic_obj.schedmaster_map[userid_name] = schedMaster _routelogic_obj.schedmaster_map_list.append({ "userid_name": userid_name, "sched_cat": sched_cat, "schedmaster_obj": schedMaster }) if schedMaster.schedcol_name == schedcol_name: if idproperty == 'team_id' or idproperty == 'fair_id': # read query parameters if idprop is team_id - div_age and div_gen div_age = request.query.div_age div_gen = request.query.div_gen return_dict = schedMaster.get_schedule(idproperty, propid, div_age=div_age, div_gen=div_gen) else: return_dict = schedMaster.get_schedule(idproperty, propid) else: return_dict = {} a = json.dumps(return_dict) return callback_name + '(' + a + ')'
def get_xls(userid_name, schedcol_name, db_type, genxls_id, sched_cat): callback_name = request.query.callback #schedMaster = _routelogic_obj.schedmaster_map.get(userid_name) sindex_list = _routelogic_obj.sindexerMatch(userid_name, sched_cat) if sindex_list: sindex = sindex_list[0] schedMaster = _routelogic_obj.schedmaster_map_list[sindex]['schedmaster_obj'] if len(sindex_list) > 1: logging.warning("RouteProcess:get_xls, multiple indexermatch=%s" % (sindex_list,)) else: schedMaster = None #raise CodeLogicError("RouteProcess:get_xls, No indexermatch with routelogic=%s" # % (_routelogic_obj.schedmaster_map_list,)) if schedMaster is None: dbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, sched_cat) param = dbInterface.getschedule_param() #if db_type == 'rrdb': if sched_cat == "L": schedMaster = SchedMaster(mongoClient, userid_name, param['divdb_type'], param['divcol_name'], param['fieldcol_name'], schedcol_name, prefcol_name=param['prefcol_name'], conflictcol_name=param['conflictcol_name']) elif sched_cat == "T": schedMaster = TournSchedMaster(mongoClient, userid_name, param['divcol_name'], param['fieldcol_name'], schedcol_name) if not schedMaster.error_code: # save schedMaster to global obj to reuse on get_schedule #_routelogic_obj.schedmaster_map[userid_name] = schedMaster _routelogic_obj.schedmaster_map_list.append( {"userid_name":userid_name, "sched_cat":sched_cat, "schedmaster_obj":schedMaster}) else: del schedMaster if schedMaster.schedcol_name == schedcol_name: xls_exporter = schedMaster.xls_exporter if xls_exporter is None: xls_exporter = XLS_Exporter(schedcol_name, divinfo_tuple=schedMaster.divinfo_tuple, fieldinfo_tuple=schedMaster.fieldinfo_tuple, sdbInterface=schedMaster.sdbInterface) schedMaster.xls_exporter = xls_exporter #if db_type == 'tourndb': if sched_cat == "T": tourn_type = request.query.tourn_type file_list = xls_exporter.export(genxls_id, db_type, tourn_type=tourn_type) else: file_list = xls_exporter.export(genxls_id, db_type) return_dict = {'file_list':file_list} else: return_dict = {} a = json.dumps(return_dict) return callback_name+'('+a+')'
def select_db_interface(userid_name, db_type, colname, sched_cat): if db_type == 'rrdb': dbInterface = RRDBInterface(mongoClient, userid_name, colname, sched_cat) elif db_type == 'tourndb': dbInterface = TournDBInterface(mongoClient, userid_name, colname, sched_cat) elif db_type == 'fielddb': dbInterface = FieldDBInterface(mongoClient, userid_name, colname, sched_cat) elif db_type == 'newscheddb': dbInterface = SchedDBInterface(mongoClient, userid_name, colname, sched_cat) elif db_type == 'prefdb': dbInterface = PrefDBInterface(mongoClient, userid_name, colname, sched_cat) elif db_type == 'teamdb': dbInterface = TeamDBInterface(mongoClient, userid_name, colname, sched_cat) elif db_type == 'conflictdb': dbInterface = ConflictDBInterface(mongoClient, userid_name, colname, sched_cat) else: raise CodeLogicError( "leaguedivprocess:select_db_interface: db_type not recognized db_type=%s" % (db_type, )) dbInterface = None return dbInterface
def __init__(self, mongoClient, userid_name, divcol_name, fieldcol_name, schedcol_name, tourn_type='RR'): self._error_code = 0x0 self.userid_name = userid_name self.sdbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, "T") self.tourn_type = tourn_type self.totalmatch_list = list() # db_type is for the divinfo schedule attached to the fielddb spec dbInterface = TournDBInterface(mongoClient, userid_name, divcol_name, "T") dbtuple = dbInterface.readDBraw() if dbtuple.config_status == 1: self.divinfo_list = dbtuple.list self.divinfo_indexerGet = lambda x: dict((p['tourndiv_id'],i) for i,p in enumerate(self.divinfo_list)).get(x) self.divinfo_tuple = _List_Indexer(self.divinfo_list, self.divinfo_indexerGet) else: self.divinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schemaster:init: div config not complete=%s" % (divcol_name,)) self._error_code |= DIVCONFIG_INCOMPLETE_MASK # get field information fdbInterface = FieldDBInterface(mongoClient, userid_name, fieldcol_name, "T") fdbtuple = fdbInterface.readDBraw(); if fdbtuple.config_status == 1: self.fieldinfo_list = fdbtuple.list self.fieldinfo_indexerGet = lambda x: dict((p['field_id'],i) for i,p in enumerate(self.fieldinfo_list)).get(x) self.fieldinfo_tuple = _List_Indexer(self.fieldinfo_list, self.fieldinfo_indexerGet) else: self.fieldinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schedmaster:init: field config not complete=%s" % (fieldcol_name,)) self._error_code |= FIELDCONFIG_INCOMPLETE_MASK # create list of div_ids that do not have a 'divfield_list' key divreqfields_list = [x[IDPROPERTY_str] for x in self.divinfo_list if 'divfield_list' not in x] # if there are div_id's with no 'divfield_list' key, create it if divreqfields_list: self.divfield_correlate(self.fieldinfo_list, dbInterface, divreqfields_list) else: self.simplifydivfield_list() if not self._error_code: self.sdbInterface.setschedule_param(DB_TYPE, divcol_name, fieldcol_name) self.fieldtimeScheduleGenerator = TournamentFieldTimeScheduleGenerator( dbinterface=self.sdbInterface, divinfo_tuple=self.divinfo_tuple, fieldinfo_tuple=self.fieldinfo_tuple, tourn_type=self.tourn_type) self.schedcol_name = schedcol_name self._xls_exporter = None
class TournSchedMaster(object): def __init__(self, mongoClient, userid_name, divcol_name, fieldcol_name, schedcol_name, tourn_type='RR'): self._error_code = 0x0 self.userid_name = userid_name self.sdbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, "T") self.tourn_type = tourn_type self.totalmatch_list = list() # db_type is for the divinfo schedule attached to the fielddb spec dbInterface = TournDBInterface(mongoClient, userid_name, divcol_name, "T") dbtuple = dbInterface.readDBraw() if dbtuple.config_status == 1: self.divinfo_list = dbtuple.list self.divinfo_indexerGet = lambda x: dict((p['tourndiv_id'],i) for i,p in enumerate(self.divinfo_list)).get(x) self.divinfo_tuple = _List_Indexer(self.divinfo_list, self.divinfo_indexerGet) else: self.divinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schemaster:init: div config not complete=%s" % (divcol_name,)) self._error_code |= DIVCONFIG_INCOMPLETE_MASK # get field information fdbInterface = FieldDBInterface(mongoClient, userid_name, fieldcol_name, "T") fdbtuple = fdbInterface.readDBraw(); if fdbtuple.config_status == 1: self.fieldinfo_list = fdbtuple.list self.fieldinfo_indexerGet = lambda x: dict((p['field_id'],i) for i,p in enumerate(self.fieldinfo_list)).get(x) self.fieldinfo_tuple = _List_Indexer(self.fieldinfo_list, self.fieldinfo_indexerGet) else: self.fieldinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schedmaster:init: field config not complete=%s" % (fieldcol_name,)) self._error_code |= FIELDCONFIG_INCOMPLETE_MASK # create list of div_ids that do not have a 'divfield_list' key divreqfields_list = [x[IDPROPERTY_str] for x in self.divinfo_list if 'divfield_list' not in x] # if there are div_id's with no 'divfield_list' key, create it if divreqfields_list: self.divfield_correlate(self.fieldinfo_list, dbInterface, divreqfields_list) else: self.simplifydivfield_list() if not self._error_code: self.sdbInterface.setschedule_param(DB_TYPE, divcol_name, fieldcol_name) self.fieldtimeScheduleGenerator = TournamentFieldTimeScheduleGenerator( dbinterface=self.sdbInterface, divinfo_tuple=self.divinfo_tuple, fieldinfo_tuple=self.fieldinfo_tuple, tourn_type=self.tourn_type) self.schedcol_name = schedcol_name self._xls_exporter = None @property def xls_exporter(self): return self._xls_exporter @xls_exporter.setter def xls_exporter(self, value): self._xls_exporter = value @property def error_code(self): return self._error_code ''' Generate either RoundRobin-format preliminary rounds, or elimination rounds ''' def schedGenerate(self): if self.tourn_type == 'RR': return self.prepGenerate() elif self.tourn_type == 'elimination': return self.elimGenerate() def get2015_U10_team_ids(self, tourndiv_id, bracket_id): bracket_list = [ {'tourndiv_id': 2, 'team_ids': [{'team_id_list': [7,9,11,19], 'bracket_id': 2}, {'team_id_list': [13,14,6,1], 'bracket_id': 3}, {'team_id_list': [3,4,18,17], 'bracket_id': 4}, {'team_id_list': [15,16,20,8], 'bracket_id': 5}, {'team_id_list': [5,10,12,2], 'bracket_id': 1}]}, {'tourndiv_id': 1, 'team_ids': [{'team_id_list': [2,5,9,13,17,21], 'bracket_id': 6}, {'team_id_list': [1,4,10,15], 'bracket_id': 5}, {'team_id_list': [6,7,14,22], 'bracket_id': 4}, {'team_id_list': [3,8,19,26], 'bracket_id': 3}, {'team_id_list': [12,25,18,23], 'bracket_id': 2}, {'team_id_list': [16,11,20,24], 'bracket_id': 1}]}] tindexerGet = lambda x: dict((p['tourndiv_id'],i) for i,p in enumerate(bracket_list)).get(x) team_ids = bracket_list[tindexerGet(tourndiv_id)]['team_ids'] bindexerGet = lambda x: dict((p['bracket_id'],i) for i,p in enumerate(team_ids)).get(x) return team_ids[bindexerGet(bracket_id)]['team_id_list'] def prepGenerate(self): totalmatch_list = list() for divinfo in self.divinfo_list: tourndiv_id = divinfo[IDPROPERTY_str] print tourndiv_id totalteams = divinfo['totalteams'] team_list = self.getTeamID_list(totalteams) totalgamedays = divinfo['totalgamedays'] minbracket_size = totalgamedays+1 brackets_num = totalteams / minbracket_size running_index = 0 #bracket_team_list = list() index = 0 match_list = list() for bracket_id in range(1, brackets_num+1): if totalteams - (index+minbracket_size) >= minbracket_size: bracket_size = minbracket_size else: bracket_size = totalteams - index running_index += bracket_size if tourndiv_id <= 2: team_id_list = self.get2015_U10_team_ids(tourndiv_id, bracket_id) else: team_id_list = team_list[index:running_index] if bracket_size != len(team_id_list): raise CodeLogicError('tournschedmaster:bracketsze mismatch div %d bracketsize %d team_id_list %s' % (tourndiv_id, bracket_size, team_id_list)) # calculate virtual number of game days required as parameter for # MatchGenerator object. Value is equal to #games if #teams is even, # if odd, add one to #games. vgames_num = totalgamedays if bracket_size%2==0 else totalgamedays+1 match = MatchGenerator(bracket_size, vgames_num, games_per_team=totalgamedays) bracket_match_list = match.generateMatchList( teamid_map=team_id_list) match_list.append(bracket_match_list) #bracket_team_list.append(bracket_dict) index = running_index totalmatch_list.append({IDPROPERTY_str: divinfo[IDPROPERTY_str], 'match_list':match_list, 'max_round':vgames_num}) status = self.fieldtimeScheduleGenerator.generateSchedule(totalmatch_list) return 1 if status else 0 def elimGenerate(self): match_id_count = _ELIM_MATCH_ID_START totalmatch_list = list() for divinfo in self.divinfo_list: match_id_begin = match_id_count div_name = divinfo['div_age'] + divinfo['div_gen'] elimination_type = divinfo['elimination_type'] thirdplace_enable = divinfo['thirdplace_enable'] # assign bracket type (Winners or Losers) btype = 'W' totalteams = divinfo['totalteams'] team_id_list = range(1,totalteams+1) # total number of rounds for winner's bracket # does not include championship game for double elimination format totalwrounds =int(ceil(_logbase2(totalteams))) maxpower2 = pow(2, totalwrounds) div_id = divinfo[IDPROPERTY_str] match_list = [] divmatch_list = [] absround_id = 0 for round_id in range(1, totalwrounds+1): absround_id += 1 if round_id == 1: r1bye_num = maxpower2 - totalteams roundteams_num = totalteams - r1bye_num seed_id_list = team_id_list[-roundteams_num:] rteam_list = ['S'+str(s) for s in seed_id_list] cumulative_list = [] cindexerGet = None else: roundteams_num = maxpower2/pow(2, round_id-1) seed_id_list = range(1, roundteams_num+1) # rm_list is from previous round, make sure to call this before # rmatch_dict in this round rm_list = rmatch_dict['match_list'] rmindexerGet = lambda x: dict((p['next_w_seed'],i) for i,p in enumerate(rm_list)).get(x) # assign team ids to the seeded team list for the current round rteam_list = [rm_list[rmindexerGet(s)]['next_w_id'] if s in carryseed_list else 'S'+str(s) for s in seed_id_list] cumulative_list = [{'match_id':x['match_id'], 'cumulative':x['cumulative']} for x in rm_list] cindexerGet = lambda x: dict((p['match_id'],i) for i,p in enumerate(cumulative_list)).get(x) # provide round sequencing information that will be usefol for # fieldtime scheduling logging.debug("elimsched:gen:**************") logging.debug("elimsched:gen:round %d rteam %s", round_id, rteam_list) print 'round rteam', round_id, rteam_list # control number to determine pairings is the sum of the highest # and lowest seed number of teams playing in round 1 numgames = roundteams_num/2 # ref http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python # for flattening two-deep and regular nested lists lseed_list = self.generate_lseed_list(round_id, seed_id_list) rmatch_dict = {'round_id': round_id, 'btype':btype, 'absround_id': absround_id, 'numgames':numgames, 'depend':0, 'div_id':div_id, 'match_list': [{'home':rteam_list[x],'away':rteam_list[-x-1], 'div_id':div_id, 'cumulative':[rteam_list[y] if rteam_list[y][0]=='S' else self.getCumulative_teams(cumulative_list, cindexerGet,rteam_list[y][1:]) for y in (x,-x-1)], 'next_w_seed':seed_id_list[x], 'next_l_seed':lseed_list[x], 'next_w_id':'W'+str(match_id_count+x+1), 'next_l_id':'L'+str(match_id_count+x+1), 'match_id':match_id_count+x+1, 'comment':"", 'round':btype + str(round_id)} for x in range(numgames) ] } logging.debug("elimsched:gen: div %d round %d", div_id, round_id) logging.debug("elimsched:gen: seed list %s lseed %s", seed_id_list, lseed_list) for rm in rmatch_dict['match_list']: logging.debug("elimsched:gen: rm %s", rm) match_list.append(rmatch_dict) # slicing for copying is fastest according to # http://stackoverflow.com/questions/2612802/how-to-clone-a-list-in-python/2612810 carryseed_list = [x['next_w_seed'] for x in rmatch_dict['match_list']] match_id_count += len(rmatch_dict['match_list']) if elimination_type != 'D': # championship game for single and consolation (modified # double) elimination tournament - winner of winner's bracket # is overall champ. For double elimination winner of winner's # bracket still has to play the winner of the consolaton bracket # which is covered below rmatch_dict['match_list'][0]['comment'] = div_name + ' Championship Game' rmatch_dict['match_list'][0]['round'] = 'Champ' divmatch_list.append({'div_id': divinfo[IDPROPERTY_str], 'elimination_type':elimination_type, 'btype':btype, 'divmatch_list':match_list, 'max_round':totalwrounds, 'totalteams':totalteams}) if totalteams > 2: if elimination_type == 'S' and thirdplace_enable == 'Y': # generate third place game for single elimination match_id_count = self.createThirdPlaceMatch(div_id, match_list, totalwrounds, match_id_count, divmatch_list, div_name) if elimination_type in ['C', 'D']: (match_id_count, c_absround_id) = self.createConsolationRound(div_id, match_list,totalwrounds, match_id_count, elimination_type, divmatch_list) if elimination_type =='D': # final game for double eliminatio game rm_list = rmatch_dict['match_list'] rmindexerGet = lambda x: dict((p['next_w_seed'],i) for i,p in enumerate(rm_list)).get(x) rteam = rm_list[rmindexerGet(carryseed_list[0])]['next_w_id'] nextabsround_id = max(rmatch_dict['absround_id'], c_absround_id) + 1 rmatch_dict = {'round_id': round_id+1, 'absround_id': nextabsround_id, 'btype':btype, 'numgames':1, 'depend':0, 'div_id':div_id, 'match_list': [{'home':rteam, 'away':'W'+str(match_id_count), 'div_id':div_id, 'match_id':match_id_count+1, 'round': 'Champ', 'comment':'Championship Game (1st Winner bracket vs 1st place Loser/Repcharge bracket'}]} match_list.append(rmatch_dict) match_id_count += 1 else: logging.warning("elimsched:gen: there should at least be three teams in div %d to make scheduling meaningful", div_id) totalmatch_list.append({'div_id':div_id, 'divmatch_list':self.addOverallRoundID(divmatch_list, totalteams, elimination_type), 'match_id_range':(match_id_begin+1, match_id_count)}) pprint(totalmatch_list) status = self.fieldtimeScheduleGenerator.generateElimSchedule(totalmatch_list) #elim_ftscheduler = EliminationFieldTimeScheduler(self.tdbInterface, self.tfield_tuple, self.tourn_divinfo, self.tindexerGet) #elim_ftscheduler.generateSchedule(self.totalmatch_list) return 1 if status else 0 def getCumulative_teams(self, clist, cGet, match_id): if clist: # generator created - watch out for iterating through gen return list(flatten(clist[cGet(int(match_id))] ['cumulative'])) else: return None def generate_lseed_list(self, round_id, seed_list): # create seeding list for losing bracket # reverse order of incoming seeding list such that returned list can also # be accessed starting index 0 (instead of index -1) slen = len(seed_list) if slen % 2: raise CodeLogicError("elimsched:genlseed: seed list should be even") ls_list = seed_list[::-1] if round_id % 2 == 0: # if round is an even number, then swap adjacent positions - # index 0,1 <-> index 1,0; index 2,3 <->index 3,2, etc index = 0 while index < slen: ls_list[index], ls_list[index+1] = ls_list[index+1], ls_list[index] index += 2 return ls_list def createConsolationRound(self, div_id, match_list, wr_total, match_id_count, elimination_type, divmatch_list): # create the seed list for the consolation matches by getting # the 'losing' seed number from the previous round # x in [-1,-2] intended to get last and second-to-last match_list # we're assuming wr_total (rounds) has at least 2 rounds (4 teams) cmatch_list = [] cround_id = 1 rm_list = [] btype = 'L' while True: if cround_id == 1: wr_round = 2 # use range if tuple needs to represent range cinitindex_tuple = (0,wr_round-1) # get info for losing teams from the first two elimination rounds # List of tuples - tuple(team id, seed, absround_id) ctuple_list = [(y['next_l_id'],y['next_l_seed'], match_list[x]['absround_id']) for x in cinitindex_tuple for y in match_list[x]['match_list']] ctuple_list.sort(key=itemgetter(1)) wr12_losing_teams = len(ctuple_list) #min_seed = ctuple_list[0][1] logging.debug("elimsched:createConsol: INIT ctuple %s INIT losing teams %d", ctuple_list, wr12_losing_teams) # get power of 2 greater than #teams cpower2 = pow(2, int(ceil(_logbase2(wr12_losing_teams)))) c1bye_num = cpower2 - wr12_losing_teams nt = wr12_losing_teams - c1bye_num seed_id_list = [x[1] for x in ctuple_list[-nt:]] rteam_list = [x[0] for x in ctuple_list[-nt:]] remain_ctuple_list = ctuple_list[0:-nt] # 2-index element of three-tuple x is the absround_id for that # team_id's last match # calculate the next absround_id, by getting the max of the current # absround_id for each team id (designated by W/L of match_id) and # adding 1 nextabsround_id = max([x[2] for x in ctuple_list[-nt:]])+1 logging.debug("elimsched:createConsol: INIT cpower2 %d cbye %d nt %d seed list %s rteam %s remain %s", cpower2, c1bye_num, nt, seed_id_list, rteam_list, remain_ctuple_list) # for cround 1, cumulative list will be made up exclusively from passed match list cumulative_list = [{'match_id':y['match_id'], 'cumulative':y['cumulative']} for x in cinitindex_tuple for y in match_list[x]['match_list']] # save it for use in later rounds wr_cumulative_list = cumulative_list[:] else: rmindexerGet = lambda x: dict((p['next_w_seed'],i) for i,p in enumerate(rm_list)).get(x) # for cround 2 and above, cumulative list will at include info from # prev cround match list; in addition it may also utiliz match list # info from previous wround match lists cumulative_list = [{'match_id':x['match_id'], 'cumulative':x['cumulative']} for x in rm_list] if remain_ctuple_list: cpower2 /= 2 if cpower2 == len(remain_ctuple_list): # if an equal number of teams have come in from the wround # bring the effective number back up cpower2 *= 2 wr_round += 1 # for depend dict key remain_seed_list = [x[1] for x in remain_ctuple_list] seed_id_list = carryseed_list + remain_seed_list seed_id_list.sort() rcindexerGet = lambda x: dict((p[1],i) for i,p in enumerate(remain_ctuple_list)).get(x) rteam_list = [rm_list[rmindexerGet(s)]['next_w_id'] if s in carryseed_list else remain_ctuple_list[rcindexerGet(s)][0] for s in seed_id_list] # calculate next absround_id by taking the max out of all # absround_id's attached to effective team_id's feeding into # this round, and then incrementing by one nextabsround_id = max( [nextabsround_id if s in carryseed_list else remain_ctuple_list[rcindexerGet(s)][2] for s in seed_id_list]) + 1 remain_ctuple_list = [] # adding the whole wround cumu list is overkill, but simple cumulative_list += wr_cumulative_list else: # increment incoming source round id from winning bracket wr_round += 1 wr_ind = wr_round-1 # get default bracket size in current round cpower2 /= 2 # get losing team information from current source winner # round if wr_round <= wr_total: ctuple_list = [(y['next_l_id'], y['next_l_seed'], match_list[wr_ind]['absround_id']) for y in match_list[wr_ind]['match_list']] cindexerGet = lambda x: dict((p[1],i) for i,p in enumerate(ctuple_list)).get(x) wr_cumulative_list = [{'match_id':y['match_id'], 'cumulative':y['cumulative']} for y in match_list[wr_ind]['match_list']] # get number of incoming teams from winner'sbracket wr_nt = len(ctuple_list) if wr_nt == len(carryseed_list): # bring the default bracket size back up because of increase # in additional games cpower2 *= 2 incoming_seed_list = [x[1] for x in ctuple_list] seed_id_list = carryseed_list + incoming_seed_list seed_id_list.sort() rteam_list = [rm_list[rmindexerGet(s)]['next_w_id'] if s in carryseed_list else ctuple_list[cindexerGet(s)][0] for s in seed_id_list] nextabsround_id = max( [nextabsround_id if s in carryseed_list else ctuple_list[cindexerGet(s)][2] for s in seed_id_list]) + 1 cumulative_list += wr_cumulative_list elif wr_nt == len(carryseed_list)/2: # if only half the number of games are coming in, save for # the next cround. For now, implement a round from previous # winners. remain_ctuple_list = ctuple_list[:] seed_id_list = carryseed_list seed_id_list.sort() rteam_list = [rm_list[rmindexerGet(s)]['next_w_id'] for s in seed_id_list] nextabsround_id += 1 wr_round -= 1 # for depend key used for ordering else: # if there are any other number of teams coming in from the # wround, raise exception. It might be a legitimate case, # so analyze crash. raise CodeLogicError("elimsched:createConsole: consolation bracket design assumptions not correct div=%d" % (div_id,)) else: seed_id_list = carryseed_list seed_id_list.sort() rteam_list = [rm_list[rmindexerGet(s)]['next_w_id'] for s in seed_id_list] if elimination_type == 'D' and cpower2 == 1: cround_id -= 1 #decrement cround_id to recover last cround break nt = cpower2 logging.debug("elimsched:createConsole: cround %d cpower %d nt %d seed %s rteamlist %s", cround_id, cpower2, nt, seed_id_list, rteam_list) logging.debug("elimsched:createConsole: cumu %s", cumulative_list) numgames = nt/2 # get list of cumulative team field for all the match sources that will be used in this round. # the match sources for the consolation round will be drawn fro previous consolation rounds and also winner rounds # for the first round, the match sources will be drawn from the winning bracket matches cindexerGet = lambda x: dict((p['match_id'],i) for i,p in enumerate(cumulative_list)).get(x) #self.checkRepeatOpponent(cumulative_list, cindexerGet, rteam_list) rmatch_dict = {'round_id': cround_id, 'btype':btype, 'absround_id': nextabsround_id, 'numgames':numgames, 'depend':wr_round, 'div_id':div_id, 'match_list': [{'home':rteam_list[x], 'away':rteam_list[-x-1], 'div_id':div_id, 'cumulative':[self.getCumulative_teams(cumulative_list, cindexerGet,rteam_list[y][1:]) for y in (x,-x-1)], 'next_w_seed':seed_id_list[x], 'next_l_seed':seed_id_list[-x-1], 'next_w_id':'W'+str(match_id_count+x+1), 'next_l_id':'L'+str(match_id_count+x+1), 'match_id':match_id_count+x+1, 'comment':"", 'round':btype + str(cround_id)} for x in range(numgames)]} logging.debug("elimsched:createConsole&&&&&&&&&&&&&&&&") logging.debug("elimsched:createConsole: Consolation div %d round %d", div_id, cround_id) for rm in rmatch_dict['match_list']: logging.debug("elimsched:createConsole: match %s", rm) cmatch_list.append(rmatch_dict) rm_list = rmatch_dict['match_list'] carryseed_list = [x['next_w_seed'] for x in rm_list] logging.debug("elimsched.createConsole: carryseed %s", carryseed_list) match_id_count += len(rm_list) if elimination_type == 'C' and cpower2 == 2: rmatch_dict['match_list'][0]['comment'] = "3rd Place Game" rmatch_dict['match_list'][0]['round'] = "3rd Place" break else: cround_id += 1 logging.debug("elimsched:createConsole: div %d consolation sched complete; last match_id = %d", div_id, match_id_count) divmatch_list.append({'div_id': div_id, 'elimination_type':elimination_type, 'btype':btype, 'divmatch_list':cmatch_list, 'max_round':cround_id}) # return last absround_id as well as last match_id_count return (match_id_count, nextabsround_id) def createThirdPlaceMatch(self, div_id, amatch_list, max_round_id, match_id_count, divmatch_list, div_name): # generate 3rd place match - just take championship match # and replace W match_id identifiers with 'L' prefix # round_id, absround_id stays the same as the championship match. # Only applicable for single elimination matches as third place # can be automatically determined for double or consolation tournaments. mindexerMatch = lambda x,y:[i for i,p in enumerate( amatch_list) if p['div_id']==x and p['round_id']==y] mindex = mindexerMatch(div_id, max_round_id)[0] rmatch_dict = amatch_list[mindex] match_list = rmatch_dict['match_list'] champ_match = match_list[0] third_pl_match = dict() third_pl_match['div_id'] = div_id third_pl_match['away'] = champ_match['away'].replace('W', 'L') third_pl_match['home'] = champ_match['home'].replace('W', 'L') third_pl_match['round'] = "3rdPlace" third_pl_match['comment'] = div_name + " 3rd Place Match" match_id_count += 1 third_pl_match['match_id'] = match_id_count match_list.append(third_pl_match) rmatch_dict['numgames'] += 1 pprint(rmatch_dict) return match_id_count ''' sort match list according to absround_id ''' def addOverallRoundID(self, adivmatch_list, totalteams, elimination_type): adjustedmatch_list = [{'div_id':dkey, 'divmatch_list':[x['divmatch_list'] for x in ditems]} for dkey, ditems in groupby(adivmatch_list,key=itemgetter('div_id'))] # adjustedmatch_list should only have one element per div # (and there will only be one div, so adjustedmatch_list will be # a single element array) divmatch_list = adjustedmatch_list[0]['divmatch_list'] multiplex_match_list = list(roundrobin(divmatch_list)) return sorted(multiplex_match_list, key=itemgetter('absround_id', 'btype')) '''function to add fields key to divinfo_list. Supersedes global function (unnamed) in leaguedivprep''' def divfield_correlate(self, fieldinfo_list, dbInterface, div_list): # use set to keep track of unique div_id's that have fields attached to them divset = set() for fieldinfo in fieldinfo_list: field_id = fieldinfo['field_id'] for div_id in fieldinfo['primaryuse_list']: if div_id in div_list: index = self.divinfo_indexerGet(div_id) if index is not None: divset.add(div_id) divinfo = self.divinfo_list[index] # check existence of key 'divfield_list' - if it exists, append to list of fields, if not create if 'divfield_list' in divinfo: divinfo['divfield_list'].append(field_id) else: divinfo['divfield_list'] = [field_id] def simplifydivfield_list(self): ''' create a simplified version of the divfield_list which only includes the field_id's - the only value necessary for schedule creation; ignore other items such as field name ''' for divinfo in self.divinfo_list: if 'divfield_list' in divinfo: new_list = [x['field_id'] for x in divinfo['divfield_list']] divinfo['divfield_list'] = new_list def get_schedule(self, idproperty, propid, div_age="", div_gen=""): if idproperty == 'tourndiv_id': divinfo = self.divinfo_list[self.divinfo_indexerGet(propid)] game_list = self.sdbInterface.get_schedule(idproperty, div_age=divinfo['div_age'], div_gen=divinfo['div_gen']) # also get fields info tied to div fieldname_dict= {x:self.fieldinfo_list[self.fieldinfo_indexerGet(x)]['field_name'] for x in divinfo['divfield_list']} return {'game_list':game_list, 'fieldname_dict':fieldname_dict} elif idproperty == 'field_id': fieldinfo = self.fieldinfo_list[self.fieldinfo_indexerGet(propid)] game_list = self.sdbInterface.get_schedule(idproperty, field_id=fieldinfo['field_id']) return {'game_list':game_list} elif idproperty == 'team_id': game_list = self.sdbInterface.get_schedule(idproperty, team_id=propid, div_age=div_age, div_gen=div_gen) return {'game_list':game_list} elif idproperty == 'fair_id': # get fairness metrics # get divinfo as fairness metric calculations require divinfo # parameters divinfo = self.divinfo_list[self.divinfo_indexerGet(propid)] metrics_list = self.sdbInterface.get_schedule(idproperty, div_age=div_age, div_gen=div_gen, divinfo=divinfo, fieldinfo_tuple=self.fieldinfo_tuple) divfield_list = [{'field_id':x, 'field_name':self.fieldinfo_list[self.fieldinfo_indexerGet(x)]['field_name']} for x in divinfo['divfield_list']] return {'metrics_list':metrics_list, 'divfield_list':divfield_list} def getTeamID_list(self, teams_num): team_id_list = range(1,teams_num+1) # ref http://docs.python.org/2/library/random.html#random.shuffle # doc above for random shuffle (e.g. for an) # start the seed with same number so random functions generates # same resuts from run to run/ seed(0) shuffle(team_id_list) return team_id_list
def __init__(self, mongoClient, userid_name, db_type, divcol_name, fieldcol_name, schedcol_name, prefcol_name=None, conflictcol_name=None): self._error_code = 0x0 self._error_message = "" self.userid_name = userid_name self.sdbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, "L") # db_type is for the divinfo schedule attached to the fielddb spec if db_type == 'rrdb': dbInterface = RRDBInterface(mongoClient, userid_name, divcol_name, "L") elif db_type == 'tourndb': dbInterface = TournDBInterface(mongoClient, userid_name, divcol_name, "L") else: raise CodeLogicError("schemaster:init: db_type not recognized db_type=%s" % (db_type,)) dbtuple = dbInterface.readDBraw() if dbtuple.config_status == 1: self.oddnumplay_mode = dbtuple.oddnum_mode self.divinfo_list = dbtuple.list self.divinfo_indexerGet = lambda x: dict((p['div_id'],i) for i,p in enumerate(self.divinfo_list)).get(x) self.divinfo_tuple = _List_Indexer(self.divinfo_list, self.divinfo_indexerGet) else: self.divinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schemaster:init: div config not complete=%s" % (divcol_name,)) self._error_code |= DIVCONFIG_INCOMPLETE_MASK self.oddnumplay_mode = 0 # get field information fdbInterface = FieldDBInterface(mongoClient, userid_name, fieldcol_name, "L") fdbtuple = fdbInterface.readDBraw(); if fdbtuple.config_status == 1: self.fieldinfo_list = fdbtuple.list self.fieldinfo_indexerGet = lambda x: dict((p['field_id'],i) for i,p in enumerate(self.fieldinfo_list)).get(x) self.fieldinfo_tuple = _List_Indexer(self.fieldinfo_list, self.fieldinfo_indexerGet) else: self.fieldinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schedmaster:init: field config not complete=%s" % (fieldcol_name,)) self._error_code |= FIELDCONFIG_INCOMPLETE_MASK # create list of div_ids that do not have a 'divfield_list' key divreqfields_list = [x['div_id'] for x in self.divinfo_list if 'divfield_list' not in x] # if there are div_id's with no 'divfield_list' key, create it if divreqfields_list: self.divfield_correlate(self.fieldinfo_list, dbInterface, divreqfields_list) else: self.simplifydivfield_list() # get team-related field affinity information, if any # use divcol_name as it shares collection name w divinfo tmdbInterface = TeamDBInterface(mongoClient, userid_name, divcol_name, "L") if tmdbInterface.check_docexists(): tmdbtuple = tmdbInterface.readDBraw() # recreate tminfo_list from db read, but leave out fields such as # team name which is not needed for schedule generation tminfo_tuple = self.get_team_field_affinity_params(tmdbtuple) tmprefdays_tuple = self.get_team_preference_params(tmdbtuple) else: tminfo_tuple = None tmprefdays_tuple = None # get pref list information, if any if prefcol_name: # preference list use is optional - only process if preference list # exists pdbInterface = PrefDBInterface(mongoClient, userid_name, prefcol_name, "L") pdbtuple = pdbInterface.readDBraw(); if pdbtuple.config_status == 1: prefinfo_list = pdbtuple.list prefinfo_indexerGet = lambda x: dict((p['pref_id'],i) for i,p in enumerate(prefinfo_list)).get(x) prefinfo_indexerMatch = lambda x: [i for i,p in enumerate(prefinfo_list) if p['div_id'] == x] prefinfo_triple = _List_IndexerGM(prefinfo_list, prefinfo_indexerGet, prefinfo_indexerMatch) else: prefinfo_triple = None # raise error as client should only be displaying in select widget # conflict lists that have config status complete raise CodeLogicError("schedmaster:init: pref config not complete=%s" % (prefcol_name,)) else: pdbInterface = None prefinfo_triple = None if conflictcol_name: cdbInterface = ConflictDBInterface(mongoClient, userid_name, conflictcol_name, "L") cdbtuple = cdbInterface.readDBraw() if cdbtuple.config_status == 1: conflictinfo_list = cdbtuple.list else: conflictinfo_list = None raise CodeLogicError("schedmaster:init: conflict config not complete=%s" % (conflictcol_name,)) else: conflictinfo_list = None cdbInterface = None #conflictinfo_tuple = None if self.divinfo_tuple.dict_list and prefinfo_triple and prefinfo_triple.dict_list: if not self.consistency_check(prefinfo_triple.dict_list): self._error_code |= PREFINFODATE_ERROR_MASK if not self._error_code: self.sdbInterface.setschedule_param(db_type, divcol_name, fieldcol_name, prefcol_name=prefcol_name, conflictcol_name=conflictcol_name) try: self.fieldtimeScheduleGenerator = FieldTimeScheduleGenerator( dbinterface=self.sdbInterface, divinfo_tuple=self.divinfo_tuple, fieldinfo_tuple=self.fieldinfo_tuple, prefinfo_triple=prefinfo_triple, pdbinterface=pdbInterface, tminfo_tuple=tminfo_tuple, conflictinfo_list=conflictinfo_list, cdbinterface=cdbInterface, tmprefdays_tuple=tmprefdays_tuple) except KeyError: self._error_code |= FIELDCONFIG_INCOMPLETE_MASK self._error_message += "Reselect Division Configuration in Field Configuration UI" self.schedcol_name = schedcol_name self._xls_exporter = None
class SchedMaster(object): def __init__(self, mongoClient, userid_name, db_type, divcol_name, fieldcol_name, schedcol_name, prefcol_name=None, conflictcol_name=None): self._error_code = 0x0 self._error_message = "" self.userid_name = userid_name self.sdbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, "L") # db_type is for the divinfo schedule attached to the fielddb spec if db_type == 'rrdb': dbInterface = RRDBInterface(mongoClient, userid_name, divcol_name, "L") elif db_type == 'tourndb': dbInterface = TournDBInterface(mongoClient, userid_name, divcol_name, "L") else: raise CodeLogicError("schemaster:init: db_type not recognized db_type=%s" % (db_type,)) dbtuple = dbInterface.readDBraw() if dbtuple.config_status == 1: self.oddnumplay_mode = dbtuple.oddnum_mode self.divinfo_list = dbtuple.list self.divinfo_indexerGet = lambda x: dict((p['div_id'],i) for i,p in enumerate(self.divinfo_list)).get(x) self.divinfo_tuple = _List_Indexer(self.divinfo_list, self.divinfo_indexerGet) else: self.divinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schemaster:init: div config not complete=%s" % (divcol_name,)) self._error_code |= DIVCONFIG_INCOMPLETE_MASK self.oddnumplay_mode = 0 # get field information fdbInterface = FieldDBInterface(mongoClient, userid_name, fieldcol_name, "L") fdbtuple = fdbInterface.readDBraw(); if fdbtuple.config_status == 1: self.fieldinfo_list = fdbtuple.list self.fieldinfo_indexerGet = lambda x: dict((p['field_id'],i) for i,p in enumerate(self.fieldinfo_list)).get(x) self.fieldinfo_tuple = _List_Indexer(self.fieldinfo_list, self.fieldinfo_indexerGet) else: self.fieldinfo_tuple = _List_Indexer(None, None) raise CodeLogicError("schedmaster:init: field config not complete=%s" % (fieldcol_name,)) self._error_code |= FIELDCONFIG_INCOMPLETE_MASK # create list of div_ids that do not have a 'divfield_list' key divreqfields_list = [x['div_id'] for x in self.divinfo_list if 'divfield_list' not in x] # if there are div_id's with no 'divfield_list' key, create it if divreqfields_list: self.divfield_correlate(self.fieldinfo_list, dbInterface, divreqfields_list) else: self.simplifydivfield_list() # get team-related field affinity information, if any # use divcol_name as it shares collection name w divinfo tmdbInterface = TeamDBInterface(mongoClient, userid_name, divcol_name, "L") if tmdbInterface.check_docexists(): tmdbtuple = tmdbInterface.readDBraw() # recreate tminfo_list from db read, but leave out fields such as # team name which is not needed for schedule generation tminfo_tuple = self.get_team_field_affinity_params(tmdbtuple) tmprefdays_tuple = self.get_team_preference_params(tmdbtuple) else: tminfo_tuple = None tmprefdays_tuple = None # get pref list information, if any if prefcol_name: # preference list use is optional - only process if preference list # exists pdbInterface = PrefDBInterface(mongoClient, userid_name, prefcol_name, "L") pdbtuple = pdbInterface.readDBraw(); if pdbtuple.config_status == 1: prefinfo_list = pdbtuple.list prefinfo_indexerGet = lambda x: dict((p['pref_id'],i) for i,p in enumerate(prefinfo_list)).get(x) prefinfo_indexerMatch = lambda x: [i for i,p in enumerate(prefinfo_list) if p['div_id'] == x] prefinfo_triple = _List_IndexerGM(prefinfo_list, prefinfo_indexerGet, prefinfo_indexerMatch) else: prefinfo_triple = None # raise error as client should only be displaying in select widget # conflict lists that have config status complete raise CodeLogicError("schedmaster:init: pref config not complete=%s" % (prefcol_name,)) else: pdbInterface = None prefinfo_triple = None if conflictcol_name: cdbInterface = ConflictDBInterface(mongoClient, userid_name, conflictcol_name, "L") cdbtuple = cdbInterface.readDBraw() if cdbtuple.config_status == 1: conflictinfo_list = cdbtuple.list else: conflictinfo_list = None raise CodeLogicError("schedmaster:init: conflict config not complete=%s" % (conflictcol_name,)) else: conflictinfo_list = None cdbInterface = None #conflictinfo_tuple = None if self.divinfo_tuple.dict_list and prefinfo_triple and prefinfo_triple.dict_list: if not self.consistency_check(prefinfo_triple.dict_list): self._error_code |= PREFINFODATE_ERROR_MASK if not self._error_code: self.sdbInterface.setschedule_param(db_type, divcol_name, fieldcol_name, prefcol_name=prefcol_name, conflictcol_name=conflictcol_name) try: self.fieldtimeScheduleGenerator = FieldTimeScheduleGenerator( dbinterface=self.sdbInterface, divinfo_tuple=self.divinfo_tuple, fieldinfo_tuple=self.fieldinfo_tuple, prefinfo_triple=prefinfo_triple, pdbinterface=pdbInterface, tminfo_tuple=tminfo_tuple, conflictinfo_list=conflictinfo_list, cdbinterface=cdbInterface, tmprefdays_tuple=tmprefdays_tuple) except KeyError: self._error_code |= FIELDCONFIG_INCOMPLETE_MASK self._error_message += "Reselect Division Configuration in Field Configuration UI" self.schedcol_name = schedcol_name self._xls_exporter = None @property def xls_exporter(self): return self._xls_exporter @xls_exporter.setter def xls_exporter(self, value): self._xls_exporter = value @property def error_code(self): return self._error_code def generate(self): totalmatch_list = [] totalbyeteam_list = list() for divinfo in self.divinfo_list: totalteams = divinfo['totalteams'] # possibly rename below to 'totalrounddays' as totalgamedays may # not match up to number of physical days totalgamedays = divinfo['totalgamedays'] div_id = divinfo['div_id'] if self.oddnumplay_mode == 1: games_per_team = totalgamedays else: games_per_team = totalgamedays if (totalteams * totalgamedays) % 2 == 0 else totalgamedays - 1 match = MatchGenerator(totalteams, totalgamedays, oddnumplay_mode=self.oddnumplay_mode, games_per_team=games_per_team) match_list = match.generateMatchList() args_obj = {'div_id':div_id, 'match_list':match_list, 'numgames_perteam_list':match.numgames_perteam_list, 'gameslots_perrnd_perdiv':match.gameslotsperday} totalmatch_list.append(args_obj) if self.oddnumplay_mode > 0 and match.byeteam_list: totalbyeteam_list.append({'div_id':div_id, 'byeteam_list':match.byeteam_list}) totalmatch_indexerGet = lambda x: dict((p['div_id'],i) for i,p in enumerate(totalmatch_list)).get(x) totalmatch_tuple = _List_Indexer(totalmatch_list, totalmatch_indexerGet) try: status = self.fieldtimeScheduleGenerator.generateSchedule( totalmatch_tuple, self.oddnumplay_mode, totalbyeteam_list) except ValueError: return {'status':0, 'error_code': GENERATE_ERROR_MASK, 'error_message': "Not enough available dates: Check Division MinMax Gap or Field Calendar Length" } except FieldAvailabilityError as e: return {'status':0, 'error_code': GENERATE_ERROR_MASK, 'error_message': 'Field space has been exhausted with divsion ' + str(e.div_id)} else: return {'status': 1} if status else {'status':0} '''function to add fields key to divinfo_list. Supersedes global function (unnamed) in leaguedivprep''' def divfield_correlate(self, fieldinfo_list, dbInterface, div_list): # use set to keep track of unique div_id's that have fields attached to them divset = set() for fieldinfo in fieldinfo_list: field_id = fieldinfo['field_id'] for div_id in fieldinfo['primaryuse_list']: if div_id in div_list: index = self.divinfo_indexerGet(div_id) if index is not None: divset.add(div_id) divinfo = self.divinfo_list[index] # check existence of key 'divfield_list' - if it exists, append to list of fields, if not create if 'divfield_list' in divinfo: divinfo['divfield_list'].append(field_id) else: divinfo['divfield_list'] = [field_id] def simplifydivfield_list(self): ''' create a simplified version of the divfield_list which only includes the field_id's - the only value necessary for schedule creation; ignore other items such as field name ''' for divinfo in self.divinfo_list: if 'divfield_list' in divinfo: new_list = [x['field_id'] for x in divinfo['divfield_list']] divinfo['divfield_list'] = new_list def get_schedule(self, idproperty, propid, div_age="", div_gen=""): if idproperty == 'div_id': divinfo = self.divinfo_list[self.divinfo_indexerGet(propid)] game_list = self.sdbInterface.get_schedule(idproperty, div_age=divinfo['div_age'], div_gen=divinfo['div_gen']) # also get fields info tied to div fieldname_dict= {x:self.fieldinfo_list[self.fieldinfo_indexerGet(x)]['field_name'] for x in divinfo['divfield_list']} return {'game_list':game_list, 'fieldname_dict':fieldname_dict} elif idproperty == 'field_id': fieldinfo = self.fieldinfo_list[self.fieldinfo_indexerGet(propid)] game_list = self.sdbInterface.get_schedule(idproperty, field_id=fieldinfo['field_id']) return {'game_list':game_list} elif idproperty == 'team_id': game_list = self.sdbInterface.get_schedule(idproperty, team_id=propid, div_age=div_age, div_gen=div_gen) return {'game_list':game_list} elif idproperty == 'fair_id': # get fairness metrics # get divinfo as fairness metric calculations require divinfo # parameters divinfo = self.divinfo_list[self.divinfo_indexerGet(propid)] metrics_list = self.sdbInterface.get_schedule(idproperty, div_age=div_age, div_gen=div_gen, divinfo=divinfo, fieldinfo_tuple=self.fieldinfo_tuple) divfield_list = [{'field_id':x, 'field_name':self.fieldinfo_list[self.fieldinfo_indexerGet(x)]['field_name']} for x in divinfo['divfield_list']] return {'metrics_list':metrics_list, 'divfield_list':divfield_list} def consistency_check(self, prefinfo_list): '''preference info date consistency check against calendar map''' cmap_list = list() cindexerGet = lambda x: dict((p['date'],i) for i,p in enumerate(cmap_list)).get(x) for prefinfo in prefinfo_list: game_date = parser.parse(prefinfo['game_date']) for fieldinfo in self.fieldinfo_list: cmap_list = fieldinfo['calendarmap_list'] index = cindexerGet(game_date) if index is not None: # match found, break break else: # no match found, go to next prefinfo continue # match already found, break from prefinfo loop break else: # prefinfo and fieldinfo list calendarmap not consistent return False # consistent, success return True def getHTMLTeamTable(self, div_age, div_gen, team_id): # https://pypi.python.org/pypi/html/ return_dict = self.get_schedule('team_id', team_id, div_age=div_age, div_gen=div_gen) game_list = return_dict['game_list'] html = HTML() table = html.table(width='100%', border='1px solid black') table.caption(self.userid_name+" "+self.schedcol_name+" "+div_age+div_gen+str(team_id)) header_row = table.tr header_row.th('Game Date', padding='5px') header_row.th('Start Time', padding='5px') header_row.th('Field', padding='5px') header_row.th('Home', padding='5px') header_row.th('Away', padding='5px') for game in game_list: game_row = table.tr game_row.td(game['game_date']) game_row.td(game['start_time']) findex = self.fieldinfo_indexerGet(game['venue']) if findex is not None: field_name = self.fieldinfo_list[findex]['field_name'] game_row.td(field_name) game_row.td(str(game['home'])) game_row.td(str(game['away'])) return str(html) def get_team_field_affinity_params(self, tmdbtuple): tminfo_list = [{'dt_id':x['dt_id'], 'div_id':x['div_id'], 'tm_id':x['tm_id'], 'af_list':x['af_list']} for x in tmdbtuple.list if x['af_list']] return self.get_indexer_tuple(tminfo_list) def get_team_preference_params(self, tmdbtuple): tminfo_list = [{'dt_id':x['dt_id'], 'div_id':x['div_id'], 'priority': x['priority'], 'tm_id':x['tm_id'], 'prefdays': x['prefdays']} for x in tmdbtuple.list if x['prefdays']] return self.get_indexer_tuple(tminfo_list) def get_indexer_tuple(self, tminfo_list): if tminfo_list: tminfo_indexerGet = lambda x: dict((p['dt_id'],i) for i,p in enumerate(tminfo_list)).get("dv"+str(x[0])+"tm"+str(x[1])) # indexermatch for list of team matches for specified div_id tminfo_indexerMatch = lambda x: [i for i,p in enumerate(tminfo_list) if p['div_id'] == x] # _List_IndexerM gets dereferenced using indexerMatch instead of # indexerGet tminfo_tuple = _List_IndexerGM(tminfo_list, tminfo_indexerGet, tminfo_indexerMatch) return tminfo_tuple else: return None
def get_xls(userid_name, schedcol_name, db_type, genxls_id, sched_cat): callback_name = request.query.callback #schedMaster = _routelogic_obj.schedmaster_map.get(userid_name) sindex_list = _routelogic_obj.sindexerMatch(userid_name, sched_cat) if sindex_list: sindex = sindex_list[0] schedMaster = _routelogic_obj.schedmaster_map_list[sindex][ 'schedmaster_obj'] if len(sindex_list) > 1: logging.warning("RouteProcess:get_xls, multiple indexermatch=%s" % (sindex_list, )) else: schedMaster = None #raise CodeLogicError("RouteProcess:get_xls, No indexermatch with routelogic=%s" # % (_routelogic_obj.schedmaster_map_list,)) if schedMaster is None: dbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, sched_cat) param = dbInterface.getschedule_param() #if db_type == 'rrdb': if sched_cat == "L": schedMaster = SchedMaster( mongoClient, userid_name, param['divdb_type'], param['divcol_name'], param['fieldcol_name'], schedcol_name, prefcol_name=param['prefcol_name'], conflictcol_name=param['conflictcol_name']) elif sched_cat == "T": schedMaster = TournSchedMaster(mongoClient, userid_name, param['divcol_name'], param['fieldcol_name'], schedcol_name) if not schedMaster.error_code: # save schedMaster to global obj to reuse on get_schedule #_routelogic_obj.schedmaster_map[userid_name] = schedMaster _routelogic_obj.schedmaster_map_list.append({ "userid_name": userid_name, "sched_cat": sched_cat, "schedmaster_obj": schedMaster }) else: del schedMaster if schedMaster.schedcol_name == schedcol_name: xls_exporter = schedMaster.xls_exporter if xls_exporter is None: xls_exporter = XLS_Exporter( schedcol_name, divinfo_tuple=schedMaster.divinfo_tuple, fieldinfo_tuple=schedMaster.fieldinfo_tuple, sdbInterface=schedMaster.sdbInterface) schedMaster.xls_exporter = xls_exporter #if db_type == 'tourndb': if sched_cat == "T": tourn_type = request.query.tourn_type file_list = xls_exporter.export(genxls_id, db_type, tourn_type=tourn_type) else: file_list = xls_exporter.export(genxls_id, db_type) return_dict = {'file_list': file_list} else: return_dict = {} a = json.dumps(return_dict) return callback_name + '(' + a + ')'
def __init__(self, mongoClient, userid_name, db_type, divcol_name, fieldcol_name, schedcol_name, prefcol_name=None, conflictcol_name=None): self._error_code = 0x0 self._error_message = "" self.userid_name = userid_name self.sdbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, "L") # db_type is for the divinfo schedule attached to the fielddb spec if db_type == 'rrdb': dbInterface = RRDBInterface(mongoClient, userid_name, divcol_name, "L") elif db_type == 'tourndb': dbInterface = TournDBInterface(mongoClient, userid_name, divcol_name, "L") else: raise CodeLogicError( "schemaster:init: db_type not recognized db_type=%s" % (db_type, )) dbtuple = dbInterface.readDBraw() if dbtuple.config_status == 1: self.oddnumplay_mode = dbtuple.oddnum_mode self.divinfo_list = dbtuple.list self.divinfo_indexerGet = lambda x: dict( (p['div_id'], i) for i, p in enumerate(self.divinfo_list)).get(x) self.divinfo_tuple = _List_Indexer(self.divinfo_list, self.divinfo_indexerGet) else: self.divinfo_tuple = _List_Indexer(None, None) raise CodeLogicError( "schemaster:init: div config not complete=%s" % (divcol_name, )) self._error_code |= DIVCONFIG_INCOMPLETE_MASK self.oddnumplay_mode = 0 # get field information fdbInterface = FieldDBInterface(mongoClient, userid_name, fieldcol_name, "L") fdbtuple = fdbInterface.readDBraw() if fdbtuple.config_status == 1: self.fieldinfo_list = fdbtuple.list self.fieldinfo_indexerGet = lambda x: dict( (p['field_id'], i) for i, p in enumerate(self.fieldinfo_list)).get(x) self.fieldinfo_tuple = _List_Indexer(self.fieldinfo_list, self.fieldinfo_indexerGet) else: self.fieldinfo_tuple = _List_Indexer(None, None) raise CodeLogicError( "schedmaster:init: field config not complete=%s" % (fieldcol_name, )) self._error_code |= FIELDCONFIG_INCOMPLETE_MASK # create list of div_ids that do not have a 'divfield_list' key divreqfields_list = [ x['div_id'] for x in self.divinfo_list if 'divfield_list' not in x ] # if there are div_id's with no 'divfield_list' key, create it if divreqfields_list: self.divfield_correlate(self.fieldinfo_list, dbInterface, divreqfields_list) else: self.simplifydivfield_list() # get team-related field affinity information, if any # use divcol_name as it shares collection name w divinfo tmdbInterface = TeamDBInterface(mongoClient, userid_name, divcol_name, "L") if tmdbInterface.check_docexists(): tmdbtuple = tmdbInterface.readDBraw() # recreate tminfo_list from db read, but leave out fields such as # team name which is not needed for schedule generation tminfo_tuple = self.get_team_field_affinity_params(tmdbtuple) tmprefdays_tuple = self.get_team_preference_params(tmdbtuple) else: tminfo_tuple = None tmprefdays_tuple = None # get pref list information, if any if prefcol_name: # preference list use is optional - only process if preference list # exists pdbInterface = PrefDBInterface(mongoClient, userid_name, prefcol_name, "L") pdbtuple = pdbInterface.readDBraw() if pdbtuple.config_status == 1: prefinfo_list = pdbtuple.list prefinfo_indexerGet = lambda x: dict( (p['pref_id'], i) for i, p in enumerate(prefinfo_list)).get(x) prefinfo_indexerMatch = lambda x: [ i for i, p in enumerate(prefinfo_list) if p['div_id'] == x ] prefinfo_triple = _List_IndexerGM(prefinfo_list, prefinfo_indexerGet, prefinfo_indexerMatch) else: prefinfo_triple = None # raise error as client should only be displaying in select widget # conflict lists that have config status complete raise CodeLogicError( "schedmaster:init: pref config not complete=%s" % (prefcol_name, )) else: pdbInterface = None prefinfo_triple = None if conflictcol_name: cdbInterface = ConflictDBInterface(mongoClient, userid_name, conflictcol_name, "L") cdbtuple = cdbInterface.readDBraw() if cdbtuple.config_status == 1: conflictinfo_list = cdbtuple.list else: conflictinfo_list = None raise CodeLogicError( "schedmaster:init: conflict config not complete=%s" % (conflictcol_name, )) else: conflictinfo_list = None cdbInterface = None #conflictinfo_tuple = None if self.divinfo_tuple.dict_list and prefinfo_triple and prefinfo_triple.dict_list: if not self.consistency_check(prefinfo_triple.dict_list): self._error_code |= PREFINFODATE_ERROR_MASK if not self._error_code: self.sdbInterface.setschedule_param( db_type, divcol_name, fieldcol_name, prefcol_name=prefcol_name, conflictcol_name=conflictcol_name) try: self.fieldtimeScheduleGenerator = FieldTimeScheduleGenerator( dbinterface=self.sdbInterface, divinfo_tuple=self.divinfo_tuple, fieldinfo_tuple=self.fieldinfo_tuple, prefinfo_triple=prefinfo_triple, pdbinterface=pdbInterface, tminfo_tuple=tminfo_tuple, conflictinfo_list=conflictinfo_list, cdbinterface=cdbInterface, tmprefdays_tuple=tmprefdays_tuple) except KeyError: self._error_code |= FIELDCONFIG_INCOMPLETE_MASK self._error_message += "Reselect Division Configuration in Field Configuration UI" self.schedcol_name = schedcol_name self._xls_exporter = None
class SchedMaster(object): def __init__(self, mongoClient, userid_name, db_type, divcol_name, fieldcol_name, schedcol_name, prefcol_name=None, conflictcol_name=None): self._error_code = 0x0 self._error_message = "" self.userid_name = userid_name self.sdbInterface = SchedDBInterface(mongoClient, userid_name, schedcol_name, "L") # db_type is for the divinfo schedule attached to the fielddb spec if db_type == 'rrdb': dbInterface = RRDBInterface(mongoClient, userid_name, divcol_name, "L") elif db_type == 'tourndb': dbInterface = TournDBInterface(mongoClient, userid_name, divcol_name, "L") else: raise CodeLogicError( "schemaster:init: db_type not recognized db_type=%s" % (db_type, )) dbtuple = dbInterface.readDBraw() if dbtuple.config_status == 1: self.oddnumplay_mode = dbtuple.oddnum_mode self.divinfo_list = dbtuple.list self.divinfo_indexerGet = lambda x: dict( (p['div_id'], i) for i, p in enumerate(self.divinfo_list)).get(x) self.divinfo_tuple = _List_Indexer(self.divinfo_list, self.divinfo_indexerGet) else: self.divinfo_tuple = _List_Indexer(None, None) raise CodeLogicError( "schemaster:init: div config not complete=%s" % (divcol_name, )) self._error_code |= DIVCONFIG_INCOMPLETE_MASK self.oddnumplay_mode = 0 # get field information fdbInterface = FieldDBInterface(mongoClient, userid_name, fieldcol_name, "L") fdbtuple = fdbInterface.readDBraw() if fdbtuple.config_status == 1: self.fieldinfo_list = fdbtuple.list self.fieldinfo_indexerGet = lambda x: dict( (p['field_id'], i) for i, p in enumerate(self.fieldinfo_list)).get(x) self.fieldinfo_tuple = _List_Indexer(self.fieldinfo_list, self.fieldinfo_indexerGet) else: self.fieldinfo_tuple = _List_Indexer(None, None) raise CodeLogicError( "schedmaster:init: field config not complete=%s" % (fieldcol_name, )) self._error_code |= FIELDCONFIG_INCOMPLETE_MASK # create list of div_ids that do not have a 'divfield_list' key divreqfields_list = [ x['div_id'] for x in self.divinfo_list if 'divfield_list' not in x ] # if there are div_id's with no 'divfield_list' key, create it if divreqfields_list: self.divfield_correlate(self.fieldinfo_list, dbInterface, divreqfields_list) else: self.simplifydivfield_list() # get team-related field affinity information, if any # use divcol_name as it shares collection name w divinfo tmdbInterface = TeamDBInterface(mongoClient, userid_name, divcol_name, "L") if tmdbInterface.check_docexists(): tmdbtuple = tmdbInterface.readDBraw() # recreate tminfo_list from db read, but leave out fields such as # team name which is not needed for schedule generation tminfo_tuple = self.get_team_field_affinity_params(tmdbtuple) tmprefdays_tuple = self.get_team_preference_params(tmdbtuple) else: tminfo_tuple = None tmprefdays_tuple = None # get pref list information, if any if prefcol_name: # preference list use is optional - only process if preference list # exists pdbInterface = PrefDBInterface(mongoClient, userid_name, prefcol_name, "L") pdbtuple = pdbInterface.readDBraw() if pdbtuple.config_status == 1: prefinfo_list = pdbtuple.list prefinfo_indexerGet = lambda x: dict( (p['pref_id'], i) for i, p in enumerate(prefinfo_list)).get(x) prefinfo_indexerMatch = lambda x: [ i for i, p in enumerate(prefinfo_list) if p['div_id'] == x ] prefinfo_triple = _List_IndexerGM(prefinfo_list, prefinfo_indexerGet, prefinfo_indexerMatch) else: prefinfo_triple = None # raise error as client should only be displaying in select widget # conflict lists that have config status complete raise CodeLogicError( "schedmaster:init: pref config not complete=%s" % (prefcol_name, )) else: pdbInterface = None prefinfo_triple = None if conflictcol_name: cdbInterface = ConflictDBInterface(mongoClient, userid_name, conflictcol_name, "L") cdbtuple = cdbInterface.readDBraw() if cdbtuple.config_status == 1: conflictinfo_list = cdbtuple.list else: conflictinfo_list = None raise CodeLogicError( "schedmaster:init: conflict config not complete=%s" % (conflictcol_name, )) else: conflictinfo_list = None cdbInterface = None #conflictinfo_tuple = None if self.divinfo_tuple.dict_list and prefinfo_triple and prefinfo_triple.dict_list: if not self.consistency_check(prefinfo_triple.dict_list): self._error_code |= PREFINFODATE_ERROR_MASK if not self._error_code: self.sdbInterface.setschedule_param( db_type, divcol_name, fieldcol_name, prefcol_name=prefcol_name, conflictcol_name=conflictcol_name) try: self.fieldtimeScheduleGenerator = FieldTimeScheduleGenerator( dbinterface=self.sdbInterface, divinfo_tuple=self.divinfo_tuple, fieldinfo_tuple=self.fieldinfo_tuple, prefinfo_triple=prefinfo_triple, pdbinterface=pdbInterface, tminfo_tuple=tminfo_tuple, conflictinfo_list=conflictinfo_list, cdbinterface=cdbInterface, tmprefdays_tuple=tmprefdays_tuple) except KeyError: self._error_code |= FIELDCONFIG_INCOMPLETE_MASK self._error_message += "Reselect Division Configuration in Field Configuration UI" self.schedcol_name = schedcol_name self._xls_exporter = None @property def xls_exporter(self): return self._xls_exporter @xls_exporter.setter def xls_exporter(self, value): self._xls_exporter = value @property def error_code(self): return self._error_code def generate(self): totalmatch_list = [] totalbyeteam_list = list() for divinfo in self.divinfo_list: totalteams = divinfo['totalteams'] # possibly rename below to 'totalrounddays' as totalgamedays may # not match up to number of physical days totalgamedays = divinfo['totalgamedays'] div_id = divinfo['div_id'] if self.oddnumplay_mode == 1: games_per_team = totalgamedays else: games_per_team = totalgamedays if ( totalteams * totalgamedays) % 2 == 0 else totalgamedays - 1 match = MatchGenerator(totalteams, totalgamedays, oddnumplay_mode=self.oddnumplay_mode, games_per_team=games_per_team) match_list = match.generateMatchList() args_obj = { 'div_id': div_id, 'match_list': match_list, 'numgames_perteam_list': match.numgames_perteam_list, 'gameslots_perrnd_perdiv': match.gameslotsperday } totalmatch_list.append(args_obj) if self.oddnumplay_mode > 0 and match.byeteam_list: totalbyeteam_list.append({ 'div_id': div_id, 'byeteam_list': match.byeteam_list }) totalmatch_indexerGet = lambda x: dict( (p['div_id'], i) for i, p in enumerate(totalmatch_list)).get(x) totalmatch_tuple = _List_Indexer(totalmatch_list, totalmatch_indexerGet) try: status = self.fieldtimeScheduleGenerator.generateSchedule( totalmatch_tuple, self.oddnumplay_mode, totalbyeteam_list) except ValueError: return { 'status': 0, 'error_code': GENERATE_ERROR_MASK, 'error_message': "Not enough available dates: Check Division MinMax Gap or Field Calendar Length" } except FieldAvailabilityError as e: return { 'status': 0, 'error_code': GENERATE_ERROR_MASK, 'error_message': 'Field space has been exhausted with divsion ' + str(e.div_id) } else: return {'status': 1} if status else {'status': 0} '''function to add fields key to divinfo_list. Supersedes global function (unnamed) in leaguedivprep''' def divfield_correlate(self, fieldinfo_list, dbInterface, div_list): # use set to keep track of unique div_id's that have fields attached to them divset = set() for fieldinfo in fieldinfo_list: field_id = fieldinfo['field_id'] for div_id in fieldinfo['primaryuse_list']: if div_id in div_list: index = self.divinfo_indexerGet(div_id) if index is not None: divset.add(div_id) divinfo = self.divinfo_list[index] # check existence of key 'divfield_list' - if it exists, append to list of fields, if not create if 'divfield_list' in divinfo: divinfo['divfield_list'].append(field_id) else: divinfo['divfield_list'] = [field_id] def simplifydivfield_list(self): ''' create a simplified version of the divfield_list which only includes the field_id's - the only value necessary for schedule creation; ignore other items such as field name ''' for divinfo in self.divinfo_list: if 'divfield_list' in divinfo: new_list = [x['field_id'] for x in divinfo['divfield_list']] divinfo['divfield_list'] = new_list def get_schedule(self, idproperty, propid, div_age="", div_gen=""): if idproperty == 'div_id': divinfo = self.divinfo_list[self.divinfo_indexerGet(propid)] game_list = self.sdbInterface.get_schedule( idproperty, div_age=divinfo['div_age'], div_gen=divinfo['div_gen']) # also get fields info tied to div fieldname_dict = { x: self.fieldinfo_list[self.fieldinfo_indexerGet(x)]['field_name'] for x in divinfo['divfield_list'] } return {'game_list': game_list, 'fieldname_dict': fieldname_dict} elif idproperty == 'field_id': fieldinfo = self.fieldinfo_list[self.fieldinfo_indexerGet(propid)] game_list = self.sdbInterface.get_schedule( idproperty, field_id=fieldinfo['field_id']) return {'game_list': game_list} elif idproperty == 'team_id': game_list = self.sdbInterface.get_schedule(idproperty, team_id=propid, div_age=div_age, div_gen=div_gen) return {'game_list': game_list} elif idproperty == 'fair_id': # get fairness metrics # get divinfo as fairness metric calculations require divinfo # parameters divinfo = self.divinfo_list[self.divinfo_indexerGet(propid)] metrics_list = self.sdbInterface.get_schedule( idproperty, div_age=div_age, div_gen=div_gen, divinfo=divinfo, fieldinfo_tuple=self.fieldinfo_tuple) divfield_list = [{ 'field_id': x, 'field_name': self.fieldinfo_list[self.fieldinfo_indexerGet(x)]['field_name'] } for x in divinfo['divfield_list']] return { 'metrics_list': metrics_list, 'divfield_list': divfield_list } def consistency_check(self, prefinfo_list): '''preference info date consistency check against calendar map''' cmap_list = list() cindexerGet = lambda x: dict( (p['date'], i) for i, p in enumerate(cmap_list)).get(x) for prefinfo in prefinfo_list: game_date = parser.parse(prefinfo['game_date']) for fieldinfo in self.fieldinfo_list: cmap_list = fieldinfo['calendarmap_list'] index = cindexerGet(game_date) if index is not None: # match found, break break else: # no match found, go to next prefinfo continue # match already found, break from prefinfo loop break else: # prefinfo and fieldinfo list calendarmap not consistent return False # consistent, success return True def getHTMLTeamTable(self, div_age, div_gen, team_id): # https://pypi.python.org/pypi/html/ return_dict = self.get_schedule('team_id', team_id, div_age=div_age, div_gen=div_gen) game_list = return_dict['game_list'] html = HTML() table = html.table(width='100%', border='1px solid black') table.caption(self.userid_name + " " + self.schedcol_name + " " + div_age + div_gen + str(team_id)) header_row = table.tr header_row.th('Game Date', padding='5px') header_row.th('Start Time', padding='5px') header_row.th('Field', padding='5px') header_row.th('Home', padding='5px') header_row.th('Away', padding='5px') for game in game_list: game_row = table.tr game_row.td(game['game_date']) game_row.td(game['start_time']) findex = self.fieldinfo_indexerGet(game['venue']) if findex is not None: field_name = self.fieldinfo_list[findex]['field_name'] game_row.td(field_name) game_row.td(str(game['home'])) game_row.td(str(game['away'])) return str(html) def get_team_field_affinity_params(self, tmdbtuple): tminfo_list = [{ 'dt_id': x['dt_id'], 'div_id': x['div_id'], 'tm_id': x['tm_id'], 'af_list': x['af_list'] } for x in tmdbtuple.list if x['af_list']] return self.get_indexer_tuple(tminfo_list) def get_team_preference_params(self, tmdbtuple): tminfo_list = [{ 'dt_id': x['dt_id'], 'div_id': x['div_id'], 'priority': x['priority'], 'tm_id': x['tm_id'], 'prefdays': x['prefdays'] } for x in tmdbtuple.list if x['prefdays']] return self.get_indexer_tuple(tminfo_list) def get_indexer_tuple(self, tminfo_list): if tminfo_list: tminfo_indexerGet = lambda x: dict( (p['dt_id'], i) for i, p in enumerate(tminfo_list)).get( "dv" + str(x[0]) + "tm" + str(x[1])) # indexermatch for list of team matches for specified div_id tminfo_indexerMatch = lambda x: [ i for i, p in enumerate(tminfo_list) if p['div_id'] == x ] # _List_IndexerM gets dereferenced using indexerMatch instead of # indexerGet tminfo_tuple = _List_IndexerGM(tminfo_list, tminfo_indexerGet, tminfo_indexerMatch) return tminfo_tuple else: return None