Пример #1
0
async def main(argv):
    # set the directory for the script
    os.chdir(os.path.dirname(sys.argv[0]))

    parser = argparse.ArgumentParser(
        description='Retrieve Tankopedia from WoTinspector.com')
    parser.add_argument('--file',
                        dest="outfile",
                        help='Write Tankopedia to file')
    parser.add_argument('-d',
                        '--debug',
                        action='store_true',
                        default=False,
                        help='Debug mode')
    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        default=False,
                        help='Verbose mode')
    parser.add_argument('-s',
                        '--silent',
                        action='store_true',
                        default=False,
                        help='Silent mode')

    args = parser.parse_args()
    bu.set_log_level(args.silent, args.verbose, args.debug)

    wi = WoTinspector()
    await wi.get_tankopedia(args.outfile)

    await wi.close()
Пример #2
0
async def main(argv):
	global wg, wi, WG_APP_ID
	# set the directory for the script
	current_dir = os.getcwd()
	os.chdir(os.path.dirname(sys.argv[0]))

	## Default options:
	OPT_DB			= False
	OPT_EXTENDED 	= False
	OPT_HIST		= False
	OPT_STAT_FUNC	= 'player'
	OPT_WORKERS_N 	= 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_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
						  ## own server app ID, it will REDUCE the performance
	
	## VERY unlikely you have a DB set up
	DB_SERVER 	= 'localhost'
	DB_PORT 	= 27017
	DB_SSL		= False
	DB_CERT_REQ = ssl.CERT_NONE
	DB_AUTH 	= 'admin'
	DB_NAME 	= 'BlitzStats'
	DB_USER		= '******'
	DB_PASSWD 	= 'PASSWORD'
	DB_CERT 	= None
	DB_CA 		= None
	
	try:
		## Read config
		if os.path.isfile(FILE_CONFIG):
			config = configparser.ConfigParser()
			config.read(FILE_CONFIG)

			try:
				if 'OPTIONS' in config.sections():
					configOptions 	= config['OPTIONS']
					# WG account id of the uploader: 
					# # Find it here: https://developers.wargaming.net/reference/all/wotb/account/list/
					OPT_DB			= configOptions.getboolean('opt_DB', OPT_DB)
					OPT_EXTENDED 	= configOptions.getboolean('opt_analyzer_extended', OPT_EXTENDED)
					OPT_HIST		= configOptions.getboolean('opt_analyzer_hist', OPT_HIST)
					OPT_STAT_FUNC	= configOptions.get('opt_analyzer_stat_func', fallback=OPT_STAT_FUNC)
					OPT_WORKERS_N 	= configOptions.getint('opt_analyzer_workers', OPT_WORKERS_N)
			except (KeyError, configparser.NoSectionError) as err:
				bu.error(exception=err)

			try:
				if 'ANALYZER' in config.sections():
					configAnalyzer	= config['ANALYZER']
					histogram_fields_str = configAnalyzer.get('histogram_buckets', None)
					if histogram_fields_str != None:
						set_histogram_buckets(json.loads(histogram_fields_str))

			except (KeyError, configparser.NoSectionError) as err:
				bu.error(exception=err)

			try:
				if 'WG' in config.sections():
					configWG 		= config['WG']
					WG_ID			= configWG.getint('wg_id', WG_ID)
					WG_ACCOUNT		= configWG.get('wg_account', WG_ACCOUNT)
					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)

			try:
				if 'DATABASE' in config.sections():
					configDB 	= config['DATABASE']
					DB_SERVER 	= configDB.get('db_server', DB_SERVER)
					DB_PORT 	= configDB.getint('db_port', DB_PORT)
					DB_SSL		= configDB.getboolean('db_ssl', DB_SSL)
					DB_CERT_REQ = configDB.getint('db_ssl_req', DB_CERT_REQ)
					DB_AUTH 	= configDB.get('db_auth', DB_AUTH)
					DB_NAME 	= configDB.get('db_name', DB_NAME)
					DB_USER		= configDB.get('db_user', DB_USER)
					DB_PASSWD 	= configDB.get('db_password', DB_PASSWD)
					DB_CERT 	= configDB.get('db_ssl_cert_file', DB_CERT)
					DB_CA 		= configDB.get('db_ssl_ca_file', DB_CA)
			except (KeyError, configparser.NoSectionError)  as 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.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('-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', '--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('--stat_func', default=OPT_STAT_FUNC, choices=STAT_FUNC.keys(), help='Select how to calculate for ally/enemy performance: tank-tier stats, global player stats')
		parser.add_argument('-u', '--url', action='store_true', default=False, help='Print replay URLs')
		parser.add_argument('--tankfile', type=str, default='tanks.json', help='JSON file to read Tankopedia from. Default is "tanks.json"')
		parser.add_argument('--mapfile', type=str, default='maps.json', help='JSON file to read Blitz map names from. Default is "maps.json"')
		parser.add_argument('-o','--outfile', type=str, default='-', metavar="OUTPUT", help='File to write results. Default STDOUT')
		parser.add_argument('--db', action='store_true', default=OPT_DB, help='Use DB - You are unlikely to have it')
		parser.add_argument('--filters', type=str, default=None, help='Filters for DB based analyses. MongoDB find() filter JSON format.')
		parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode')
		parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Verbose mode')
		parser.add_argument('-s', '--silent', action='store_true', default=False, help='Silent mode')
		parser.add_argument('files', metavar='FILE1 [FILE2 ...]', type=str, nargs='+', help='Files/dirs to read. Use \'-\' for STDIN, "db:" for database')
		
		try:
			args = parser.parse_args()
		except Exception as err:
			raise

		bu.set_log_level(args.silent, args.verbose, args.debug)
		bu.set_progress_step(250)  # Set the frequency of the progress dots. 
		
		wg = WG(WG_APP_ID, args.tankfile, args.mapfile, stats_cache=True, rate_limit=WG_RATE_LIMIT)
		wi = WoTinspector(rate_limit=10)

		if args.account != None:
			args.account_id = await wg.get_account_id(args.account)
			bu.debug('WG  account_id: ' + str(args.account_id))

		BattleRecord.set_fields(args.extended)

		#### Connect to MongoDB (TBD)
		bu.debug('DB_SERVER: ' + DB_SERVER)
		bu.debug('DB_PORT: ' + str(DB_PORT))
		bu.debug('DB_SSL: ' + "True" if DB_SSL else "False")
		bu.debug('DB_AUTH: ' + DB_AUTH)
		bu.debug('DB_NAME: ' + DB_NAME)
		
		client = None
		db = None
		if args.db:
			try:
				client = motor.motor_asyncio.AsyncIOMotorClient(DB_SERVER,DB_PORT, authSource=DB_AUTH, username=DB_USER, password=DB_PASSWD, ssl=DB_SSL, ssl_cert_reqs=DB_CERT_REQ, ssl_certfile=DB_CERT, tlsCAFile=DB_CA)
				db = client[DB_NAME]
				args.account_id = None
				bu.debug('Database connection initiated')
			except Exception as err: 
				bu.error("Could no initiate DB connection: Disabling DB", err) 
				args.db = False
				pass
		else:
			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:
			replayQ  = asyncio.Queue(maxsize=1000)			
			reader_tasks = []
			# Make replay Queue

			scanner_task = asyncio.create_task(mk_replayQ(replayQ, args, db))
			bu.debug('Replay scanner started')
			# Start tasks to process the Queue
			for i in range(OPT_WORKERS_N):
				reader_tasks.append(asyncio.create_task(replay_reader(replayQ, i, args)))
				bu.debug('ReplayReader ' + str(i) + ' started')

			bu.debug('Waiting for the replay scanner to finish')
			await asyncio.wait([scanner_task])
			
			# bu.debug('Scanner finished. Waiting for replay readers to finish the queue')
			await replayQ.join()
			await asyncio.sleep(0.1)
			bu.debug('Replays read. Cancelling Readers and analyzing results')
			for task in reader_tasks:
				task.cancel()
				await asyncio.sleep(0.1)	
			results = []
			players = set()
			for res in await asyncio.gather(*reader_tasks):
				results.extend(res[0])
				players.update(res[1])
			if len(players) == 0:
				raise Exception("No players found to fetch stats for. No replays found?")

			(player_stats, stat_id_map) = await process_player_stats(players, OPT_WORKERS_N, args, db)
			bu.verbose('')
			bu.debug('Number of player stats: ' + str(len(player_stats)))
			teamresults = calc_team_stats(results, player_stats, stat_id_map, args)
			process_battle_results(teamresults, args)	
			if args.hist: 
				print('\nPlayer Histograms______', end='', flush=True)
				process_player_dist(results, player_stats, stat_id_map)
			bu.debug('Finished. Cleaning up..................')
		except Exception as err:
			bu.error(exception=err)
	except UserWarning as err:
		bu.verbose(str(err))
		pass
	except Exception as err:
			bu.error(exception=err)
	finally:
		## Need to close the aiohttp.session since Python destructors do not support async methods... 
		if wg != None: await wg.close()
		if wi != None: await wi.close()
	return None
Пример #3
0
async def main(argv):
    global wg, wi
    # set the directory for the script
    os.chdir(os.path.dirname(sys.argv[0]))

    ## Read config
    config = configparser.ConfigParser()
    config.read(FILE_CONFIG)

    configOptions = config['OPTIONS']
    OPT_WORKERS_N = configOptions.getint('opt_uploader_workers', 5)

    configWG = config['WG']
    # WG account id of the uploader:
    # # Find it here: https://developers.wargaming.net/reference/all/wotb/account/list/
    WG_ID = configWG.getint('wg_id', None)
    ## WG API Rules limit 10 request / sec. Higher rate of requests will return errors ==> extra delay
    WG_RATE_LIMIT = configWG.getint('wg_rate_limit', 10)

    parser = argparse.ArgumentParser(
        description=
        'Post replays(s) to WoTinspector.com and retrieve battle data')
    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(
        '-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(
        '--tankopedia',
        type=str,
        default='tanks.json',
        help='JSON file to read Tankopedia from. Default: "tanks.json"')
    parser.add_argument(
        '--mapfile',
        type=str,
        default='maps.json',
        help='JSON file to read Blitz map names from. Default: "maps.json"')
    parser.add_argument('-d',
                        '--debug',
                        action='store_true',
                        default=False,
                        help='Debug mode')
    parser.add_argument('-v',
                        '--verbose',
                        action='store_true',
                        default=False,
                        help='Verbose mode')
    parser.add_argument('-s',
                        '--silent',
                        action='store_true',
                        default=False,
                        help='Silent mode')
    parser.add_argument('files',
                        metavar='FILE1 [FILE2 ...]',
                        type=str,
                        nargs='+',
                        help='Files to read. Use \'-\' for STDIN"')
    args = parser.parse_args()

    bu.set_verbose(args.verbose)
    bu.set_log_level(args.silent, args.verbose, args.debug)

    wg = WG(WG_appID, args.tankopedia, args.mapfile)
    wi = WoTinspector(rate_limit=WG_RATE_LIMIT)

    if args.account != None:
        args.accountID = await wg.get_account_id(args.account)
        bu.debug('WG  account_id: ' + str(args.accountID))

    if args.accountID == None:
        args.accountID = 0

    try:
        queue = asyncio.Queue()

        tasks = []
        # Make replay Queue
        tasks.append(
            asyncio.create_task(mkReplayQ(queue, args.files, args.title)))
        # Start tasks to process the Queue
        for i in range(OPT_WORKERS_N):
            tasks.append(
                asyncio.create_task(
                    replayWorker(queue, i, args.accountID, args.private)))
            bu.debug('Task ' + str(i) + ' started')

        bu.debug('Waiting for the replay scanner to finish')
        await asyncio.wait([tasks[0]])
        bu.debug('Scanner finished. Waiting for workers to finish queue')
        await queue.join()
        bu.debug('Cancelling workers')
        for task in tasks:
            task.cancel()
        bu.debug('Waiting for workers to cancel')
        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')

    except KeyboardInterrupt:
        print('Ctrl-c pressed ...')
        sys.exit(1)
    finally:
        ## Need to close the aiohttp.session since Python destructors do not support async methods...
        await wg.session.close()
        await wi.close()

    return None
Пример #4
0
async def main(argv):
    global wg
    # set the directory for the script
    os.chdir(os.path.dirname(sys.argv[0]))

    ## Read config
    BLITZAPP_FOLDER = '.'
    try:
        if os.path.isfile(FILE_CONFIG):
            config = configparser.ConfigParser()
            config.read(FILE_CONFIG)

            configOptions = config['EXTRACT_TANKOPEDIA']
            BLITZAPP_FOLDER = configOptions.get('blitz_app_dir',
                                                BLITZAPP_FOLDER)
    except:
        pass

    parser = argparse.ArgumentParser(
        description='Extract Tankopedia data from Blitz game files')
    parser.add_argument('blitz_app_base',
                        type=str,
                        nargs='?',
                        metavar="BLITZAPP_FOLDER",
                        default=BLITZAPP_FOLDER,
                        help='Base dir of the Blitz App files')
    parser.add_argument('tanks',
                        type=str,
                        default='tanks.json',
                        nargs='?',
                        metavar="TANKS_FILE",
                        help='File to write Tankopedia')
    parser.add_argument('maps',
                        type=str,
                        default='maps.json',
                        nargs='?',
                        metavar='MAPS_FILE',
                        help='File to write map names')
    arggroup = parser.add_mutually_exclusive_group()
    arggroup.add_argument('-d',
                          '--debug',
                          action='store_true',
                          default=False,
                          help='Debug mode')
    arggroup.add_argument('-v',
                          '--verbose',
                          action='store_true',
                          default=False,
                          help='Verbose mode')
    arggroup.add_argument('-s',
                          '--silent',
                          action='store_true',
                          default=False,
                          help='Silent mode')

    args = parser.parse_args()
    bu.set_log_level(args.silent, args.verbose, args.debug)

    wg = WG()

    tasks = []
    for nation in wg.NATIONS:
        tasks.append(
            asyncio.create_task(extract_tanks(args.blitz_app_base, nation)))

    tanklist = []
    for tanklist_tmp in await asyncio.gather(*tasks):
        tanklist.extend(tanklist_tmp)

    tank_strs, map_strs = await read_user_strs(args.blitz_app_base)

    json_data = None
    userStrs = {}
    tanks = {}
    if os.path.exists(args.tanks):
        try:
            async with aiofiles.open(args.tanks) as infile:
                json_data = json.loads(await infile.read())
                userStrs = json_data['userStr']
                tanks = json_data['data']
        except Exception as err:
            bu.error('Unexpected error when reading file: ' + args.tanks +
                     ' : ' + str(err))

    async with aiofiles.open(args.tanks, 'w', encoding="utf8") as outfile:
        new_tanks, new_userStrs = await convert_tank_names(tanklist, tank_strs)
        # merge old and new tankopedia
        tanks.update(new_tanks)
        userStrs.update(new_userStrs)
        tankopedia = collections.OrderedDict()
        tankopedia['status'] = 'ok'
        tankopedia['meta'] = {"count": len(tanks)}
        tankopedia['data'] = tanks
        tankopedia['userStr'] = userStrs
        bu.verbose_std('New tankopedia \'' + args.tanks + '\' contains ' +
                       str(len(tanks)) + ' tanks')
        await outfile.write(
            json.dumps(tankopedia,
                       ensure_ascii=False,
                       indent=4,
                       sort_keys=False))

    if args.maps != None:
        maps = {}
        if os.path.exists(args.maps):
            try:
                async with aiofiles.open(args.maps) as infile:
                    maps = json.loads(await infile.read())
            except Exception as err:
                bu.error('Unexpected error when reading file: ' + args.maps +
                         ' : ' + str(err))
        # merge old and new map data
        maps.update(map_strs)
        async with aiofiles.open(args.maps, 'w', encoding="utf8") as outfile:
            bu.verbose_std('New maps file \'' + args.maps + '\' contains ' +
                           str(len(maps)) + ' maps')
            await outfile.write(
                json.dumps(maps, ensure_ascii=False, indent=4, sort_keys=True))

    return None
Пример #5
0
async def main(argv):
	global wg, wi
	current_dir = os.getcwd()
	# set the directory for the script
	os.chdir(os.path.dirname(sys.argv[0]))
	
	# options defaults
	OPT_WORKERS_N  	= 5
	WG_ID 			= None
	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:
		## Read config
		if os.path.isfile(FILE_CONFIG):
			bu.debug('Reading config file: ' + FILE_CONFIG)
			config = configparser.ConfigParser()
			config.read(FILE_CONFIG)
			try:
				if 'UPLOADER' in config.sections():
					configUploader 	= config['UPLOADER']
					OPT_WORKERS_N = configUploader.getint('opt_workers', OPT_WORKERS_N)
			except (KeyError, configparser.NoSectionError) as err:
				bu.error(exception=err)
				
			try:
				if 'WG' in config.sections():
					configWG 		= config['WG']
					# WG account id of the uploader: 
					# # Find it here: https://developers.wargaming.net/reference/all/wotb/account/list/
					WG_ID			= configWG.getint('wg_id', WG_ID)
					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:
				bu.error(exception=err)
		
	except Exception as err:
		bu.error(exception=err)

	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('-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 automatic naming')
	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('--mapfile', type=str, default='maps.json', help='JSON file to read Blitz map names from. Default: "maps.json"')
	parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode')
	parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Verbose mode')
	parser.add_argument('-s', '--silent', action='store_true', default=False, help='Silent mode')
	parser.add_argument('files', metavar='FILE1 [FILE2 ...]', type=str, nargs='+', help='Files to read. Use \'-\' for STDIN"')
	args = parser.parse_args()

	bu.set_verbose(args.verbose)
	bu.set_log_level(args.silent, args.verbose, args.debug)

	wg = WG(WG_appID, args.tankopedia, args.mapfile)
	wi = WoTinspector(rate_limit=WG_RATE_LIMIT)

	if args.account != None:
		args.accountID = await wg.get_account_id(args.account)
		bu.debug('WG  account_id: ' + str(args.accountID))

	if args.accountID == None: 
		args.accountID = 0

	try:
		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 = []
		# Make replay Queue
		tasks.append(asyncio.create_task(mkReplayQ(queue, args.files, args.title)))
		# Start tasks to process the Queue
		for i in range(OPT_WORKERS_N):
			tasks.append(asyncio.create_task(replayWorker(queue, i, args.accountID, args.private)))
			bu.debug('Task ' + str(i) + ' started')
		
		bu.debug('Waiting for the replay scanner to finish')
		await asyncio.wait([tasks[0]])
		bu.debug('Scanner finished. Waiting for workers to finish queue')
		await queue.join()
		bu.debug('Cancelling workers')
		for task in tasks:
			task.cancel()
		bu.debug('Waiting for workers to cancel')
		await asyncio.gather(*tasks, return_exceptions=True)
		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:
		print('Ctrl-c pressed ...')
		sys.exit(1)
	finally:
		## Need to close the aiohttp.session since Python destructors do not support async methods... 
		await wg.session.close()
		await wi.close()

	return None