Files
hockey_new/khl_site/career_stats.py
2026-09-27 00:25:40 +03:00

53 lines
3.2 KiB
Python

"""Career totals: count quantities, recompute percentages, never add averages."""
from __future__ import annotations
import math
try:
from .person_data import number, stage_name
except ImportError:
from person_data import number, stage_name
SUM = set('gp g a pts pm plus minus pim g_even g_pp g_sh g_ot gwg sds shots faceoffs faceoff_wins hits blocks fouls_against takeaways interceptions w l shootout_games shots_against ga sv so toi'.split())
AVERAGES = set('toi_avg sft_avg shots_avg pim_avg toi_even_avg sft_even_avg toi_pp_avg sft_pp_avg toi_sh_avg sft_sh_avg'.split())
def minutes(value):
if isinstance(value, str) and ':' in value:
try:
m, s = value.split(':');return int(m) + int(s) / 60 if 0 <= int(s) < 60 else None
except (ValueError, TypeError):return None
return number(value)
def aggregate(player, stages, mode='all'):
records, seen = [], set()
for sid, record in (player.get('stages') or {}).items():
meta = stages.get(str(sid), {})
kind = {'Регулярный чемпионат':'regular','Плей-офф':'playoff','Кубок Надежды':'hope'}.get(stage_name(meta.get('title')),'unknown')
if mode != 'all' and kind != mode:continue
data = record.get('data') or {};team = data.get('team') or {}
if str(team.get('name') or '').casefold().startswith('всего'):continue
stats = {str(x.get('id')): x.get('val') for x in data.get('stats') or []}
key = (meta.get('season'),kind,team.get('name'),tuple(sorted((k,str(v)) for k,v in stats.items())))
if key in seen or not stats:continue
seen.add(key);records.append(stats)
result = {}
for key in SUM:
vals = [(minutes if key == 'toi' else number)(r.get(key)) for r in records]
# Unknown values remain unknown; don't pass partial sums off as a career total.
if vals and all(v is not None for v in vals):result[key]=sum(vals)
for key in AVERAGES:
parse = minutes if key.startswith('toi') else number
pairs = [(parse(r.get(key)),number(r.get('gp'))) for r in records]
if pairs and all(v is not None and weight is not None for v,weight in pairs):
weight=sum(w for _,w in pairs)
if weight:result[key]=sum(v*w for v,w in pairs)/weight
for target, numerator, denominator in [('shot_pct','g','shots'),('sv_pct','sv','shots_against'),('faceoff_pct','faceoff_wins','faceoffs')]:
if result.get(denominator) and numerator in result:result[target]=100*result[numerator]/result[denominator]
if result.get('toi') and 'ga' in result:result['gaa']=60*result['ga']/result['toi']
for key in ('toi', 'toi_avg','toi_even_avg','toi_pp_avg','toi_sh_avg'):
if key in result:
seconds=round(result[key]*60);result[key]=f'{seconds//60}:{seconds%60:02d}'
return {'stage_count':len(records),'source':'computed_seasons','stats':[{'id':k,'title':k,'val':round(v,2) if isinstance(v,float) else v} for k,v in sorted(result.items())]}
def career_modes(player, stages):
exact=player.get('site_aggregates') or {}
return {mode: exact[mode] if (exact.get(mode) or {}).get('stats') else aggregate(player,stages,mode) for mode in ('all','regular','playoff','hope')}