mirror of
https://github.com/Jylpah/blitz-tools.git
synced 2026-10-24 03:29:54 +02:00
BattleCategorization() child classes: WIP
This commit is contained in:
1 parent
0fb19c7fcd
commit
0b4615bf90
1 file changed
+385
-282
+385
-282
@@ -90,11 +90,11 @@ def def_value_zero():
|
|||||||
def def_value_BattleRecord():
|
def def_value_BattleRecord():
|
||||||
return BattleRecord()
|
return BattleRecord()
|
||||||
|
|
||||||
class BattleRecordCategory():
|
class BattleCategorizationList():
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def mk_battle_modes(modes: dict):
|
def mk_battle_modes(modes: dict):
|
||||||
"""Make a list of battle modes as required for the _result_categories"""
|
"""Make a list of battle modes as required for the _categorizations"""
|
||||||
mode_ids = modes.values()
|
mode_ids = modes.values()
|
||||||
id_max = max(mode_ids)
|
id_max = max(mode_ids)
|
||||||
ret_modes = ['-'] * (id_max+16) # create a list of '-' elements
|
ret_modes = ['-'] * (id_max+16) # create a list of '-' elements
|
||||||
@@ -119,7 +119,7 @@ class BattleRecordCategory():
|
|||||||
|
|
||||||
_BATTLE_MODES = mk_battle_modes.__func__(_battle_modes)
|
_BATTLE_MODES = mk_battle_modes.__func__(_battle_modes)
|
||||||
|
|
||||||
_result_categories = {
|
_categorizations = {
|
||||||
'total' : [ 'TOTAL', 'total' ],
|
'total' : [ 'TOTAL', 'total' ],
|
||||||
'battle_result' : [ 'Result', 'category', [ 'Loss', 'Win', 'Draw']],
|
'battle_result' : [ 'Result', 'category', [ 'Loss', 'Win', 'Draw']],
|
||||||
'battle_type' : [ 'Battle Type', 'category', ['Encounter', 'Supremacy']],
|
'battle_type' : [ 'Battle Type', 'category', ['Encounter', 'Supremacy']],
|
||||||
@@ -138,7 +138,7 @@ class BattleRecordCategory():
|
|||||||
'player_damage_dealt' : [ 'Player Avg Dmg', 'bucket', [ 0, 500, 1000, 1250, 1500, 1750, 2000, 2500]]
|
'player_damage_dealt' : [ 'Player Avg Dmg', 'bucket', [ 0, 500, 1000, 1250, 1500, 1750, 2000, 2500]]
|
||||||
}
|
}
|
||||||
|
|
||||||
_result_categories_default = [
|
_categorizations_default = [
|
||||||
'total',
|
'total',
|
||||||
'battle_result',
|
'battle_result',
|
||||||
'battle_type',
|
'battle_type',
|
||||||
@@ -146,214 +146,107 @@ class BattleRecordCategory():
|
|||||||
'top_tier'
|
'top_tier'
|
||||||
]
|
]
|
||||||
|
|
||||||
# _result_categories_extra = [
|
|
||||||
# 'mastery_badge',
|
@classmethod
|
||||||
# 'room_type',
|
def get_categorizations_all(cls):
|
||||||
# 'tank_name',
|
return cls._categorizations.keys()
|
||||||
# 'map_name',
|
|
||||||
# 'team_result',
|
|
||||||
# 'battle_i',
|
@classmethod
|
||||||
# 'player_wins',
|
def get_categorizations_default(cls):
|
||||||
# 'player_name',
|
return cls._categorizations_default
|
||||||
# 'protagonist'
|
|
||||||
# ]
|
|
||||||
|
@classmethod
|
||||||
|
def get_categorizations_extra(cls) -> list:
|
||||||
|
#return cls._categorizations_extra
|
||||||
|
return list( set(cls._categorizations.keys() ) - set(cls.get_categorizations_default() ) )
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_categorization_title(cls, cat: str) -> str:
|
||||||
|
try:
|
||||||
|
return cls._categorizations[cat][0]
|
||||||
|
except Exception as err:
|
||||||
|
bu.error(exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_categorization_type(cls, cat: str) -> str:
|
||||||
|
try:
|
||||||
|
return cls._categorizations[cat][1]
|
||||||
|
except Exception as err:
|
||||||
|
bu.error(exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_categorization_params(cls, cat: str) -> list:
|
||||||
|
try:
|
||||||
|
if len(cls._categorizations[cat]) > 2:
|
||||||
|
return cls._categorizations[cat][3:]
|
||||||
|
except Exception as err:
|
||||||
|
bu.error(exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, default_cats: list, args : argparse.Namespace):
|
||||||
|
extra_cats = args.extra
|
||||||
|
only_extra = args.only_extra
|
||||||
|
|
||||||
|
cats = list()
|
||||||
|
if not only_extra:
|
||||||
|
if default_cats == None:
|
||||||
|
cats = self.get_categorizations_default()
|
||||||
|
else:
|
||||||
|
cats = default_cats
|
||||||
|
|
||||||
|
if extra_cats != None:
|
||||||
|
cats = cats + extra_cats
|
||||||
|
|
||||||
|
cats = list(set(cats)) # remove duplicates
|
||||||
|
# ordering
|
||||||
|
cats = [ cat for cat in self._categorizations if cat in cats ]
|
||||||
|
|
||||||
|
self.categorizations = dict()
|
||||||
|
for cat in cats:
|
||||||
|
ctitle = self.get_categorization_title(cat)
|
||||||
|
ctype = self.get_categorization_type(cat)
|
||||||
|
cparams = self.get_categorization_params(cat)
|
||||||
|
self.categorizations[cat] = BattleCategorization(cat, ctitle, ctype, cparams)
|
||||||
|
|
||||||
|
|
||||||
|
class BattleCategorization():
|
||||||
|
|
||||||
total_battles = 0
|
total_battles = 0
|
||||||
|
|
||||||
RESULT_CAT_FRMT = '{:>20s}'
|
RESULT_CAT_FRMT = '{:>20s}'
|
||||||
|
|
||||||
|
#def __init__(self, cat : str, title: str, cat_type:str, params: list = None ):
|
||||||
@classmethod
|
def __init__(self, cat : str, title: str):
|
||||||
def get_categories_all(cls):
|
self.categorization = cat
|
||||||
return cls._result_categories.keys()
|
self.title = title
|
||||||
|
# self.type = cat_type # is this needed?
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_default_categories(cls):
|
|
||||||
return cls._result_categories_default
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_result_categories(cls, default_cats: list, args : argparse.Namespace):
|
|
||||||
extra_cats = args.extra
|
|
||||||
only_extra = args.only_extra
|
|
||||||
|
|
||||||
if only_extra:
|
|
||||||
res_categories = list()
|
|
||||||
else:
|
|
||||||
if default_cats == None:
|
|
||||||
res_categories = cls.get_default_categories()
|
|
||||||
else:
|
|
||||||
res_categories = default_cats
|
|
||||||
if extra_cats != None:
|
|
||||||
res_categories = res_categories + extra_cats
|
|
||||||
res_categories = list(set(res_categories)) # remove duplicates
|
|
||||||
|
|
||||||
return [ cat for cat in cls._result_categories if cat in res_categories ]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_extra_categories(cls) -> list:
|
|
||||||
#return cls._result_categories_extra
|
|
||||||
return list( set(cls._result_categories.keys() ) - set(cls.get_default_categories() ) )
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, cat_name : str):
|
|
||||||
self.category_name = cat_name
|
|
||||||
self.bucket_labels = list()
|
|
||||||
self.categories = collections.defaultdict(def_value_BattleRecord)
|
self.categories = collections.defaultdict(def_value_BattleRecord)
|
||||||
if self._result_categories[self.category_name][1] == 'string':
|
|
||||||
self.type = 'string'
|
|
||||||
elif self._result_categories[self.category_name][1] == 'number':
|
|
||||||
self.type = 'number'
|
|
||||||
elif self._result_categories[self.category_name][1] == 'total':
|
|
||||||
self.type = 'total'
|
|
||||||
elif self._result_categories[self.category_name][1] == 'bucket':
|
|
||||||
self.type = 'bucket'
|
|
||||||
self.bucket_format = self.get_bucket_label_format()
|
|
||||||
self.buckets = self.get_bucket_breaks()
|
|
||||||
for start in range(len(self.buckets)):
|
|
||||||
self.bucket_labels.append(self.mk_bucket_label(start, self.buckets))
|
|
||||||
|
|
||||||
else:
|
# self.bucket_labels = list()
|
||||||
self.type = 'category'
|
# if self.type == 'bucket':
|
||||||
|
# self.bucket_format = self.get_label_prefix()
|
||||||
|
# self.buckets = BattleCategorizationList.get_bucket_breaks(self.categorization)
|
||||||
|
# for start in range(len(self.buckets)):
|
||||||
|
# self.bucket_labels.append(self.mk_bucket_label(start, self.buckets))
|
||||||
|
|
||||||
|
# else:
|
||||||
|
# self.type = 'category'
|
||||||
|
|
||||||
def get_sub_categories(self):
|
def get_categories(self):
|
||||||
return self.categories
|
return self.categories
|
||||||
|
|
||||||
|
|
||||||
def get_fields(self):
|
|
||||||
"""Return fields as dict"""
|
|
||||||
key = next(iter(self.get_sub_categories()))
|
|
||||||
sub_category = self.categories[key]
|
|
||||||
fields = dict()
|
|
||||||
for col in sub_category.get_result_fields():
|
|
||||||
fields[col] = sub_category.get_field_name(col)
|
|
||||||
return fields
|
|
||||||
|
|
||||||
|
|
||||||
def get_category_name(self) -> str:
|
|
||||||
return self._result_categories[self.category_name][0]
|
|
||||||
|
|
||||||
|
|
||||||
def get_category(self, id):
|
|
||||||
"""Get category of 'category' type of BattleRecordCategory"""
|
|
||||||
try:
|
|
||||||
return self._result_categories[self.category_name][2][id]
|
|
||||||
except Exception as err:
|
|
||||||
bu.error(exception=err)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_bucket_breaks(self) -> list:
|
|
||||||
"""Return bucket breaks"""
|
|
||||||
try:
|
|
||||||
return sorted(self._result_categories[self.category_name][2])
|
|
||||||
except Exception as err:
|
|
||||||
bu.error(exception=err)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_bucket_label_format(self) -> str:
|
|
||||||
"""Return bucket label type, if any"""
|
|
||||||
try:
|
|
||||||
if len(self._result_categories[self.category_name]) > 3:
|
|
||||||
return self._result_categories[self.category_name][3]
|
|
||||||
return ''
|
|
||||||
except Exception as err:
|
|
||||||
bu.error(exception=err)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_bucket(self, result: dict) -> str:
|
|
||||||
try:
|
|
||||||
ndx = self.find_bucket(result[self.category_name], self.buckets)
|
|
||||||
return self.bucket_labels[ndx]
|
|
||||||
except Exception as err:
|
|
||||||
bu.error('Category: ' + self.category_name)
|
|
||||||
bu.error('Unknown error', exception=err)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def find_bucket(self, value, buckets):
|
|
||||||
"""Find bucket for value"""
|
|
||||||
max = len(buckets)
|
|
||||||
if max <= 1:
|
|
||||||
return 0
|
|
||||||
ndx = int(max//2)
|
|
||||||
if value > buckets[ndx]:
|
|
||||||
return ndx + self.find_bucket(value, buckets[ndx:])
|
|
||||||
elif value < buckets[ndx]:
|
|
||||||
return self.find_bucket(value, buckets[:ndx])
|
|
||||||
else:
|
|
||||||
return ndx
|
|
||||||
|
|
||||||
|
|
||||||
def mk_bucket_label(self, ndx: int, buckets: list) -> str:
|
|
||||||
try:
|
|
||||||
low = buckets[ndx]
|
|
||||||
if ndx >= len(buckets)-1:
|
|
||||||
high = None
|
|
||||||
else:
|
|
||||||
high = buckets[min(ndx+1, len(buckets)-1)]
|
|
||||||
|
|
||||||
prefix = ''
|
|
||||||
if self.bucket_format == '%':
|
|
||||||
frmt = '{:2.0f}'
|
|
||||||
if low != None:
|
|
||||||
low *= 100
|
|
||||||
if high != None:
|
|
||||||
high *= 100
|
|
||||||
prefix = '%'
|
|
||||||
elif self.bucket_format == 'ratio':
|
|
||||||
frmt = '{:.2f}'
|
|
||||||
else:
|
|
||||||
frmt = '{:.0f}'
|
|
||||||
|
|
||||||
label = frmt.format(low) + ' - '
|
|
||||||
if high != None:
|
|
||||||
label = label + frmt.format(high)
|
|
||||||
else:
|
|
||||||
label = label + ' '
|
|
||||||
label = label + prefix
|
|
||||||
return label
|
|
||||||
except KeyError as err:
|
|
||||||
bu.error('KeyError: ndx=' + str(ndx) + ' buckets=' + str(buckets), exception=err)
|
|
||||||
except Exception as err:
|
|
||||||
bu.error('Unknown error', exception=err)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_sub_category(self, result: dict) -> str:
|
|
||||||
"""Get category"""
|
|
||||||
try:
|
|
||||||
cat = None
|
|
||||||
if self.type == 'total':
|
|
||||||
cat = 'Total'
|
|
||||||
elif self.type == 'number':
|
|
||||||
cat = str(result[self.category_name])
|
|
||||||
elif self.type == 'string':
|
|
||||||
cat = result[self.category_name]
|
|
||||||
elif self.type == 'category':
|
|
||||||
cat = self.get_category(result[self.category_name])
|
|
||||||
#cat = self._result_categories[self.category_name][2][result[self.category_name]]
|
|
||||||
elif self.type == 'bucket':
|
|
||||||
cat = self.get_bucket(result)
|
|
||||||
return cat
|
|
||||||
except KeyError as err:
|
|
||||||
bu.error('Category: ', str(cat))
|
|
||||||
bu.error('Key not found', exception=err)
|
|
||||||
except Exception as err:
|
|
||||||
bu.error('Category: ', str(cat))
|
|
||||||
bu.error('Unknown error', exception=err)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def record_result(self, result: dict):
|
def record_result(self, result: dict):
|
||||||
try:
|
try:
|
||||||
cat = self.get_sub_category(result)
|
cat = self.get_category(result)
|
||||||
self.categories[cat].record_result(result)
|
self.categories[cat].record_result(result)
|
||||||
self.total_battles += 1
|
self.total_battles += 1
|
||||||
return True
|
return True
|
||||||
@@ -369,7 +262,7 @@ class BattleRecordCategory():
|
|||||||
|
|
||||||
def calc_results(self):
|
def calc_results(self):
|
||||||
try:
|
try:
|
||||||
for sub_cat in self.get_sub_categories().keys():
|
for sub_cat in self.get_categories().keys():
|
||||||
self.categories[sub_cat].calc_results(self.total_battles)
|
self.categories[sub_cat].calc_results(self.total_battles)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
bu.error(exception = err)
|
bu.error(exception = err)
|
||||||
@@ -377,6 +270,7 @@ class BattleRecordCategory():
|
|||||||
|
|
||||||
def print_results(self):
|
def print_results(self):
|
||||||
try:
|
try:
|
||||||
|
# FIX use BattleRecord.get_results_fields()
|
||||||
first_btl_record = list(self.categories.values())[0]
|
first_btl_record = list(self.categories.values())[0]
|
||||||
print(' '.join(first_btl_record.get_headers(self.get_category_name())))
|
print(' '.join(first_btl_record.get_headers(self.get_category_name())))
|
||||||
for row in self.get_results():
|
for row in self.get_results():
|
||||||
@@ -398,7 +292,7 @@ class BattleRecordCategory():
|
|||||||
row = [ self.RESULT_CAT_FRMT.format(cat) ]
|
row = [ self.RESULT_CAT_FRMT.format(cat) ]
|
||||||
row.extend(self.categories[cat].get_results())
|
row.extend(self.categories[cat].get_results())
|
||||||
results.append(row)
|
results.append(row)
|
||||||
if self.type == 'bucket':
|
elif self.type == 'bucket':
|
||||||
for cat in [ c for c in self.bucket_labels if c in self.categories.keys()]:
|
for cat in [ c for c in self.bucket_labels if c in self.categories.keys()]:
|
||||||
row = [ self.RESULT_CAT_FRMT.format(cat) ]
|
row = [ self.RESULT_CAT_FRMT.format(cat) ]
|
||||||
row.extend(self.categories[cat].get_results())
|
row.extend(self.categories[cat].get_results())
|
||||||
@@ -433,6 +327,186 @@ class BattleRecordCategory():
|
|||||||
bu.error('Key not found', err)
|
bu.error('Key not found', err)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
## Get it gtorm BattleRecord.get_result_fields()
|
||||||
|
# def get_fields(self):
|
||||||
|
# """Return fields as dict"""
|
||||||
|
# key = next(iter(self.get_categories()))
|
||||||
|
# sub_category = self.categories[key]
|
||||||
|
# fields = dict()
|
||||||
|
# for col in sub_category.get_result_fields():
|
||||||
|
# fields[col] = sub_category.get_field_name(col)
|
||||||
|
# return fields
|
||||||
|
|
||||||
|
### PER Child Class
|
||||||
|
def get_category(self, result: dict) -> str:
|
||||||
|
"""Get category"""
|
||||||
|
try:
|
||||||
|
cat = None
|
||||||
|
if self.type == 'total':
|
||||||
|
cat = 'Total'
|
||||||
|
elif self.type == 'number':
|
||||||
|
cat = str(result[self.categorization])
|
||||||
|
elif self.type == 'string':
|
||||||
|
cat = result[self.categorization]
|
||||||
|
elif self.type == 'category':
|
||||||
|
cat = self.get_category(result[self.categorization])
|
||||||
|
#cat = self._categorizations[self.categorization][2][result[self.categorization]]
|
||||||
|
elif self.type == 'bucket':
|
||||||
|
cat = self.get_bucket(result)
|
||||||
|
return cat
|
||||||
|
except KeyError as err:
|
||||||
|
bu.error('Category: ', str(cat))
|
||||||
|
bu.error('Key not found', exception=err)
|
||||||
|
except Exception as err:
|
||||||
|
bu.error('Category: ', str(cat))
|
||||||
|
bu.error('Unknown error', exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class BattleTotals(BattleCategorization):
|
||||||
|
def get_category(self, result: dict) -> str:
|
||||||
|
return 'total'
|
||||||
|
|
||||||
|
|
||||||
|
class BattleClassCategorization(BattleCategorization):
|
||||||
|
"""Class for categorization by fixed classes with ID (int)"""
|
||||||
|
def __init__(self, cat : str, title: str, classes: list):
|
||||||
|
super().__init__(cat, title)
|
||||||
|
self.categories = classes
|
||||||
|
|
||||||
|
|
||||||
|
def get_category(self, result: dict) -> str:
|
||||||
|
"""Get category"""
|
||||||
|
cat_id = -1
|
||||||
|
try:
|
||||||
|
cat_id = result[self.categorization]
|
||||||
|
return self.categories[cat_id]
|
||||||
|
except KeyError as err:
|
||||||
|
bu.error('Category: ' + str(self.categorization) + 'cat ID: ' + str(cat_id), exception=err)
|
||||||
|
except Exception as err:
|
||||||
|
bu.error('Category: ' + str(self.categorization) + 'cat ID: ' + str(cat_id), exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class BattleNumberCategorization(BattleCategorization):
|
||||||
|
"""Class for categorization by number"""
|
||||||
|
def get_category(self, result: dict) -> str:
|
||||||
|
"""Get category as """
|
||||||
|
try:
|
||||||
|
return str(result[self.categorization])
|
||||||
|
except KeyError as err:
|
||||||
|
bu.error('Category: ', str(self.categorization), exception=err)
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
bu.error('Category: ', str(self.categorization), exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class BattleBucketCategorization(BattleCategorization):
|
||||||
|
"""Class for categorization by buckets based on value"""
|
||||||
|
|
||||||
|
def __init__(self, cat : str, title: str, breaks: list, bucket_format = ''):
|
||||||
|
super().__init__(cat, title)
|
||||||
|
self.breaks = breaks
|
||||||
|
self.bucket_format = bucket_format
|
||||||
|
self.categories = self.mk_bucket_labels(breaks)
|
||||||
|
|
||||||
|
|
||||||
|
def get_category(self, result: dict) -> str:
|
||||||
|
"""Get category"""
|
||||||
|
cat_id = -1
|
||||||
|
try:
|
||||||
|
cat_id = self.find_bucket(result[self.categorization], self.breaks)
|
||||||
|
return self.categories[cat_id]
|
||||||
|
except KeyError as err:
|
||||||
|
bu.error('Category: ' + str(self.categorization) + 'cat ID: ' + str(cat_id), exception=err)
|
||||||
|
except Exception as err:
|
||||||
|
bu.error('Category: ' + str(self.categorization) + 'cat ID: ' + str(cat_id), exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_bucket_breaks(self) -> list:
|
||||||
|
"""Return bucket breaks"""
|
||||||
|
try:
|
||||||
|
return sorted(self._categorizations[self.categorization][2])
|
||||||
|
except Exception as err:
|
||||||
|
bu.error(exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_label_prefix(self) -> str:
|
||||||
|
"""Return bucket label type, if any"""
|
||||||
|
try:
|
||||||
|
if len(self._categorizations[self.categorization]) > 3:
|
||||||
|
return self._categorizations[self.categorization][3]
|
||||||
|
return ''
|
||||||
|
except Exception as err:
|
||||||
|
bu.error(exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# def get_bucket(self, result: dict) -> str:
|
||||||
|
# try:
|
||||||
|
# ndx = self.find_bucket(result[self.categorization], self.buckets)
|
||||||
|
# return self.bucket_labels[ndx]
|
||||||
|
# except Exception as err:
|
||||||
|
# bu.error('Category: ' + self.categorization + ' Unknown error', exception=err)
|
||||||
|
# return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_bucket(self, value, bucket_breaks: list):
|
||||||
|
"""Find bucket for value"""
|
||||||
|
max = len(bucket_breaks)
|
||||||
|
if max <= 1:
|
||||||
|
return 0
|
||||||
|
ndx = int(max//2)
|
||||||
|
if value > bucket_breaks[ndx]:
|
||||||
|
return ndx + self.find_bucket(value, bucket_breaks[ndx:])
|
||||||
|
elif value < bucket_breaks[ndx]:
|
||||||
|
return self.find_bucket(value, bucket_breaks[:ndx])
|
||||||
|
else:
|
||||||
|
return ndx
|
||||||
|
|
||||||
|
|
||||||
|
def mk_bucket_labels(self, bucket_breaks: list) -> list:
|
||||||
|
try:
|
||||||
|
# low = bucket_breaks[ndx]
|
||||||
|
# if ndx >= len(bucket_breaks)-1:
|
||||||
|
# high = None
|
||||||
|
# else:
|
||||||
|
# high = bucket_breaks[min(ndx+1, len(bucket_breaks)-1)]
|
||||||
|
multiplier = 1
|
||||||
|
if self.bucket_format == '%':
|
||||||
|
frmt = '{:2.0f}'
|
||||||
|
multiplier = 100
|
||||||
|
prefix = '%'
|
||||||
|
elif self.bucket_format == 'float':
|
||||||
|
frmt = '{:.2f}'
|
||||||
|
else:
|
||||||
|
frmt = '{:.0f}'
|
||||||
|
|
||||||
|
labels = list()
|
||||||
|
for ndx in range(0,len(bucket_breaks) - 1):
|
||||||
|
low = bucket_breaks[ndx]
|
||||||
|
high = bucket_breaks[ndx +1]
|
||||||
|
|
||||||
|
## IF NONE
|
||||||
|
|
||||||
|
label = frmt.format(low) + ' - '
|
||||||
|
if high != None:
|
||||||
|
label = label + frmt.format(high)
|
||||||
|
else:
|
||||||
|
label = label + ' '
|
||||||
|
label = label + prefix
|
||||||
|
return label
|
||||||
|
except KeyError as err:
|
||||||
|
bu.error('KeyError: ndx=' + str(ndx) + ' buckets=' + str(bucket_breaks), exception=err)
|
||||||
|
except Exception as err:
|
||||||
|
bu.error('Unknown error', exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class BattleRecord():
|
class BattleRecord():
|
||||||
|
|
||||||
## Syntax: Check how the replay JSON files look. The algorithm is counting/recording fields
|
## Syntax: Check how the replay JSON files look. The algorithm is counting/recording fields
|
||||||
@@ -441,6 +515,7 @@ class BattleRecord():
|
|||||||
'battles%' : [ '% Battles', 'Share of Battles', 6, '{:6.0%}' ],
|
'battles%' : [ '% Battles', 'Share of Battles', 6, '{:6.0%}' ],
|
||||||
'win' : [ 'WR', 'Win rate', 6, '{:6.1%}' ],
|
'win' : [ 'WR', 'Win rate', 6, '{:6.1%}' ],
|
||||||
'damage_made' : [ 'DPB', 'Average Damage', 5, '{:5.0f}' ],
|
'damage_made' : [ 'DPB', 'Average Damage', 5, '{:5.0f}' ],
|
||||||
|
'frags' : [ 'KDB', 'Kills / Battle', 4, '{:4.2f}' ],
|
||||||
'DR' : [ 'DR', 'Damage Ratio', 5, '{:5.1f}' ],
|
'DR' : [ 'DR', 'Damage Ratio', 5, '{:5.1f}' ],
|
||||||
'KDR' : [ 'KDR', 'Kills / Death', 4, '{:4.1f}' ],
|
'KDR' : [ 'KDR', 'Kills / Death', 4, '{:4.1f}' ],
|
||||||
'enemies_spotted' : [ 'Spot', 'Enemies spotted per battle', 4, '{:4.1f}' ],
|
'enemies_spotted' : [ 'Spot', 'Enemies spotted per battle', 4, '{:4.1f}' ],
|
||||||
@@ -460,54 +535,64 @@ class BattleRecord():
|
|||||||
MISSING_STATS : [ 'No stats', 'Players without stats avail', 8, '{:8.1%}']
|
MISSING_STATS : [ 'No stats', 'Players without stats avail', 8, '{:8.1%}']
|
||||||
}
|
}
|
||||||
|
|
||||||
_team_fields = [ 'wins', 'battles', 'damage_dealt' ]
|
|
||||||
|
|
||||||
# fields to display in results
|
# fields to display in results
|
||||||
_result_fields_default = [
|
_result_fields_modes = {
|
||||||
'battles',
|
'default' : [ 'battles', 'battles%', 'win','damage_made', 'frags', 'enemies_spotted', 'top_tier', 'DR', 'survived', 'allies_wins', 'enemies_wins'],
|
||||||
'battles%',
|
'team' : [ 'battles', 'battles%', 'win','player_wins','allies_wins','enemies_wins'],
|
||||||
'win',
|
'team-extended' : [ 'battles', 'battles%', 'win','player_wins','allies_wins','enemies_wins', 'player_damage_dealt', 'allies_damage_dealt', 'enemies_damage_dealt', 'player_battles','allies_battles','enemies_battles' ],
|
||||||
'damage_made',
|
'extended' : [ 'battles', 'battles%', 'win','damage_made','enemies_spotted', 'top_tier', 'DR', 'KDR', 'hit_rate', 'pen_rate', 'survived', 'time_alive%', 'player_wins', 'allies_damage_wins', 'enemies_wins', MISSING_STATS ]
|
||||||
'player_wins',
|
}
|
||||||
'allies_wins',
|
|
||||||
'enemies_wins',
|
|
||||||
'allies_battles',
|
|
||||||
'enemies_battles'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
_result_fields_team = [
|
_team_fields = [ 'wins', 'battles', 'damage_dealt' ]
|
||||||
'battles',
|
|
||||||
'player_wins',
|
# _result_fields_default = [
|
||||||
'allies_wins',
|
# 'battles',
|
||||||
'enemies_wins',
|
# 'battles%',
|
||||||
'allies_damage_dealt',
|
# 'win',
|
||||||
'enemies_damage_dealt',
|
# 'damage_made',
|
||||||
'allies_battles',
|
# 'player_wins',
|
||||||
'enemies_battles'
|
# 'allies_wins',
|
||||||
]
|
# 'enemies_wins',
|
||||||
|
# 'allies_battles',
|
||||||
|
# 'enemies_battles'
|
||||||
|
# ]
|
||||||
|
|
||||||
|
|
||||||
_result_fields_extended = [
|
# _result_fields_team = [
|
||||||
'enemies_spotted',
|
# 'battles',
|
||||||
'top_tier',
|
# 'player_wins',
|
||||||
'DR',
|
# 'allies_wins',
|
||||||
'KDR',
|
# 'enemies_wins',
|
||||||
'hit_rate',
|
# 'allies_damage_dealt',
|
||||||
'pen_rate',
|
# 'enemies_damage_dealt',
|
||||||
'survived',
|
# 'allies_battles',
|
||||||
'time_alive%',
|
# 'enemies_battles'
|
||||||
'player_battles',
|
# ]
|
||||||
'allies_damage_dealt',
|
|
||||||
'enemies_damage_dealt',
|
|
||||||
MISSING_STATS
|
|
||||||
]
|
|
||||||
|
|
||||||
_result_counts = [
|
|
||||||
|
# _result_fields_extended = [
|
||||||
|
# 'enemies_spotted',
|
||||||
|
# 'top_tier',
|
||||||
|
# 'DR',
|
||||||
|
# 'KDR',
|
||||||
|
# 'hit_rate',
|
||||||
|
# 'pen_rate',
|
||||||
|
# 'survived',
|
||||||
|
# 'time_alive%',
|
||||||
|
# 'player_battles',
|
||||||
|
# 'allies_damage_dealt',
|
||||||
|
# 'enemies_damage_dealt',
|
||||||
|
# MISSING_STATS
|
||||||
|
# ]
|
||||||
|
|
||||||
|
count_fields = [
|
||||||
'battles',
|
'battles',
|
||||||
MISSING_STATS,
|
MISSING_STATS,
|
||||||
'mastery_badge'
|
'mastery_badge'
|
||||||
]
|
]
|
||||||
|
|
||||||
_result_ratios = {
|
_result_ratios = {
|
||||||
'KDR' : [ 'enemies_destroyed', 'destroyed' ],
|
'KDR' : [ 'enemies_destroyed', 'destroyed' ],
|
||||||
'DR' : [ 'damage_made', 'damage_received' ],
|
'DR' : [ 'damage_made', 'damage_received' ],
|
||||||
@@ -524,27 +609,38 @@ class BattleRecord():
|
|||||||
|
|
||||||
RESULT_CAT_HEADER_FRMT = '{:_<20s}'
|
RESULT_CAT_HEADER_FRMT = '{:_<20s}'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_modes(cls) -> list:
|
||||||
|
return list(cls._result_fields_modes.keys())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_mode_fields(cls, mode: str):
|
||||||
|
try:
|
||||||
|
return cls._result_fields_modes[mode]
|
||||||
|
except Exception as err:
|
||||||
|
bu.error(exception=err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_fields(cls, mode: str = None):
|
def set_fields(cls, mode: str = None):
|
||||||
try:
|
try:
|
||||||
cls._mode = mode
|
if mode not in cls.get_modes():
|
||||||
|
bu.error('Mode ' + mode + ' not in defined modes: ' + ', '.join(cls.get_modes()))
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
if cls._mode == OPT_MODE_EXTENDED:
|
cls.mode = mode
|
||||||
cls.result_fields = cls._result_fields_default + cls._result_fields_extended
|
cls.result_fields = cls.get_mode_fields(cls.mode)
|
||||||
elif cls._mode == OPT_MODE_TEAM:
|
|
||||||
cls.result_fields = cls._result_fields_team
|
|
||||||
else:
|
|
||||||
cls.result_fields = cls._result_fields_default
|
|
||||||
cls.result_fields_ratio = set(cls._result_ratios.keys()) & set(cls.result_fields)
|
cls.result_fields_ratio = set(cls._result_ratios.keys()) & set(cls.result_fields)
|
||||||
|
|
||||||
# sort results fields according to _results_fields
|
# # sort results fields according to _results_fields
|
||||||
cls.result_fields.sort(key = lambda i: list(cls._result_fields.keys()).index(i))
|
# cls.result_fields.sort(key = lambda i: list(cls._result_fields.keys()).index(i))
|
||||||
|
|
||||||
|
cls.avg_fields = set(cls.result_fields) - set(cls._result_ratios.keys()) - set(cls.count_fields)
|
||||||
|
|
||||||
|
# for field in (set(cls.count_fields) & cls.avg_fields):
|
||||||
|
# cls.avg_fields.remove(field)
|
||||||
|
|
||||||
cls.avg_fields = set(cls.result_fields)
|
|
||||||
for field in (set(cls._result_counts) & cls.avg_fields):
|
|
||||||
cls.avg_fields.remove(field)
|
|
||||||
cls.avg_fields.difference_update(set(cls._result_ratios.keys()))
|
|
||||||
for ratio in cls.result_fields_ratio:
|
for ratio in cls.result_fields_ratio:
|
||||||
cls.ratio_fields.add(cls._result_ratios[ratio][0])
|
cls.ratio_fields.add(cls._result_ratios[ratio][0])
|
||||||
cls.ratio_fields.add(cls._result_ratios[ratio][1])
|
cls.ratio_fields.add(cls._result_ratios[ratio][1])
|
||||||
@@ -553,9 +649,18 @@ class BattleRecord():
|
|||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_result_fields(cls) -> set:
|
def get_result_fields(cls) -> list:
|
||||||
return cls.result_fields
|
return cls.result_fields
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_avg_fields(cls) -> set:
|
||||||
|
return cls.avg_fields
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_ratio_fields(cls) -> set:
|
||||||
|
return cls.ratio_fields
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_field_name(cls, field: str):
|
def get_field_name(cls, field: str):
|
||||||
@@ -573,17 +678,7 @@ class BattleRecord():
|
|||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_fields_avg(cls) -> set:
|
def get_fields_team(cls) -> list:
|
||||||
return cls.avg_fields
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fields_ratio(cls) -> set:
|
|
||||||
return cls.ratio_fields
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_team_fields(cls) -> list:
|
|
||||||
return cls._team_fields
|
return cls._team_fields
|
||||||
|
|
||||||
|
|
||||||
@@ -779,6 +874,7 @@ class ErrorCatchingArgumentParser(argparse.ArgumentParser):
|
|||||||
else:
|
else:
|
||||||
raise UserWarning()
|
raise UserWarning()
|
||||||
|
|
||||||
|
|
||||||
## main() -------------------------------------------------------------
|
## main() -------------------------------------------------------------
|
||||||
|
|
||||||
OPT_MODE_DEFAULT = None
|
OPT_MODE_DEFAULT = None
|
||||||
@@ -794,11 +890,11 @@ async def main(argv):
|
|||||||
os.chdir(os.path.dirname(sys.argv[0]))
|
os.chdir(os.path.dirname(sys.argv[0]))
|
||||||
|
|
||||||
## Default options:
|
## Default options:
|
||||||
OPT_DB = False
|
OPT_DB = False
|
||||||
OPT_HIST = False
|
OPT_HIST = False
|
||||||
OPT_STAT_FUNC = 'player'
|
OPT_STAT_FUNC = 'player'
|
||||||
OPT_WORKERS_N = 10
|
OPT_WORKERS_N = 10
|
||||||
OPT_CATEGORIES = None
|
OPT_CATEGORIZATIONS = None
|
||||||
|
|
||||||
WG_ACCOUNT = None # format: nick@server, where server is either 'eu', 'ru', 'na', 'asia' or 'china'.
|
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
|
# China is not supported since WG API stats are not available there
|
||||||
@@ -829,13 +925,13 @@ async def main(argv):
|
|||||||
try:
|
try:
|
||||||
if 'ANALYZER' in config.sections():
|
if 'ANALYZER' in config.sections():
|
||||||
configAnalyzer = config['ANALYZER']
|
configAnalyzer = config['ANALYZER']
|
||||||
OPT_MODE = configAnalyzer.getboolean('opt_mode', OPT_MODE_DEFAULT)
|
OPT_MODE = configAnalyzer.getboolean('mode', OPT_MODE_DEFAULT)
|
||||||
OPT_HIST = configAnalyzer.getboolean('opt_hist', OPT_HIST)
|
OPT_HIST = configAnalyzer.getboolean('histograms', OPT_HIST)
|
||||||
OPT_STAT_FUNC = configAnalyzer.get('opt_stat_func', fallback=OPT_STAT_FUNC)
|
OPT_STAT_FUNC = configAnalyzer.get('stat_func', fallback=OPT_STAT_FUNC)
|
||||||
OPT_WORKERS_N = configAnalyzer.getint('opt_workers', OPT_WORKERS_N)
|
OPT_WORKERS_N = configAnalyzer.getint('workers', OPT_WORKERS_N)
|
||||||
res_categories = configAnalyzer.get('opt_categories', None)
|
res_categorizations = configAnalyzer.get('categorizations', None)
|
||||||
if res_categories != None:
|
if res_categorizations != None:
|
||||||
OPT_CATEGORIES = res_categories.split(',')
|
OPT_CATEGORIZATIONS = res_categorizations.split(',')
|
||||||
histogram_fields_str = configAnalyzer.get('histogram_buckets', None)
|
histogram_fields_str = configAnalyzer.get('histogram_buckets', None)
|
||||||
if histogram_fields_str != None:
|
if histogram_fields_str != None:
|
||||||
set_histogram_buckets(json.loads(histogram_fields_str))
|
set_histogram_buckets(json.loads(histogram_fields_str))
|
||||||
@@ -878,7 +974,7 @@ async def main(argv):
|
|||||||
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=WG_ACCOUNT, 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('--mode', default=OPT_MODE, choices=OPT_MODES, help='Select stats mode. Options: ' + ', '.join(OPT_MODES[1:]))
|
parser.add_argument('--mode', default=OPT_MODE, choices=OPT_MODES, help='Select stats mode. Options: ' + ', '.join(OPT_MODES[1:]))
|
||||||
parser.add_argument('--extra', choices=BattleRecordCategory.get_categories_all(), default=None, nargs='*', help='Print extra categories: ' + ', '.join( cat + '=' + BattleRecordCategory._result_categories[cat][0] for cat in BattleRecordCategory.get_categories_all()))
|
parser.add_argument('--extra', choices=BattleCategorizationList.get_categorizations_all(), default=None, nargs='*', help='Print extra categories: ' + ', '.join( cat + '=' + BattleCategorization._categorizations[cat][0] for cat in BattleCategorization.get_categorizations_all()))
|
||||||
parser.add_argument('--only_extra', action='store_true', default=False, help='Print only the extra categories')
|
parser.add_argument('--only_extra', action='store_true', default=False, help='Print only the extra categories')
|
||||||
parser.add_argument('--hist', action='store_true', default=OPT_HIST, help='Print player histograms: ' + ', '.join( histogram_fields[k][0] for k in histogram_fields))
|
parser.add_argument('--hist', action='store_true', default=OPT_HIST, help='Print player histograms: ' + ', '.join( histogram_fields[k][0] for k in histogram_fields))
|
||||||
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('--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')
|
||||||
@@ -892,7 +988,7 @@ async def main(argv):
|
|||||||
parser.add_argument('-d', '--debug', action='store_true', default=False, help='Debug mode')
|
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('-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('-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')
|
parser.add_argument('files', metavar='FILE1 [FILE2 ...]', type=str, nargs='*', help='Files/dirs to read. Use \'-\' for STDIN, "db:" for database')
|
||||||
## parser.add_argument('--help_filters', action='store_true', default=False, help='Extended help for --filters')
|
## parser.add_argument('--help_filters', action='store_true', default=False, help='Extended help for --filters')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -900,7 +996,7 @@ async def main(argv):
|
|||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
res_categories = BattleRecordCategory.get_result_categories(OPT_CATEGORIES, args)
|
res_categories = BattleCategorization.get_categorizations(OPT_CATEGORIZATIONS, args)
|
||||||
|
|
||||||
bu.set_log_level(args.silent, args.verbose, args.debug)
|
bu.set_log_level(args.silent, args.verbose, args.debug)
|
||||||
bu.set_progress_step(250) # Set the frequency of the progress dots.
|
bu.set_progress_step(250) # Set the frequency of the progress dots.
|
||||||
@@ -940,6 +1036,9 @@ async def main(argv):
|
|||||||
if args.mode == OPT_MODE_HELP:
|
if args.mode == OPT_MODE_HELP:
|
||||||
await help_extended(db, parser)
|
await help_extended(db, parser)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
elif len(args.files) == 0:
|
||||||
|
bu.error('No FILES argument given: No replays to analyse')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# rebase file arguments due to moving the working directory to the script location
|
# rebase file arguments due to moving the working directory to the script location
|
||||||
args.files = bu.rebase_file_args(current_dir, args.files)
|
args.files = bu.rebase_file_args(current_dir, args.files)
|
||||||
@@ -1008,7 +1107,11 @@ async def help_extended(db : motor.motor_asyncio.AsyncIOMotorDatabase = None, pa
|
|||||||
# Result category options
|
# Result category options
|
||||||
|
|
||||||
# Filter usage
|
# Filter usage
|
||||||
print("\nSyntax: --filters '{ \"replay.param\": VALUE, \"replay.param2\": { \"$MONGO_OPERATOR\" : VALUE }, ... }'")
|
print('\n-------------------------------------------------------------------')
|
||||||
|
print('| Filter usage |')
|
||||||
|
print('-------------------------------------------------------------------')
|
||||||
|
|
||||||
|
print("Syntax: --filters '{ \"replay.param\": VALUE, \"replay.param2\": { \"$MONGO_OPERATOR\" : VALUE }, ... }'")
|
||||||
print("\tExample: --filters '{ \"data.summary.protagonist\": ACCOUNT_ID, \"data.summary.battle_start_timestamp\": { \"$gt\" : 1602853200 } }'")
|
print("\tExample: --filters '{ \"data.summary.protagonist\": ACCOUNT_ID, \"data.summary.battle_start_timestamp\": { \"$gt\" : 1602853200 } }'")
|
||||||
if db != None:
|
if db != None:
|
||||||
dbc = db[DB_C_REPLAYS]
|
dbc = db[DB_C_REPLAYS]
|
||||||
@@ -1075,7 +1178,7 @@ def process_battle_results(results: dict, args : argparse.Namespace, cats: list)
|
|||||||
categories = {}
|
categories = {}
|
||||||
|
|
||||||
for cat in cats:
|
for cat in cats:
|
||||||
categories[cat] = BattleRecordCategory(cat)
|
categories[cat] = BattleCategorization(cat)
|
||||||
|
|
||||||
max_title_len = 0
|
max_title_len = 0
|
||||||
for result in results:
|
for result in results:
|
||||||
@@ -1180,7 +1283,7 @@ def calc_team_stats(results: list, player_stats : dict, stat_id_map : dict, arg
|
|||||||
## Bug here??
|
## Bug here??
|
||||||
#stat_types = player_stats[list(player_stats.keys())[0]].keys()
|
#stat_types = player_stats[list(player_stats.keys())[0]].keys()
|
||||||
stat_types = list()
|
stat_types = list()
|
||||||
stat_types = BattleRecord.get_team_fields()
|
stat_types = BattleRecord.get_fields_team()
|
||||||
|
|
||||||
for result in results:
|
for result in results:
|
||||||
try:
|
try:
|
||||||
@@ -1590,7 +1693,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.warning(msg_str + 'Invalid replay' + (replay_file if replay_file != None else '') )
|
bu.warning(msg_str + 'Invalid replay ' + (replay_file if replay_file != None else '') )
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1656,7 +1759,7 @@ async def read_replay_JSON(replay_json: dict, args : argparse.Namespace) -> dict
|
|||||||
bu.error(exception=err)
|
bu.error(exception=err)
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
bu.debug('Part 2')
|
#bu.debug('Part 2')
|
||||||
result['allies'] = set()
|
result['allies'] = set()
|
||||||
result['enemies'] = set()
|
result['enemies'] = set()
|
||||||
result['allies_survived'] = 0 # for steamroller stats
|
result['allies_survived'] = 0 # for steamroller stats
|
||||||
|
|||||||
Reference in new issue
Block a user