Пример #1
0
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+')'
Пример #2
0
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 + ')'
Пример #3
0
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+')'
Пример #4
0
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
Пример #5
0
    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
Пример #6
0
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
Пример #7
0
    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
Пример #8
0
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
Пример #9
0
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 + ')'
Пример #10
0
    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
Пример #11
0
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