Merge pull request #18 from Jylpah/stable-fixes

Robust config file reading, updated histogram buckets, better warninigs for invalid replays
This commit is contained in:
Jylpah authored and GitHub committed 2020-07-10 12:55:29 +03:00
commit 80adb60cbd
4 files changed
+162 -84

No files matched your search

+55 -44
View File
@@ -74,9 +74,9 @@ replay_details_flds = [
## Syntax: key == stat field in https://api.wotblitz.eu/wotb/tanks/stats/ (all.KEY) ## Syntax: key == stat field in https://api.wotblitz.eu/wotb/tanks/stats/ (all.KEY)
## Value array [ 'Stat Title', [ 0, data buckets ....], scaling_factor_for_bucket_values, 'print_format' ] ## Value array [ 'Stat Title', [ 0, data buckets ....], scaling_factor_for_bucket_values, 'print_format' ]
histogram_fields = { histogram_fields = {
'wins' : [ 'Win rate', [0, .35, .40, .45, .5, .55, .60, .65, 1], 100, '{:.0f}%' ], 'wins' : [ 'Win rate', [0, .35, .40, .45, .48, .5, .52, .55, .60, .65, .70, 1], 100, '{:.0f}%' ],
'damage_dealt' : [ 'Avg. Dmg.', [0, .25e3, .5e3, 1e3, 1.5e3, 2e3,2.5e3,3e3,100e3], 1, '{:.0f}' ], 'damage_dealt' : [ 'Avg. Dmg.', [0, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1750, 2000, 2250, 2500, 2750, 3000, 100e3], 1, '{:.0f}' ],
'battles' : [ 'Battles', [0, 1e3, 3e3, 5e3, 10e3, 15e3, 25e3, 50e3, 5e7], .001, '{:.0f}k'] # battles is a mandatory stat to include 'battles' : [ 'Battles', [0, 1000, 2500, 5000, 7000, 10e3, 15e3, 25e3, 50e3, 5e7], .001, '{:.0f}k'] # battles is a mandatory stat to include
} }
@@ -499,6 +499,7 @@ class ErrorCatchingArgumentParser(argparse.ArgumentParser):
async def main(argv): async def main(argv):
global wg, wi, WG_APP_ID global wg, wi, WG_APP_ID
# set the directory for the script # set the directory for the script
current_dir = os.getcwd()
os.chdir(os.path.dirname(sys.argv[0])) os.chdir(os.path.dirname(sys.argv[0]))
## Default options: ## Default options:
@@ -508,8 +509,11 @@ async def main(argv):
OPT_STAT_FUNC = 'player' OPT_STAT_FUNC = 'player'
OPT_WORKERS_N = 10 OPT_WORKERS_N = 10
WG_ID = None WG_ACCOUNT = None # format: nick@server, where server is either 'eu', 'ru', 'na', 'asia' or 'china'.
#WG_APP_ID = WG_APP_ID # China is not supported since WG API stats are not available there
WG_ID = None # WG account_id in integer format.
# WG_ACCOUNT will be used to retrieve the account_id, but it can be set directly too
# WG_APP_ID = WG_APP_ID
WG_RATE_LIMIT = 10 ## WG standard. Do not edit unless you have your WG_RATE_LIMIT = 10 ## WG standard. Do not edit unless you have your
## own server app ID, it will REDUCE the performance ## own server app ID, it will REDUCE the performance
@@ -532,45 +536,49 @@ async def main(argv):
config.read(FILE_CONFIG) config.read(FILE_CONFIG)
try: try:
configOptions = config['OPTIONS'] if 'OPTIONS' in config.sections():
# WG account id of the uploader: configOptions = config['OPTIONS']
# # Find it here: https://developers.wargaming.net/reference/all/wotb/account/list/ # WG account id of the uploader:
OPT_DB = configOptions.getboolean('opt_DB', OPT_DB) # # Find it here: https://developers.wargaming.net/reference/all/wotb/account/list/
OPT_EXTENDED = configOptions.getboolean('opt_analyzer_extended', OPT_EXTENDED) OPT_DB = configOptions.getboolean('opt_DB', OPT_DB)
OPT_HIST = configOptions.getboolean('opt_analyzer_hist', OPT_HIST) OPT_EXTENDED = configOptions.getboolean('opt_analyzer_extended', OPT_EXTENDED)
OPT_STAT_FUNC = configOptions.get('opt_analyzer_stat_func', fallback=OPT_STAT_FUNC) OPT_HIST = configOptions.getboolean('opt_analyzer_hist', OPT_HIST)
OPT_WORKERS_N = configOptions.getint('opt_analyzer_workers', OPT_WORKERS_N) OPT_STAT_FUNC = configOptions.get('opt_analyzer_stat_func', fallback=OPT_STAT_FUNC)
except configparser.NoSectionError as err: OPT_WORKERS_N = configOptions.getint('opt_analyzer_workers', OPT_WORKERS_N)
except (KeyError, configparser.NoSectionError) as err:
bu.error(exception=err) bu.error(exception=err)
try: try:
configWG = config['WG'] if 'WG' in config.sections():
WG_ID = configWG.getint('wg_id', WG_ID) configWG = config['WG']
WG_APP_ID = configWG.get('wg_app_id', WG_APP_ID) WG_ID = configWG.getint('wg_id', WG_ID)
WG_RATE_LIMIT = configWG.getint('wg_rate_limit', WG_RATE_LIMIT) WG_ACCOUNT = configWG.get('wg_account', WG_ACCOUNT)
except configparser.NoSectionError as err: WG_APP_ID = configWG.get('wg_app_id', WG_APP_ID)
WG_RATE_LIMIT = configWG.getint('wg_rate_limit', WG_RATE_LIMIT)
except (KeyError, configparser.NoSectionError) as err:
bu.error(exception=err) bu.error(exception=err)
try: try:
configDB = config['DATABASE'] if 'DATABASE' in config.sections():
DB_SERVER = configDB.get('db_server', DB_SERVER) configDB = config['DATABASE']
DB_PORT = configDB.getint('db_port', DB_PORT) DB_SERVER = configDB.get('db_server', DB_SERVER)
DB_SSL = configDB.getboolean('db_ssl', DB_SSL) DB_PORT = configDB.getint('db_port', DB_PORT)
DB_CERT_REQ = configDB.getint('db_ssl_req', DB_CERT_REQ) DB_SSL = configDB.getboolean('db_ssl', DB_SSL)
DB_AUTH = configDB.get('db_auth', DB_AUTH) DB_CERT_REQ = configDB.getint('db_ssl_req', DB_CERT_REQ)
DB_NAME = configDB.get('db_name', DB_NAME) DB_AUTH = configDB.get('db_auth', DB_AUTH)
DB_USER = configDB.get('db_user', DB_USER) DB_NAME = configDB.get('db_name', DB_NAME)
DB_PASSWD = configDB.get('db_password', DB_PASSWD) DB_USER = configDB.get('db_user', DB_USER)
DB_CERT = configDB.get('db_ssl_cert_file', DB_CERT) DB_PASSWD = configDB.get('db_password', DB_PASSWD)
DB_CA = configDB.get('db_ssl_ca_file', DB_CA) DB_CERT = configDB.get('db_ssl_cert_file', DB_CERT)
except configparser.NoSectionError as err: DB_CA = configDB.get('db_ssl_ca_file', DB_CA)
except (KeyError, configparser.NoSectionError) as err:
bu.error(exception=err) bu.error(exception=err)
parser = ErrorCatchingArgumentParser(description='Analyze Blitz replay JSON files from WoTinspector.com. Use \'upload_wotb_replays.py\' to upload the replay files first.') parser = ErrorCatchingArgumentParser(description='Analyze Blitz replay JSON files from WoTinspector.com. Use \'upload_wotb_replays.py\' to upload the replay files first.')
parser.add_argument('--output', default='plain', choices=['plain', 'db'], help='Select output mode: plain text or database') parser.add_argument('--output', default='plain', choices=['plain', 'db'], help='Select output mode: plain text or database')
parser.add_argument('-id', dest='account_id', type=int, default=WG_ID, help='WG account_id to analyze') parser.add_argument('-id', dest='account_id', type=int, default=WG_ID, help='WG account_id to analyze')
parser.add_argument('-a', '--account', type=str, default=None, help='WG account nameto analyze. Format: ACCOUNT_NAME@SERVER') parser.add_argument('-a', '--account', type=str, default=WG_ACCOUNT, help='WG account nameto analyze. Format: ACCOUNT_NAME@SERVER')
parser.add_argument('-x', '--extended', action='store_true', default=OPT_EXTENDED, help='Print Extended stats') parser.add_argument('-x', '--extended', action='store_true', default=OPT_EXTENDED, help='Print Extended stats')
parser.add_argument('-X', '--extra_categories', choices=BattleRecordCategory.get_extra_categories(), default=None, nargs='*', help='Print Extended categories') parser.add_argument('-X', '--extra_categories', choices=BattleRecordCategory.get_extra_categories(), default=None, nargs='*', help='Print Extended categories')
parser.add_argument('--hist', action='store_true', default=OPT_HIST, help='Print player histograms (WR/battles)') parser.add_argument('--hist', action='store_true', default=OPT_HIST, help='Print player histograms (WR/battles)')
@@ -622,10 +630,12 @@ async def main(argv):
bu.error("Could no initiate DB connection: Disabling DB", err) bu.error("Could no initiate DB connection: Disabling DB", err)
args.db = False args.db = False
pass pass
else:
if not(args.db):
bu.debug('No DB in use') bu.debug('No DB in use')
# rebase file arguments due to moving the working directory to the script location
args.files = bu.rebase_file_args(current_dir, args.files)
try: try:
replayQ = asyncio.Queue(maxsize=1000) replayQ = asyncio.Queue(maxsize=1000)
reader_tasks = [] reader_tasks = []
@@ -1149,7 +1159,7 @@ async def mk_replayQ(queue : asyncio.Queue, args : argparse.Namespace, db : moto
if (p_replayfile.match(line) != None): if (p_replayfile.match(line) != None):
replay_json = await bu.open_JSON(line, wi.chk_JSON_replay) replay_json = await bu.open_JSON(line, wi.chk_JSON_replay)
await queue.put(await mk_readerQ_item(replay_json)) await queue.put(await mk_readerQ_item(replay_json, line))
except Exception as err: except Exception as err:
bu.error(exception=err) bu.error(exception=err)
else: else:
@@ -1160,7 +1170,7 @@ async def mk_replayQ(queue : asyncio.Queue, args : argparse.Namespace, db : moto
fn = fn[:-1] fn = fn[:-1]
if os.path.isfile(fn) and (p_replayfile.match(fn) != None): if os.path.isfile(fn) and (p_replayfile.match(fn) != None):
replay_json = await bu.open_JSON(fn, wi.chk_JSON_replay) replay_json = await bu.open_JSON(fn, wi.chk_JSON_replay)
await queue.put(await mk_readerQ_item(replay_json)) await queue.put(await mk_readerQ_item(replay_json, fn))
bu.debug('File added to queue: ' + fn) bu.debug('File added to queue: ' + fn)
Nreplays += 1 Nreplays += 1
elif os.path.isdir(fn): elif os.path.isdir(fn):
@@ -1169,7 +1179,7 @@ async def mk_replayQ(queue : asyncio.Queue, args : argparse.Namespace, db : moto
if entry.is_file() and (p_replayfile.match(entry.name) != None): if entry.is_file() and (p_replayfile.match(entry.name) != None):
bu.debug(entry.name) bu.debug(entry.name)
replay_json = await bu.open_JSON(entry.path, wi.chk_JSON_replay) replay_json = await bu.open_JSON(entry.path, wi.chk_JSON_replay)
await queue.put(await mk_readerQ_item(replay_json)) await queue.put(await mk_readerQ_item(replay_json, entry.name))
bu.debug('File added to queue: ' + entry.path) bu.debug('File added to queue: ' + entry.path)
Nreplays += 1 Nreplays += 1
except Exception as err: except Exception as err:
@@ -1178,28 +1188,29 @@ async def mk_replayQ(queue : asyncio.Queue, args : argparse.Namespace, db : moto
return Nreplays return Nreplays
async def mk_readerQ_item(replay_json) -> list: async def mk_readerQ_item(replay_json, filename : str = None) -> list:
"""Make an item to replay queue""" """Make an item to replay queue"""
global REPLAY_N global REPLAY_N
REPLAY_N +=1 REPLAY_N +=1
return [replay_json, REPLAY_N] return [replay_json, REPLAY_N, os.path.basename(filename) ]
async def replay_reader(queue: asyncio.Queue, readerID: int, args : argparse.Namespace): async def replay_reader(queue: asyncio.Queue, readerID: int, args : argparse.Namespace):
"""Async Worker to process the replay queue""" """Async Worker to process the replay queue"""
#global SKIPPED_N #global SKIPPED_N
# account_id = args.account_id
results = [] results = []
playerstanks = set() playerstanks = set()
try: try:
while True: while True:
item = await queue.get() item = await queue.get()
replay_json = item[0] replay_json = item[0]
replayID = item[1] replayID = item[1]
replay_file = item[2]
try: try:
msg_str = 'Replay[' + str(replayID) + ']: '
if replay_json == None: if replay_json == None:
bu.verbose('Replay[' + str(replayID) + ']: is empty. Skipping.' ) bu.warning(msg_str + 'Invalid replay. Skipping: ' + (replay_file if replay_file != None else '') )
#SKIPPED_N += 1 #SKIPPED_N += 1
queue.task_done() queue.task_done()
continue continue
@@ -1209,7 +1220,7 @@ async def replay_reader(queue: asyncio.Queue, readerID: int, args : argparse.Nam
result = await read_replay_JSON(replay_json, args) result = await read_replay_JSON(replay_json, args)
bu.print_progress() bu.print_progress()
if result == None: if result == None:
bu.debug('Replay[' + str(replayID) + ']: Invalid replay', id=readerID) bu.warning(msg_str + 'Invalid replay' + (replay_file if replay_file != None else '') )
queue.task_done() queue.task_done()
continue continue
+7 -1
View File
@@ -5,8 +5,14 @@
#opt_analyzer_stat_func = player #opt_analyzer_stat_func = player
[WG] [WG]
## Find your WG account_id : https://developers.wargaming.net/reference/all/wotb/account/list/ ## Find your WG account_id : https://developers.wargaming.net/reference/all/wotb/account/list/
#wg_id = 0 # wg_id = 0
## wg_account format: nick@server, where server is either 'eu', 'ru', 'na', 'asia' or 'china'.
## China is not supported since WG API stats are not available there
# wg_account = your_nick@eu
## WG API limits requests / sec to 10 for mobile apps. ## WG API limits requests / sec to 10 for mobile apps.
## You can register your "own" server app and ## You can register your "own" server app and
## upgrade the rate limit to 20 requests / sec. ## upgrade the rate limit to 20 requests / sec.
+62 -16
View File
@@ -9,7 +9,7 @@ from progress.counter import Counter
from decimal import Decimal from decimal import Decimal
MAX_RETRIES= 3 MAX_RETRIES= 3
SLEEP = 3 SLEEP = 1.5
LOG_LEVELS = { 'silent': 0, 'normal': 1, 'verbose': 2, 'debug': 3 } LOG_LEVELS = { 'silent': 0, 'normal': 1, 'verbose': 2, 'debug': 3 }
SILENT = 0 SILENT = 0
@@ -210,6 +210,13 @@ def verbose_std(msg = "", id = None) -> bool:
return True return True
return False return False
def warning(msg = "", id = None) -> bool:
"""Print a warning message"""
if _log_level >= NORMAL:
_print_log_msg('', 'Warning: ' + msg, None, id)
return True
return False
def debug(msg = "", id = None, exception = None, force: bool = False) -> bool: def debug(msg = "", id = None, exception = None, force: bool = False) -> bool:
"""print a conditional debug message""" """print a conditional debug message"""
if (_log_level >= DEBUG) or force: if (_log_level >= DEBUG) or force:
@@ -341,6 +348,10 @@ def NOW() -> int:
return int(time.time()) return int(time.time())
def rebase_file_args(current_dir, files):
"""REbase file command line params after moving working dir to the script's dir"""
return [ os.path.join(current_dir, fn) for fn in files ]
async def read_int_list(filename: str) -> list(): async def read_int_list(filename: str) -> list():
"""Read file to a list and return list of integers in the input file""" """Read file to a list and return list of integers in the input file"""
@@ -445,6 +456,40 @@ def bld_dict_hierarcy(d : dict, key : str, value) -> dict:
error('Unexpected Exception', err) error('Unexpected Exception', err)
return None return None
def get_JSON_keypath(keypath: str, key: str):
if keypath == None:
return key
else:
return '.'.join([keypath, key])
def get_JSON_value(json, key : str = None, keys : list = None, keypath = None):
if keys == None:
keys = key.split('.')
if len(keys) == 0:
return json
key = keys.pop(0)
if type(json) == dict:
if key in json:
return get_JSON_value(json[key], keys=keys, keypath=get_JSON_keypath(keypath, key))
else:
raise KeyError('Key: '+ get_JSON_keypath(keypath, key) + ' not found')
if type(json) == list:
p = re.compile(r'^\[(\d+)\]$')
m = p.match(key)
if len(m.groups()) != 1:
raise KeyError('Invalid key given: ' + get_JSON_keypath(keypath, key))
ndx = m.group(1)
try:
return get_JSON_value(json[ndx], keys=keys, keypath=get_JSON_keypath(keypath, key))
except IndexError:
raise KeyError('JSON array index out of range: ' + get_JSON_keypath(keypath, key))
raise KeyError('Key not found: ' + get_JSON_keypath(keypath, keys[0]))
## ----------------------------------------------------------- ## -----------------------------------------------------------
#### Class SlowBar #### Class SlowBar
## ----------------------------------------------------------- ## -----------------------------------------------------------
@@ -1462,41 +1507,39 @@ class WoTinspector:
error(msg_str + 'Unexpected Exception', err) error(msg_str + 'Unexpected Exception', err)
return None return None
json_resp = None
for retry in range(MAX_RETRIES): for retry in range(MAX_RETRIES):
debug(msg_str + 'Posting: ' + title + ' Try #: ' + str(retry + 1) + '/' + str(MAX_RETRIES) ) debug(msg_str + 'Posting: ' + title + ' Try #: ' + str(retry + 1) + '/' + str(MAX_RETRIES) )
try: try:
async with self.session.post(url, headers=headers, data=payload) as resp: async with self.session.post(url, headers=headers, data=payload) as resp:
debug(msg_str + 'HTTP response: '+ str(resp.status)) debug(msg_str + 'HTTP response: '+ str(resp.status))
if resp.status == 200: if resp.status == 200:
debug('HTTP POST 200 = Success. Reading response data') debug(msg_str + 'HTTP POST 200 = Success. Reading response data')
json_resp = await resp.json() json_resp = await resp.json()
if json_resp.get('status', None) == None: if self.chk_JSON_replay(json_resp):
error(msg_str +' : ' + title + ' : Received invalid JSON') debug(msg_str + 'Response data read. Status OK')
elif (json_resp['status'] == 'ok'):
debug('Response data read. Status OK')
return json_resp return json_resp
elif (json_resp['status'] == 'error'): debug(msg_str +' : ' + title + ' : Receive invalid JSON')
error(msg_str + json_resp['error']['message'] + ' : ' + title)
else:
error(msg_str + ' Unspecified error: ' + title)
else: else:
debug(msg_str + 'Got HTTP/' + str(resp.status)) debug(msg_str + 'Got HTTP/' + str(resp.status))
except Exception as err: except Exception as err:
error(exception=err) debug(msg_str, exception=err)
await asyncio.sleep(SLEEP) await asyncio.sleep(SLEEP)
error(msg_str + ' Could not post replay: ' + title) debug(msg_str + ' Could not post replay: ' + title)
return None return json_resp
async def get_replay_listing(self, page: int = 0) -> aiohttp.ClientResponse: async def get_replay_listing(self, page: int = 0) -> aiohttp.ClientResponse:
url = self.get_url_replay_listing(page) url = self.get_url_replay_listing(page)
return await self.session.get(url) return await self.session.get(url)
@classmethod @classmethod
def get_url_replay_listing(cls, page : int): def get_url_replay_listing(cls, page : int):
return cls.URL_REPLAY_LIST + str(page) + '?vt=#filters' return cls.URL_REPLAY_LIST + str(page) + '?vt=#filters'
@classmethod @classmethod
def get_replay_links(cls, doc: str): def get_replay_links(cls, doc: str):
"""Get replay download links from WoTinspector.com replay listing page""" """Get replay download links from WoTinspector.com replay listing page"""
@@ -1513,21 +1556,24 @@ class WoTinspector:
error(exception=err) error(exception=err)
return replay_links return replay_links
@classmethod @classmethod
def get_replay_id(cls, url): def get_replay_id(cls, url):
return url.rsplit('/', 1)[-1] return url.rsplit('/', 1)[-1]
@classmethod @classmethod
def chk_JSON_replay(cls, json_resp): def chk_JSON_replay(cls, json_resp):
""""Check String for being a valid JSON file""" """"Check String for being a valid JSON file"""
try: try:
if ('status' in json_resp) and json_resp['status'] == 'ok' and ('data' in json_resp) and json_resp['data'] != None: if ('status' in json_resp) and json_resp['status'] == 'ok' and \
(get_JSON_value(json_resp, key='data.summary.exp_base') != None) :
debug("JSON check OK") debug("JSON check OK")
return True return True
except KeyError as err: except KeyError as err:
error('Key not found', err) debug('Replay JSON check failed', exception=err)
except: except:
debug("JSON check failed: " + str(json_resp)) debug("Replay JSON check failed: " + str(json_resp))
return False return False
class BlitzStars: class BlitzStars:
+38 -23
View File
@@ -25,42 +25,48 @@ wi = None
async def main(argv): async def main(argv):
global wg, wi global wg, wi
current_dir = os.getcwd()
# set the directory for the script # set the directory for the script
os.chdir(os.path.dirname(sys.argv[0])) os.chdir(os.path.dirname(sys.argv[0]))
# options defaults # options defaults
OPT_WORKERS_N = 5 OPT_WORKERS_N = 5
WG_ID = None WG_ID = None
WG_RATE_LIMIT = 10 WG_ACCOUNT = None # format: nick@server, where server is either 'eu', 'ru', 'na', 'asia' or 'china'.
# China is not supported since WG API stats are not available there
WG_RATE_LIMIT = 10
try: try:
## Read config ## Read config
if os.path.isfile(FILE_CONFIG): if os.path.isfile(FILE_CONFIG):
bu.debug('Reading config file: ' + FILE_CONFIG)
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(FILE_CONFIG) config.read(FILE_CONFIG)
try: try:
configOptions = config['OPTIONS'] if 'OPTIONS' in config.sections():
OPT_WORKERS_N = configOptions.getint('opt_uploader_workers', OPT_WORKERS_N) configOptions = config['OPTIONS']
OPT_WORKERS_N = configOptions.getint('opt_uploader_workers', OPT_WORKERS_N)
except (KeyError, configparser.NoSectionError) as err: except (KeyError, configparser.NoSectionError) as err:
bu.debug("No section 'OPTIONS' found in configuration file: " + FILE_CONFIG) bu.error(exception=err)
try: try:
configWG = config['WG'] if 'WG' in config.sections():
# WG account id of the uploader: configWG = config['WG']
# # Find it here: https://developers.wargaming.net/reference/all/wotb/account/list/ # WG account id of the uploader:
WG_ID = configWG.getint('wg_id', WG_ID) # # Find it here: https://developers.wargaming.net/reference/all/wotb/account/list/
## WG API Rules limit 10 request / sec. Higher rate of requests will return errors ==> extra delay WG_ID = configWG.getint('wg_id', WG_ID)
WG_RATE_LIMIT = configWG.getint('wg_rate_limit', WG_RATE_LIMIT) WG_ACCOUNT = configWG.get('wg_account', WG_ACCOUNT)
## WG API Rules limit 10 request / sec. Higher rate of requests will return errors ==> extra delay
WG_RATE_LIMIT = configWG.getint('wg_rate_limit', WG_RATE_LIMIT)
except (KeyError, configparser.NoSectionError) as err: except (KeyError, configparser.NoSectionError) as err:
bu.debug("No section 'WG' found in configuration file: " + FILE_CONFIG) bu.error(exception=err)
except Exception as err: except Exception as err:
bu.error(exception=err) bu.error(exception=err)
parser = argparse.ArgumentParser(description='Post replays(s) to WoTinspector.com and retrieve replay data as JSON') parser = argparse.ArgumentParser(description='Post replays(s) to WoTinspector.com and retrieve replay data as JSON')
parser.add_argument('-id', dest='accountID', type=int, default=WG_ID, help='WG account_id') parser.add_argument('-id', dest='accountID', type=int, default=WG_ID, help='WG account_id')
parser.add_argument('-a', '--account', dest='account', type=str, default=None, help='Uploader\'s WG account name. Format: ACCOUNT_NAME@SERVER') parser.add_argument('-a', '--account', dest='account', type=str, default=WG_ACCOUNT, help='Uploader\'s WG account name. Format: ACCOUNT_NAME@SERVER')
parser.add_argument('-t','--title', type=str, default=None, help='Title for replays. Use NN for continous numbering. Default is filename-based numbering') parser.add_argument('-t','--title', type=str, default=None, help='Title for replays. Use NN for continous numbering. Default is filename-based numbering')
parser.add_argument('-p', '--private', dest="private", action='store_true', default=False, help='Set replays private on WoTinspector.com') parser.add_argument('-p', '--private', dest="private", action='store_true', default=False, help='Set replays private on WoTinspector.com')
parser.add_argument('--tankopedia', type=str, default='tanks.json', help='JSON file to read Tankopedia from. Default: "tanks.json"') parser.add_argument('--tankopedia', type=str, default='tanks.json', help='JSON file to read Tankopedia from. Default: "tanks.json"')
@@ -86,7 +92,9 @@ async def main(argv):
try: try:
queue = asyncio.Queue() queue = asyncio.Queue()
# rebase file arguments due to moving the working directory to the script location
args.files = bu.rebase_file_args(current_dir, args.files)
tasks = [] tasks = []
# Make replay Queue # Make replay Queue
tasks.append(asyncio.create_task(mkReplayQ(queue, args.files, args.title))) tasks.append(asyncio.create_task(mkReplayQ(queue, args.files, args.title)))
@@ -104,7 +112,7 @@ async def main(argv):
task.cancel() task.cancel()
bu.debug('Waiting for workers to cancel') bu.debug('Waiting for workers to cancel')
await asyncio.gather(*tasks, return_exceptions=True) await asyncio.gather(*tasks, return_exceptions=True)
bu.verbose(str(REPLAY_N) + ' replays: ' + str(REPLAY_N - SKIPPED_N - ERROR_N) + ' uploaded, ' + str(SKIPPED_N) + ' skipped, ' + str(ERROR_N) + ' errors') bu.verbose_std(str(REPLAY_N) + ' replays: ' + str(REPLAY_N - SKIPPED_N - ERROR_N) + ' uploaded, ' + str(SKIPPED_N) + ' skipped, ' + str(ERROR_N) + ' errors')
except KeyboardInterrupt: except KeyboardInterrupt:
print('Ctrl-c pressed ...') print('Ctrl-c pressed ...')
@@ -186,7 +194,7 @@ async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, pri
bu.verbose_std(msg_str + title + ' has already been posted. Skipping.' ) bu.verbose_std(msg_str + title + ' has already been posted. Skipping.' )
else: else:
os.remove(replay_json_fn) os.remove(replay_json_fn)
bu.debug("Replay JSON not valid: Deleting " + replay_json_fn) bu.debug(msg_str + "Replay JSON not valid: Deleting " + replay_json_fn, id=workerID)
SKIPPED_N += 1 SKIPPED_N += 1
queue.task_done() queue.task_done()
continue continue
@@ -203,11 +211,18 @@ async def replayWorker(queue: asyncio.Queue, workerID: int, account_id: int, pri
json_resp = await wi.post_replay(await fp.read(), filename, account_id, title, priv, N) json_resp = await wi.post_replay(await fp.read(), filename, account_id, title, priv, N)
if json_resp != None: if json_resp != None:
if (await bu.save_JSON(replay_json_fn,json_resp)): if (await bu.save_JSON(replay_json_fn,json_resp)):
if not bu.debug(msg_str + 'Replay saved OK: ' + filename): if wi.chk_JSON_replay(json_resp):
bu.verbose_std(msg_str + title + ' posted') if not bu.debug(msg_str + 'Replay saved OK: ' + filename):
bu.verbose_std(msg_str + title + ' posted')
else:
bu.warning(msg_str +'Replay file is not valid: ' + filename)
ERROR_N += 1
else: else:
bu.error(msg_str + 'Error saving replay: ' + filename) bu.error(msg_str + 'Error saving replay: ' + filename)
ERROR_N += 1 ERROR_N += 1
else:
bu.error(msg_str + 'Replay file is not valid: ' + filename)
ERROR_N += 1
except Exception as err: except Exception as err:
bu.error(msg_str + 'Unexpected Exception: ' + str(type(err)) + ' : ' + str(err) ) bu.error(msg_str + 'Unexpected Exception: ' + str(type(err)) + ' : ' + str(err) )
bu.debug(msg_str + 'Marking task done') bu.debug(msg_str + 'Marking task done')