import argparse import configparser import datetime import html import logging import os, os.path import sys import export import resources import smashgg import version # ============================================================================= def main(): # ------------------------------------------------------------------------- parser = argparse.ArgumentParser() subparsers = parser.add_subparsers( dest = "command", help = "commands", ) parser.add_argument( "--proxy", "-p", default = None, help = "the proxy to use" ) parser.add_argument( "--token", "-t", default = None, help = "the authentication token to use" ) parser.add_argument( "--rootdir", "-RD", default = None, help = "The directories containing this script, defaults to '.'", ) # ------------------------------------------------------------------------- init_parser = subparsers.add_parser( "init", ) init_parser.add_argument( "game", default = "ssbu", help = "The game you want to initialize the resources for", ) init_parser.add_argument( "--imgdir", "-ID", default = "res/ssbu", help = "The directory we should download the resources to", ) # ------------------------------------------------------------------------- top8_parser = subparsers.add_parser( "top8", ) top8_parser.add_argument( "tournament", default = None, help = "The tournament slug or id", ) top8_parser.add_argument( "--imgdir", "-ID", default = "res/ssbu", help = "The directories containing images", ) top8_parser.add_argument( "--templatesdir", "-TD", default = "templates", help = "The local result templates directory", ) top8_parser.add_argument( "--template", "-T", default = "rebootlyon", help = "The local result template to use", ) top8_parser.add_argument( "--lkrz-file", "-f", default = None, help = "The lkrz file in which the results are stored ; if it " \ "does not exist, one will be created from the smashgg data", ) top8_parser.add_argument( "--outfile", "-o", default = None, help = "The SVG local result file to output to ; defaults to " \ "./tournament-slug.svg", ) # ------------------------------------------------------------------------- parser.add_argument( "--verbose", "-v", default = 0, action = "count", help = "increase verbosity" ) parser.add_argument( "--version", "-V", default = False, action = "store_true", help = "show version number" ) # ------------------------------------------------------------------------- args = parser.parse_args() # Set log level # ------------------------------------------------------------------------- log = logging.getLogger(version.NAME) log.setLevel(logging.DEBUG) log_handler_console = logging.StreamHandler() log_handler_console.setLevel(logging.WARNING) if(args.verbose >= 2): log_handler_console.setLevel(logging.DEBUG) elif(args.verbose >=1): log_handler_console.setLevel(logging.INFO) else: log_handler_console.setLevel(logging.WARNING) log_formatter_console = logging.Formatter("%(name)s:%(levelname)s: %(message)s") log_handler_console.setFormatter(log_formatter_console) log.addHandler(log_handler_console) # Print version if required # ------------------------------------------------------------------------- if args.version: print(version.VERSION_NAME) return 0 # Set default arguments # ------------------------------------------------------------------------- # Default rootdir is "." if args.rootdir is None: args.rootdir = "." # TODO compute script root? # ------------------------------------------------------------------------- if args.command not in [ "init", "top8" ]: parser.print_help() return 1 # ------------------------------------------------------------------------- if args.command == "init": resources.download_res_ssbu( dstdir = args.imgdir, log = log, ) return 0 # ------------------------------------------------------------------------- if args.command == "top8": tournament = None top_players = {} try: lkrz = configparser.ConfigParser() lkrz.read(args.lkrz_file) log.info("Loading data from '{}'".format(args.lkrz_file)) for s in lkrz: section = lkrz[s] if s == "Tournament": tournament = smashgg.Tournament( id = 0, name = section["name"], slug = section["slug"], startAt = datetime.datetime.strptime( section["date"], "%Y-%m-%d %H:%M:%S", ), numEntrants = int(section["numEntrants"]), venueName = section["location"], ) elif s.startswith("player "): chars = {} for char in section["characters"].split(","): c = char.strip() charname = c.split("_")[0] charskin = c.split("_")[1].split(" ")[0] charscore = float(c.split("(")[1].split(")")[0]) chars[(charname,charskin)] = charscore player = smashgg.Player( id = 0, prefix = section["team"], gamerTag = section["tag"], placement = section["placement"], seeding = section["seeding"], chars = chars, ) top_players[player.gamerTag] = player except Exception as e: log.warning(e) # Get infos from smash.gg and write the config file tournament, top_players = getTournamentTop( id_or_slug = args.tournament, top = 8, token = args.token, proxy = args.proxy, log = log, ) if tournament is None or top_players is None: log.error("Could not load data from smash.gg") return 1 lkrz_data = "\n".join( [ tournament.conf() ] \ + list(map( lambda p:p.conf(), top_players.values(), )) ) if args.lkrz_file is None: args.lkrz_file = "{}.lkrz".format(tournament.slug) with open(args.lkrz_file, "w", encoding="utf8") as f: f.write(lkrz_data) # Default outfile is 'tournament-slug.svg' if args.outfile is None: args.outfile = "{}.svg".format(tournament.slug) # Build the context which will be passed to the template context = { "tournament": tournament, "players" : sorted( top_players.values(), key = lambda p: p.placement, ), "dir_root": args.rootdir, "dir_res_ssbu": os.path.join( args.imgdir, "chars", ), } rv = export.generate_outfile( args.templatesdir, args.template, context, args.outfile, log, ) if rv is None: return 1 log.info("Successfully saved outfile as '{}'".format(rv)) return 0 # ----------------------------------------------------------------------------- def getTournamentTop( id_or_slug, top=8, token = "", proxy = None, log=None): """Returns a tuple : the smashgg.Tournament object and a list of the top smashgg.Player in that tournament.""" # ------------------------------------------------------------------------- # Select the right event (the one with the most entrants or the most sets) def selectBiggestEvent(data, log=None): try: event = data["events"][0] except: log.error("No event found in data") log.debug(data) return None try: numEntrants = event["numEntrants"] except KeyError: numEntrants = event["standings"]["pageInfo"]["total"] for e in data["events"]: try: ne = e["numEntrants"] except KeyError: ne = e["standings"]["pageInfo"]["total"] if ne > numEntrants: event = e numEntrants = ne log.info("Selected Event '{}' with {} entrants" \ .format( event["name"], numEntrants, )) return event # ------------------------------------------------------------------------- data = None try: data = smashgg.run_query( query_name = "getTournamentTopById", variables = { "id" : int(id_or_slug), # If this fails, it's a slug "top": top, }, token = token, proxy = proxy, log = log, ) except ValueError: data = smashgg.run_query( query_name = "getTournamentTopBySlug", variables = { "slug" : id_or_slug, "top": top, }, token = token, proxy = proxy, log = log, ) try: tournament_data = data["tournament"] except: log.error("Failed to load Tournaments") return None,None if tournament_data is None: log.error("Failed to load Tournament") return None,None event = selectBiggestEvent(tournament_data, log) if event is None : return None,None # Get the tournament tournament = smashgg.Tournament( id = tournament_data["id"], slug = tournament_data["slug"], name = tournament_data["name"], startAt = \ datetime.datetime. \ fromtimestamp(tournament_data["startAt"]), numEntrants = event["standings"]["pageInfo"]["total"], venueAddress = tournament_data["venueAddress"], venueName = tournament_data["venueName"], city = tournament_data["city"], countryCode = tournament_data["countryCode"], hashtag = tournament_data["hashtag"], ) # Get the top players top_players = {} standings = event["standings"]["nodes"] for standing in standings : seeding = None for seed in standing["entrant"]["seeds"]: # Take the seeding from the phase with *all* Event entrants if seed["phase"]["numSeeds"] == tournament.numEntrants: seeding = seed["groupSeedNum"] participant_data = standing["entrant"]["participants"][0] try: twitterHandle = participant_data \ ["player"] \ ["user"] \ ["authorizations"] \ [0] \ ["externalUsername"] except TypeError: twitterHandle = None player = smashgg.Player( id = standing["entrant"]["id"], prefix = participant_data["prefix"], gamerTag = participant_data["gamerTag"], placement = standing["placement"], seeding = seeding, twitterHandle = twitterHandle, ) top_players[player.id] = player # ------------------------------------------------------------------------- # Now, we need to find which characters those top players chose data = None data = smashgg.run_query( query_name = "getCharsByTournamentIdAndEntrantIds", variables = { "tournamentId" : int(tournament.id), "entrantIds": [ id for id in top_players.keys() ], }, token = token, proxy = proxy, log = log, ) try: tournament_data = data["tournament"] except: log.error("Failed to load Tournament") return None,None if tournament_data is None: log.error("Failed to load Tournament") return None,None event = selectBiggestEvent(tournament_data, log) if event is None : return None,None # TODO check that sets number is < to hardcoded 100 max value (cf query) sets = event["sets"]["nodes"] for s in sets: try: for g in s["games"]: winnerId = g["winnerId"] for slct in g["selections"]: if slct["selectionType"] == "CHARACTER": eid = slct["entrant"]["id"] try: top_players[eid].add_character_selection( character = slct["selectionValue"], win = (winnerId == eid), ) except KeyError: pass except TypeError: # If some games or selections are null, this can happen continue # Return the data return tournament, top_players # ============================================================================= if __name__ == '__main__': rv = main() sys.exit(rv)