ssbu_lokrez/lokrez/__init__.py

496 lines
16 KiB
Python

import argparse
import configparser
import datetime
import html
import json
import logging
import os, os.path
import sys
import lokrez.export as export
import lokrez.resources as resources
import lokrez.smashgg as smashgg
import lokrez.version as version
# =============================================================================
__version__ = version.__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)