ssbu_lokrez/lokrez/__init__.py

494 lines
16 KiB
Python

import argparse
import configparser
import datetime
import html
import json
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/chars",
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/chars",
help = "The directories containing images",
)
top8_parser.add_argument(
"--playerskinsdb", "-PD",
default = "res/ssbu/playerskins/lyon.json",
help = "A CSV file matching player tags, characters and " \
"preferred skins",
)
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",
)
top8_parser.add_argument(
"--name-seo-delimiter",
default = None,
help = "A character that will delimit in a tournament name what " \
"really is its name, and what's actually here for SEO " \
"purposes (example: in 'Cornismash #42 - Ultimate Weekly " \
"Lyon', only the 'Cornismash #42' is the tournament name, "\
"the rest is here to help find the tournament).",
)
# -------------------------------------------------------------------------
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":
# Initialize PLAYERSKINS db
log.debug("loading playerskins db from '{}'" \
.format(args.playerskinsdb))
with open(args.playerskinsdb, "r") as f:
smashgg.PLAYERSKINS = json.load(f)
#
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.clean_name(args.name_seo_delimiter),
"players" : sorted(
top_players.values(),
key = lambda p: p.placement,
),
"dir_root": args.rootdir,
"dir_res_ssbu": os.path.join(
args.imgdir,
),
}
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)