Add the multiple games feature
parent
7b9d3f13f4
commit
18a4b6674e
|
@ -1,10 +1,13 @@
|
|||
import argparse
|
||||
import configparser
|
||||
import copy
|
||||
import datetime
|
||||
import html
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import pathlib
|
||||
import pprint
|
||||
import requests
|
||||
import sys
|
||||
import urllib
|
||||
|
@ -31,6 +34,62 @@ LOG_DUMMY.addHandler(logging.NullHandler())
|
|||
|
||||
DEFAULT_DIR_TEMPLATES = ROOTDIR / "templates"
|
||||
|
||||
# =============================================================================
|
||||
class StoreOptionKeyPair(argparse.Action):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=1,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar="KEY=VALUE",
|
||||
):
|
||||
|
||||
if nargs == 0:
|
||||
raise ValueError(
|
||||
'nargs for append actions must be > 0; if arg '
|
||||
'strings are not supplying the value to append, '
|
||||
'the append const action may be more appropriate'
|
||||
)
|
||||
if const is not None and nargs != '?':
|
||||
raise ValueError('nargs must be %r to supply const' % '?')
|
||||
|
||||
super(StoreOptionKeyPair, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
nargs=nargs,
|
||||
const=const,
|
||||
default=default,
|
||||
type=type,
|
||||
choices=choices,
|
||||
required=required,
|
||||
help=help,
|
||||
metavar=metavar,
|
||||
)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
try:
|
||||
options = getattr(namespace, self.dest)
|
||||
except AttributeError:
|
||||
options = {}
|
||||
|
||||
for kv in values:
|
||||
try:
|
||||
k,v = kv.split("=")
|
||||
except ValueError:
|
||||
k = kv
|
||||
v = True
|
||||
|
||||
options[k] = v
|
||||
|
||||
setattr(namespace, self.dest, options)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
def get_templates_list(
|
||||
dir_templates = DEFAULT_DIR_TEMPLATES,
|
||||
|
@ -47,6 +106,76 @@ def get_templates_list(
|
|||
|
||||
return templates_list
|
||||
|
||||
# =============================================================================
|
||||
def get_infos_from_file(
|
||||
lkrz_file_path,
|
||||
options = {},
|
||||
outform = "dict",
|
||||
log = LOG_DUMMY,
|
||||
):
|
||||
|
||||
if not lkrz_file_path.exists():
|
||||
raise IOError( "lkrz file '{}' does not exist" \
|
||||
.format(str(lkrz_file_path)) )
|
||||
|
||||
lkrz = configparser.ConfigParser()
|
||||
lkrz.read(str(lkrz_file_path))
|
||||
|
||||
log.info("Loading data from '{}'".format(str(lkrz_file_path)))
|
||||
|
||||
if s in lkrz:
|
||||
section = lkrz[s]
|
||||
if s == "Tournament":
|
||||
tournament = smashgg.Tournament(
|
||||
id = 0,
|
||||
name = section["name"],
|
||||
game = section["game"],
|
||||
slug = section["slug"],
|
||||
startAt = datetime.datetime.strptime(
|
||||
section["date"],
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
),
|
||||
numEntrants = int(section["numEntrants"]),
|
||||
venueName = section["location"],
|
||||
) \
|
||||
.clean_name(
|
||||
options.get("name_seo_delimiter", None)
|
||||
)
|
||||
|
||||
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"],
|
||||
twitterHandle = section["twitter"],
|
||||
chars = chars,
|
||||
)
|
||||
|
||||
top_players[player.gamerTag] = player
|
||||
|
||||
# Re-sort top players by their placement
|
||||
top_players = sorted(
|
||||
top_players.values(),
|
||||
key = lambda p: p.placement,
|
||||
)
|
||||
|
||||
return format_infos(
|
||||
outform,
|
||||
tournament,
|
||||
top_players,
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
def get_infos_from_url(
|
||||
url,
|
||||
|
@ -65,25 +194,76 @@ def get_infos_from_url(
|
|||
if outform not in [ "dict", "lkrz" ]:
|
||||
raise ValueError("Unsupported outform")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
tournament = None
|
||||
event = None
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
if url_parsed.netloc == "smash.gg":
|
||||
|
||||
if (url_parsed.path.split("/")[1] != "tournament"):
|
||||
raise Exception("No tournament found in url {}".format(url_parsed.path.split("/")))
|
||||
log.error("Incomplete URL '{}'".format(url))
|
||||
raise Exception("No tournament found in url {}".format(url))
|
||||
|
||||
# Get infos from smash.gg and write the config file
|
||||
tournament, top_players = getTournamentTop(
|
||||
id_or_slug = url_parsed.path.split("/")[2],
|
||||
import_options = options,
|
||||
top = top,
|
||||
token = token,
|
||||
proxy = proxy,
|
||||
log = log,
|
||||
)
|
||||
try:
|
||||
tournament = url_parsed.path.split("/")[2]
|
||||
except:
|
||||
log.error("Incomplete URL '{}'".format(url))
|
||||
raise Exception("No tournament slug found in url {}".format(url))
|
||||
|
||||
if tournament is None or top_players is None:
|
||||
log.error("Could not load data from smash.gg")
|
||||
raise Exception("Could not load data from smash.gg")
|
||||
try:
|
||||
if (url_parsed.path.split("/")[3] == "event"):
|
||||
event = url_parsed.path.split("/")[4]
|
||||
except:
|
||||
log.info("No event slug found in url")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
return get_infos_from_id_or_slug(
|
||||
id_or_slug = tournament,
|
||||
event_slug = event,
|
||||
token = token,
|
||||
options = options,
|
||||
outform = outform,
|
||||
top = top,
|
||||
proxy = proxy,
|
||||
log = log,
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
def get_infos_from_id_or_slug(
|
||||
id_or_slug,
|
||||
event_slug = None,
|
||||
token = "",
|
||||
options = [],
|
||||
outform = "dict",
|
||||
top = 8,
|
||||
proxy = None,
|
||||
log = LOG_DUMMY,
|
||||
):
|
||||
# Get infos from smash.gg and write the config file
|
||||
tournament, top_players = getTournamentTop(
|
||||
id_or_slug = id_or_slug,
|
||||
event_slug = event_slug,
|
||||
import_options = options,
|
||||
top = top,
|
||||
token = token,
|
||||
proxy = proxy,
|
||||
log = log,
|
||||
)
|
||||
|
||||
if tournament is None or top_players is None:
|
||||
log.error("Could not load data from smash.gg")
|
||||
raise Exception("Could not load data from smash.gg")
|
||||
|
||||
return format_infos(outform, tournament, top_players)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
def format_infos(
|
||||
outform,
|
||||
tournament,
|
||||
top_players,
|
||||
):
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
if outform == "dict":
|
||||
|
@ -117,36 +297,24 @@ def init_resources(
|
|||
|
||||
# Start resources download according to game
|
||||
if game in ssbu.GAME.list_names():
|
||||
resources.download_res(
|
||||
dstdir = imgdir,
|
||||
game = ssbu,
|
||||
source = source,
|
||||
store_raw = store_raw,
|
||||
proxy = proxy,
|
||||
log = log,
|
||||
)
|
||||
game = ssbu
|
||||
elif game in melee.GAME.list_names():
|
||||
resources.download_res_ssbm(
|
||||
dstdir = imgdir,
|
||||
game = melee,
|
||||
source = source,
|
||||
store_raw = store_raw,
|
||||
proxy = proxy,
|
||||
log = log,
|
||||
)
|
||||
game = melee
|
||||
elif game in pplus.GAME.list_names():
|
||||
resources.download_res(
|
||||
dstdir = imgdir,
|
||||
game = pplus,
|
||||
source = source,
|
||||
store_raw = store_raw,
|
||||
proxy = proxy,
|
||||
log = log,
|
||||
)
|
||||
game = pplus
|
||||
else:
|
||||
log.error("Unknown game '{}'".format(game))
|
||||
return 1
|
||||
|
||||
resources.download_res(
|
||||
dstdir = imgdir,
|
||||
game = game,
|
||||
source = source,
|
||||
store_raw = store_raw,
|
||||
proxy = proxy,
|
||||
log = log,
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
# =============================================================================
|
||||
|
@ -161,12 +329,20 @@ def generate_pic(
|
|||
log = LOG_DUMMY,
|
||||
):
|
||||
|
||||
if outform.startswith("."):
|
||||
outform = outform[1:]
|
||||
|
||||
if outform not in ["svg", "png"]:
|
||||
raise Exception("Unsupported outform")
|
||||
|
||||
if type(infos_or_lkrzfile) == str:
|
||||
# TODO : load lkrz as dict infos
|
||||
raise NotImplementedError()
|
||||
# load lkrz as dict infos
|
||||
infos = get_infos_from_file(
|
||||
lkrz_file_path = pathlib.Path(infos_or_lkrzfile),
|
||||
options = options,
|
||||
outform = "dict",
|
||||
log = log,
|
||||
)
|
||||
else:
|
||||
infos = infos_or_lkrzfile
|
||||
|
||||
|
@ -185,7 +361,7 @@ def generate_pic(
|
|||
),
|
||||
"dir_res_ssbu": dir_res,
|
||||
"dir_template": str(dir_templates/template),
|
||||
"options": options.get("template_options", []),
|
||||
"options": options,
|
||||
}
|
||||
|
||||
pic = export.generate_pic(
|
||||
|
@ -195,7 +371,7 @@ def generate_pic(
|
|||
outform,
|
||||
log = log,
|
||||
cachedir = dir_cache,
|
||||
options = { "svg_embed_png": options.get("svg_embed_png",False) },
|
||||
options = options,
|
||||
)
|
||||
|
||||
if pic is None:
|
||||
|
@ -308,21 +484,24 @@ def main():
|
|||
)
|
||||
top8_parser.add_argument(
|
||||
"--template-options", "-TO",
|
||||
action = "append",
|
||||
default = [],
|
||||
nargs="+",
|
||||
action = StoreOptionKeyPair,
|
||||
default = {},
|
||||
help = "Template-specific options (like 'covid' or 'animated')",
|
||||
)
|
||||
top8_parser.add_argument(
|
||||
"--export-options", "-EO",
|
||||
action = "append",
|
||||
default = [],
|
||||
nargs="+",
|
||||
action = StoreOptionKeyPair,
|
||||
default = {},
|
||||
help = "Export options (like 'svg_embed_png')",
|
||||
)
|
||||
|
||||
top8_parser.add_argument(
|
||||
"--import-options", "-IO",
|
||||
action = "append",
|
||||
default = [],
|
||||
nargs="+",
|
||||
action = StoreOptionKeyPair,
|
||||
default = {},
|
||||
help = "Import options (like 'use_smashgg_prefixes')",
|
||||
)
|
||||
|
||||
|
@ -378,6 +557,10 @@ def main():
|
|||
|
||||
log.addHandler(log_handler_console)
|
||||
|
||||
# Print all arguments in debug
|
||||
# -------------------------------------------------------------------------
|
||||
log.debug( "Command arguments:\n{}".format( pprint.pformat(vars(args))) )
|
||||
|
||||
# Print version if required
|
||||
# -------------------------------------------------------------------------
|
||||
if args.version:
|
||||
|
@ -423,58 +606,21 @@ def main():
|
|||
#
|
||||
tournament = None
|
||||
top_players = {}
|
||||
lkrz_file = None
|
||||
|
||||
if args.lkrz_file is not None and args.lkrz_file.exists():
|
||||
lkrz = configparser.ConfigParser()
|
||||
lkrz.read(str(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"],
|
||||
twitterHandle = section["twitter"],
|
||||
chars = chars,
|
||||
)
|
||||
|
||||
top_players[player.gamerTag] = player
|
||||
|
||||
# Re-sort top players by their placement
|
||||
top_players = sorted(
|
||||
top_players.values(),
|
||||
key = lambda p: p.placement,
|
||||
)
|
||||
|
||||
else:
|
||||
all_options = {
|
||||
**args.import_options,
|
||||
**args.template_options,
|
||||
**args.export_options,
|
||||
}
|
||||
|
||||
# Determine the nature of the 'tournament' argument :
|
||||
# - url
|
||||
# - id or slug
|
||||
# - lkrz file
|
||||
# url
|
||||
if ( args.tournament.startswith("http://")
|
||||
or args.tournament.startswith("https://") ):
|
||||
infos = get_infos_from_url(
|
||||
url = args.tournament,
|
||||
token = args.token,
|
||||
|
@ -485,29 +631,33 @@ def main():
|
|||
log = log,
|
||||
)
|
||||
|
||||
tournament = infos["tournament"]
|
||||
top_players = infos["players"]
|
||||
|
||||
if tournament is None or top_players is None:
|
||||
log.error("Could not load data from smash.gg")
|
||||
return 1
|
||||
|
||||
# Save a lkrz file
|
||||
lkrz_data = "\n".join(
|
||||
[ tournament.conf() ] \
|
||||
+ list(map(
|
||||
lambda p:p.conf(),
|
||||
top_players,
|
||||
))
|
||||
# lkrz file
|
||||
elif pathlib.Path(args.tournament).exists():
|
||||
infos = get_infos_from_file(
|
||||
lkrz_file_path = pathlib.Path(args.tournament),
|
||||
options = args.import_options,
|
||||
outform = "dict",
|
||||
log = log,
|
||||
)
|
||||
|
||||
if args.lkrz_file is None:
|
||||
args.lkrz_file = pathlib.Path(
|
||||
"{}.lkrz".format(tournament.slug)
|
||||
)
|
||||
# id or slug
|
||||
else:
|
||||
infos = get_infos_from_id_or_slug(
|
||||
id_or_slug = args.tournament,
|
||||
token = args.token,
|
||||
options = args.import_options,
|
||||
outform = "dict",
|
||||
top = 8,
|
||||
proxy = args.proxy,
|
||||
log = log,
|
||||
)
|
||||
|
||||
with args.lkrz_file.open("w", encoding="utf8") as f:
|
||||
f.write(lkrz_data)
|
||||
tournament = infos["tournament"]
|
||||
top_players = infos["players"]
|
||||
|
||||
if tournament is None or top_players is None:
|
||||
log.error("Could not load data")
|
||||
return 1
|
||||
|
||||
# Default outfile is 'tournament-slug.svg'
|
||||
if args.outfile is None:
|
||||
|
@ -515,48 +665,60 @@ def main():
|
|||
"{}.svg".format(tournament.slug),
|
||||
)
|
||||
|
||||
# Build the context which will be passed to the template
|
||||
# Save a lkrz file
|
||||
if not args.no_lkrz:
|
||||
lkrz_data = format_infos("lkrz", tournament, top_players)
|
||||
lkrz_file = args.outfile.with_suffix(".lkrz")
|
||||
|
||||
with lkrz_file.open("w", encoding="utf8") as f:
|
||||
f.write(lkrz_data)
|
||||
|
||||
# If the outfile we were asked for was a .lkrz, we're done
|
||||
if args.outfile.suffix == ".lkrz":
|
||||
return 0
|
||||
|
||||
# Otherwise, let's generate the picture file
|
||||
# First build the context which will be passed to the template
|
||||
try:
|
||||
dir_res_ssbu = args.imgdir.as_uri() # not absolute => error
|
||||
dir_res = (args.imgdir / tournament.game.name).as_uri() # not absolute => error
|
||||
except ValueError:
|
||||
dir_res_ssbu = args.imgdir.as_posix()
|
||||
dir_res = (args.imgdir / tournament.game.name).as_posix()
|
||||
|
||||
context = {
|
||||
"tournament": tournament.clean_name(args.name_seo_delimiter),
|
||||
"players" : sorted(
|
||||
top_players,
|
||||
key = lambda p: p.placement,
|
||||
),
|
||||
"dir_res_ssbu": dir_res_ssbu,
|
||||
"dir_template": str(args.templatesdir / args.template),
|
||||
"options": args.template_options,
|
||||
}
|
||||
|
||||
rv = export.generate_outfile(
|
||||
args.templatesdir,
|
||||
args.template,
|
||||
context,
|
||||
args.outfile,
|
||||
pic = generate_pic(
|
||||
infos_or_lkrzfile = infos,
|
||||
template = args.template,
|
||||
outform = args.outfile.suffix,
|
||||
options = all_options,
|
||||
dir_templates = args.templatesdir,
|
||||
dir_res = dir_res,
|
||||
dir_cache = args.cachedir,
|
||||
log = log,
|
||||
cachedir = args.cachedir,
|
||||
options={"svg_embed_png": "svg_embed_png" in args.export_options},
|
||||
)
|
||||
|
||||
if rv is None:
|
||||
if pic is None:
|
||||
return 1
|
||||
|
||||
log.info("Successfully saved outfile as '{}'".format(rv))
|
||||
log.info("Saving picture as '{}'".format(args.outfile))
|
||||
|
||||
if type(pic) == io.StringIO:
|
||||
openmode = "w"
|
||||
else:
|
||||
openmode = "wb"
|
||||
with args.outfile.open(openmode) as f:
|
||||
f.write(pic.read())
|
||||
|
||||
return 0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def getTournamentTop(
|
||||
id_or_slug,
|
||||
import_options = {}
|
||||
event_slug = None,
|
||||
import_options = [],
|
||||
top = 8,
|
||||
token = "",
|
||||
proxy = None,
|
||||
log=LOG_DUMMY):
|
||||
log=LOG_DUMMY,
|
||||
):
|
||||
"""Returns a tuple : the smashgg.Tournament object and a list of the top
|
||||
smashgg.Player in that tournament."""
|
||||
|
||||
|
@ -605,6 +767,36 @@ def getTournamentTop(
|
|||
|
||||
return event
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Select the specified event
|
||||
def selectEventBySlug(data, slug, log=LOG_DUMMY):
|
||||
|
||||
try:
|
||||
event = data["events"][0]
|
||||
except:
|
||||
log.error("No event found in data")
|
||||
log.debug(data)
|
||||
return None
|
||||
|
||||
for e in data["events"]:
|
||||
try:
|
||||
slug_full = e["slug"]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
if ( slug == slug_full
|
||||
or slug == slug_full.split("/")[-1] ):
|
||||
log.info("Selected Event '{}' by slug '{}'" \
|
||||
.format(
|
||||
e["name"],
|
||||
slug,
|
||||
))
|
||||
return e
|
||||
|
||||
|
||||
log.error("No Event matching slug '{}' found".format(slug))
|
||||
return None
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
data = None
|
||||
|
||||
|
@ -644,7 +836,11 @@ def getTournamentTop(
|
|||
log.error("Failed to load Tournament")
|
||||
return None,None
|
||||
|
||||
event = selectBiggestEvent(tournament_data, log)
|
||||
event = None
|
||||
if event_slug is None:
|
||||
event = selectBiggestEvent(tournament_data, log)
|
||||
else:
|
||||
event = selectEventBySlug(tournament_data, event_slug, log)
|
||||
|
||||
if event is None :
|
||||
return None,None
|
||||
|
@ -653,6 +849,7 @@ def getTournamentTop(
|
|||
tournament = smashgg.Tournament(
|
||||
id = tournament_data["id"],
|
||||
slug = tournament_data["slug"],
|
||||
game = event["videogame"],
|
||||
name = tournament_data["name"],
|
||||
startAt = \
|
||||
datetime.datetime. \
|
||||
|
@ -663,7 +860,10 @@ def getTournamentTop(
|
|||
city = tournament_data["city"],
|
||||
countryCode = tournament_data["countryCode"],
|
||||
hashtag = tournament_data["hashtag"],
|
||||
)
|
||||
) \
|
||||
.clean_name(
|
||||
import_options.get("name_seo_delimiter", None)
|
||||
)
|
||||
|
||||
|
||||
# Get the top players
|
||||
|
@ -688,10 +888,10 @@ def getTournamentTop(
|
|||
["authorizations"] \
|
||||
[0] \
|
||||
["externalUsername"]
|
||||
except TypeError:
|
||||
except:
|
||||
twitterHandle = None
|
||||
|
||||
if import_options.get("use_smashgg_prefixes", True):
|
||||
if "use_smashgg_prefixes" in import_options:
|
||||
prefix = participant_data["prefix"]
|
||||
else:
|
||||
prefix = ""
|
||||
|
@ -733,7 +933,11 @@ def getTournamentTop(
|
|||
log.error("Failed to load Tournament")
|
||||
return None,None
|
||||
|
||||
event = selectBiggestEvent(tournament_data, log)
|
||||
event = None
|
||||
if event_slug is None:
|
||||
event = selectBiggestEvent(tournament_data, log)
|
||||
else:
|
||||
event = selectEventBySlug(tournament_data, event_slug, log)
|
||||
|
||||
if event is None :
|
||||
return None,None
|
||||
|
@ -753,6 +957,7 @@ def getTournamentTop(
|
|||
eid = slct["entrant"]["id"]
|
||||
try:
|
||||
top_players[eid].add_character_selection(
|
||||
game = tournament.game,
|
||||
character = slct["selectionValue"],
|
||||
win = (winnerId == eid),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue