diff --git a/lokrez/__init__.py b/lokrez/__init__.py index 2f4a8ae..dbd19df 100644 --- a/lokrez/__init__.py +++ b/lokrez/__init__.py @@ -1,9 +1,14 @@ -from .version import VERSION_NAME - import argparse +import datetime import logging +import os, os.path import sys +import jinja2 + +import smashgg +import version + # ============================================================================= def main(): @@ -22,7 +27,7 @@ def main(): parser.add_argument( "--token", "-t", default = None, - help = "the auhentication token to use" ) + help = "the authentication token to use" ) # ------------------------------------------------------------------------- top8_parser = subparsers.add_parser( @@ -34,10 +39,19 @@ def main(): default = None, help = "The tournament slug or id", ) - + top8_parser.add_argument( + "--resourcesdir", "-RD", + default = "res", + help = "The directories containing images and templates resources", + ) + top8_parser.add_argument( + "--templatesdir", "-TD", + default = "templates", + help = "The local result templates directory", + ) top8_parser.add_argument( "--template", "-T", - default = "default", + default = "rebootlyon", help = "The local result template to use", ) top8_parser.add_argument( @@ -47,6 +61,13 @@ def main(): "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, @@ -63,7 +84,7 @@ def main(): # Set log level # ------------------------------------------------------------------------- - log = logging.getLogger(NAME) + log = logging.getLogger(version.NAME) log.setLevel(logging.DEBUG) log_handler_console = logging.StreamHandler() @@ -85,9 +106,285 @@ def main(): # Print version if required # ------------------------------------------------------------------------- if args.version: - print(VERSION_NAME) + print(version.VERSION_NAME) sys.exit(0) + # ------------------------------------------------------------------------- + if args.command not in [ "top8" ]: + parser.print_help() + sys.exit(1) + + # ------------------------------------------------------------------------- + if args.command == "top8": + + try: + with open(args.lkrz_file, "r") as f: + # TODO parse config file to fill tournament & top_players + log.error("config file loading is not implemened yet") + raise NotImplementedError + + except: + # 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") + sys.exit(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) + + # Template rendering + log.info("Génération du SVG en utilisant le template") + + jj2_env = jinja2.Environment( + loader = jinja2.FileSystemLoader( + os.path.join( + args.resourcesdir, + args.templatesdir, + ) + ) + ) + + jj2_tpl = jj2_env.get_template( + os.path.join( + args.template, + "template.svg.j2", + ) + ) + + context = { + "tournament": tournament, + "players" : sorted( + top_players.values(), + key = lambda p: p.placement, + ) + } + + + if args.oufile is None: + args.outfile = "{}.svg".format(tournament.slug) + + jj2_tpl.stream(context).dump( args.outfile ) + + +# ----------------------------------------------------------------------------- +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__': main()