mirror of
https://github.com/Jylpah/blitz-tools.git
synced 2026-10-21 10:17:05 +02:00
BattleCategorization() child classes: WIP
This commit is contained in:
1 parent
0fb19c7fcd
commit
0b4615bf90
1 file changed
+388
-285
+388
-285
@@ -90,11 +90,11 @@ def def_value_zero():
|
||||
def def_value_BattleRecord():
|
||||
return BattleRecord()
|
||||
|
||||
class BattleRecordCategory():
|
||||
|
||||
class BattleCategorizationList():
|
||||
|
||||
@staticmethod
|
||||
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()
|
||||
id_max = max(mode_ids)
|
||||
ret_modes = ['-'] * (id_max+16) # create a list of '-' elements
|
||||
@@ -119,7 +119,7 @@ class BattleRecordCategory():
|
||||
|
||||
_BATTLE_MODES = mk_battle_modes.__func__(_battle_modes)
|
||||
|
||||
_result_categories = {
|
||||
_categorizations = {
|
||||
'total' : [ 'TOTAL', 'total' ],
|
||||
'battle_result' : [ 'Result', 'category', [ 'Loss', 'Win', 'Draw']],
|
||||
'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]]
|
||||
}
|
||||
|
||||
_result_categories_default = [
|
||||
_categorizations_default = [
|
||||
'total',
|
||||
'battle_result',
|
||||
'battle_type',
|
||||
@@ -146,214 +146,107 @@ class BattleRecordCategory():
|
||||
'top_tier'
|
||||
]
|
||||
|
||||
# _result_categories_extra = [
|
||||
# 'mastery_badge',
|
||||
# 'room_type',
|
||||
# 'tank_name',
|
||||
# 'map_name',
|
||||
# 'team_result',
|
||||
# 'battle_i',
|
||||
# 'player_wins',
|
||||
# 'player_name',
|
||||
# 'protagonist'
|
||||
# ]
|
||||
|
||||
@classmethod
|
||||
def get_categorizations_all(cls):
|
||||
return cls._categorizations.keys()
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_categorizations_default(cls):
|
||||
return cls._categorizations_default
|
||||
|
||||
|
||||
@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
|
||||
|
||||
RESULT_CAT_FRMT = '{:>20s}'
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_categories_all(cls):
|
||||
return cls._result_categories.keys()
|
||||
|
||||
|
||||
@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()
|
||||
#def __init__(self, cat : str, title: str, cat_type:str, params: list = None ):
|
||||
def __init__(self, cat : str, title: str):
|
||||
self.categorization = cat
|
||||
self.title = title
|
||||
# self.type = cat_type # is this needed?
|
||||
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.type = 'category'
|
||||
|
||||
# self.bucket_labels = list()
|
||||
# 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))
|
||||
|
||||
def get_sub_categories(self):
|
||||
# else:
|
||||
# self.type = 'category'
|
||||
|
||||
def get_categories(self):
|
||||
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):
|
||||
try:
|
||||
cat = self.get_sub_category(result)
|
||||
cat = self.get_category(result)
|
||||
self.categories[cat].record_result(result)
|
||||
self.total_battles += 1
|
||||
return True
|
||||
@@ -369,7 +262,7 @@ class BattleRecordCategory():
|
||||
|
||||
def calc_results(self):
|
||||
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)
|
||||
except Exception as err:
|
||||
bu.error(exception = err)
|
||||
@@ -377,6 +270,7 @@ class BattleRecordCategory():
|
||||
|
||||
def print_results(self):
|
||||
try:
|
||||
# FIX use BattleRecord.get_results_fields()
|
||||
first_btl_record = list(self.categories.values())[0]
|
||||
print(' '.join(first_btl_record.get_headers(self.get_category_name())))
|
||||
for row in self.get_results():
|
||||
@@ -398,7 +292,7 @@ class BattleRecordCategory():
|
||||
row = [ self.RESULT_CAT_FRMT.format(cat) ]
|
||||
row.extend(self.categories[cat].get_results())
|
||||
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()]:
|
||||
row = [ self.RESULT_CAT_FRMT.format(cat) ]
|
||||
row.extend(self.categories[cat].get_results())
|
||||
@@ -433,6 +327,186 @@ class BattleRecordCategory():
|
||||
bu.error('Key not found', err)
|
||||
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():
|
||||
|
||||
## 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%}' ],
|
||||
'win' : [ 'WR', 'Win rate', 6, '{:6.1%}' ],
|
||||
'damage_made' : [ 'DPB', 'Average Damage', 5, '{:5.0f}' ],
|
||||
'frags' : [ 'KDB', 'Kills / Battle', 4, '{:4.2f}' ],
|
||||
'DR' : [ 'DR', 'Damage Ratio', 5, '{:5.1f}' ],
|
||||
'KDR' : [ 'KDR', 'Kills / Death', 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%}']
|
||||
}
|
||||
|
||||
_team_fields = [ 'wins', 'battles', 'damage_dealt' ]
|
||||
|
||||
# fields to display in results
|
||||
_result_fields_default = [
|
||||
'battles',
|
||||
'battles%',
|
||||
'win',
|
||||
'damage_made',
|
||||
'player_wins',
|
||||
'allies_wins',
|
||||
'enemies_wins',
|
||||
'allies_battles',
|
||||
'enemies_battles'
|
||||
]
|
||||
_result_fields_modes = {
|
||||
'default' : [ 'battles', 'battles%', 'win','damage_made', 'frags', 'enemies_spotted', 'top_tier', 'DR', 'survived', 'allies_wins', 'enemies_wins'],
|
||||
'team' : [ 'battles', 'battles%', 'win','player_wins','allies_wins','enemies_wins'],
|
||||
'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' ],
|
||||
'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 ]
|
||||
}
|
||||
|
||||
|
||||
_result_fields_team = [
|
||||
'battles',
|
||||
'player_wins',
|
||||
'allies_wins',
|
||||
'enemies_wins',
|
||||
'allies_damage_dealt',
|
||||
'enemies_damage_dealt',
|
||||
'allies_battles',
|
||||
'enemies_battles'
|
||||
]
|
||||
_team_fields = [ 'wins', 'battles', 'damage_dealt' ]
|
||||
|
||||
# _result_fields_default = [
|
||||
# 'battles',
|
||||
# 'battles%',
|
||||
# 'win',
|
||||
# 'damage_made',
|
||||
# 'player_wins',
|
||||
# 'allies_wins',
|
||||
# 'enemies_wins',
|
||||
# 'allies_battles',
|
||||
# 'enemies_battles'
|
||||
# ]
|
||||
|
||||
|
||||
_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
|
||||
]
|
||||
# _result_fields_team = [
|
||||
# 'battles',
|
||||
# 'player_wins',
|
||||
# 'allies_wins',
|
||||
# 'enemies_wins',
|
||||
# 'allies_damage_dealt',
|
||||
# 'enemies_damage_dealt',
|
||||
# 'allies_battles',
|
||||
# 'enemies_battles'
|
||||
# ]
|
||||
|
||||
_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',
|
||||
MISSING_STATS,
|
||||
'mastery_badge'
|
||||
]
|
||||
|
||||
_result_ratios = {
|
||||
'KDR' : [ 'enemies_destroyed', 'destroyed' ],
|
||||
'DR' : [ 'damage_made', 'damage_received' ],
|
||||
@@ -524,27 +609,38 @@ class BattleRecord():
|
||||
|
||||
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
|
||||
def set_fields(cls, mode: str = None):
|
||||
try:
|
||||
cls._mode = mode
|
||||
|
||||
if cls._mode == OPT_MODE_EXTENDED:
|
||||
cls.result_fields = cls._result_fields_default + cls._result_fields_extended
|
||||
elif cls._mode == OPT_MODE_TEAM:
|
||||
cls.result_fields = cls._result_fields_team
|
||||
else:
|
||||
cls.result_fields = cls._result_fields_default
|
||||
if mode not in cls.get_modes():
|
||||
bu.error('Mode ' + mode + ' not in defined modes: ' + ', '.join(cls.get_modes()))
|
||||
sys.exit(2)
|
||||
|
||||
cls.mode = mode
|
||||
cls.result_fields = cls.get_mode_fields(cls.mode)
|
||||
cls.result_fields_ratio = set(cls._result_ratios.keys()) & set(cls.result_fields)
|
||||
|
||||
# sort results fields according to _results_fields
|
||||
cls.result_fields.sort(key = lambda i: list(cls._result_fields.keys()).index(i))
|
||||
# # sort results fields according to _results_fields
|
||||
# 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:
|
||||
cls.ratio_fields.add(cls._result_ratios[ratio][0])
|
||||
cls.ratio_fields.add(cls._result_ratios[ratio][1])
|
||||
@@ -553,9 +649,18 @@ class BattleRecord():
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_result_fields(cls) -> set:
|
||||
def get_result_fields(cls) -> list:
|
||||
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
|
||||
def get_field_name(cls, field: str):
|
||||
@@ -573,17 +678,7 @@ class BattleRecord():
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_fields_avg(cls) -> set:
|
||||
return cls.avg_fields
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_fields_ratio(cls) -> set:
|
||||
return cls.ratio_fields
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_team_fields(cls) -> list:
|
||||
def get_fields_team(cls) -> list:
|
||||
return cls._team_fields
|
||||
|
||||
|
||||
@@ -779,6 +874,7 @@ class ErrorCatchingArgumentParser(argparse.ArgumentParser):
|
||||
else:
|
||||
raise UserWarning()
|
||||
|
||||
|
||||
## main() -------------------------------------------------------------
|
||||
|
||||
OPT_MODE_DEFAULT = None
|
||||
@@ -794,11 +890,11 @@ async def main(argv):
|
||||
os.chdir(os.path.dirname(sys.argv[0]))
|
||||
|
||||
## Default options:
|
||||
OPT_DB = False
|
||||
OPT_HIST = False
|
||||
OPT_STAT_FUNC = 'player'
|
||||
OPT_WORKERS_N = 10
|
||||
OPT_CATEGORIES = None
|
||||
OPT_DB = False
|
||||
OPT_HIST = False
|
||||
OPT_STAT_FUNC = 'player'
|
||||
OPT_WORKERS_N = 10
|
||||
OPT_CATEGORIZATIONS = 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
|
||||
@@ -829,13 +925,13 @@ async def main(argv):
|
||||
try:
|
||||
if 'ANALYZER' in config.sections():
|
||||
configAnalyzer = config['ANALYZER']
|
||||
OPT_MODE = configAnalyzer.getboolean('opt_mode', OPT_MODE_DEFAULT)
|
||||
OPT_HIST = configAnalyzer.getboolean('opt_hist', OPT_HIST)
|
||||
OPT_STAT_FUNC = configAnalyzer.get('opt_stat_func', fallback=OPT_STAT_FUNC)
|
||||
OPT_WORKERS_N = configAnalyzer.getint('opt_workers', OPT_WORKERS_N)
|
||||
res_categories = configAnalyzer.get('opt_categories', None)
|
||||
if res_categories != None:
|
||||
OPT_CATEGORIES = res_categories.split(',')
|
||||
OPT_MODE = configAnalyzer.getboolean('mode', OPT_MODE_DEFAULT)
|
||||
OPT_HIST = configAnalyzer.getboolean('histograms', OPT_HIST)
|
||||
OPT_STAT_FUNC = configAnalyzer.get('stat_func', fallback=OPT_STAT_FUNC)
|
||||
OPT_WORKERS_N = configAnalyzer.getint('workers', OPT_WORKERS_N)
|
||||
res_categorizations = configAnalyzer.get('categorizations', None)
|
||||
if res_categorizations != None:
|
||||
OPT_CATEGORIZATIONS = res_categorizations.split(',')
|
||||
histogram_fields_str = configAnalyzer.get('histogram_buckets', None)
|
||||
if histogram_fields_str != None:
|
||||
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('-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('--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('--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')
|
||||
@@ -892,7 +988,7 @@ async def main(argv):
|
||||
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')
|
||||
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')
|
||||
|
||||
try:
|
||||
@@ -900,7 +996,7 @@ async def main(argv):
|
||||
except Exception as err:
|
||||
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_progress_step(250) # Set the frequency of the progress dots.
|
||||
@@ -940,6 +1036,9 @@ async def main(argv):
|
||||
if args.mode == OPT_MODE_HELP:
|
||||
await help_extended(db, parser)
|
||||
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
|
||||
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
|
||||
|
||||
# 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 } }'")
|
||||
if db != None:
|
||||
dbc = db[DB_C_REPLAYS]
|
||||
@@ -1075,7 +1178,7 @@ def process_battle_results(results: dict, args : argparse.Namespace, cats: list)
|
||||
categories = {}
|
||||
|
||||
for cat in cats:
|
||||
categories[cat] = BattleRecordCategory(cat)
|
||||
categories[cat] = BattleCategorization(cat)
|
||||
|
||||
max_title_len = 0
|
||||
for result in results:
|
||||
@@ -1180,7 +1283,7 @@ def calc_team_stats(results: list, player_stats : dict, stat_id_map : dict, arg
|
||||
## Bug here??
|
||||
#stat_types = player_stats[list(player_stats.keys())[0]].keys()
|
||||
stat_types = list()
|
||||
stat_types = BattleRecord.get_team_fields()
|
||||
stat_types = BattleRecord.get_fields_team()
|
||||
|
||||
for result in results:
|
||||
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)
|
||||
bu.print_progress()
|
||||
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()
|
||||
continue
|
||||
|
||||
@@ -1656,7 +1759,7 @@ async def read_replay_JSON(replay_json: dict, args : argparse.Namespace) -> dict
|
||||
bu.error(exception=err)
|
||||
return None
|
||||
try:
|
||||
bu.debug('Part 2')
|
||||
#bu.debug('Part 2')
|
||||
result['allies'] = set()
|
||||
result['enemies'] = set()
|
||||
result['allies_survived'] = 0 # for steamroller stats
|
||||
|
||||
Reference in new issue
Block a user