import math
from scipy import stats
from typing import List, Dict, Any
INDIKAATTORI_ASETUKSET = {
'RHR': {'huono': 70.0, 'hyva': 57.0, 'muutos': -0.16},
'FFMI': {'huono': 19.335, 'hyva': 21.055, 'muutos': 0.015},
'WHtR': {'huono': 0.632, 'hyva': 0.500, 'muutos': -0.000554667},
}
KORJAUKSET_NAISILLE = {'FFMI': 3.8, 'RHR': -3.5, 'WHtR': 0.0}
EKSPONENTIN_SKAALAUSKERROIN = 0.02
VAKUUS_KASVU_MAX_PROSENTTI_PER_VIIKKO = 0.01
PUUTTUVA_PISTE_SAKKO_KERROIN = 0.005
def _calculate_collateral_from_indicators(jakson_pituus_viikkoina: int, alkuperainen_vakuus: float, puuttuvien_pisteiden_lkm: int, indicator_values: Dict[str, List[float | None]]) -> float:
"""Laskee vakuuden arvon käsiteltyjen indikaattoridatan perusteella."""
kokonaiskerroin = 1.0
for avain, asetukset in INDIKAATTORI_ASETUKSET.items():
valid_datapoints = [(i, val) for i, val in enumerate(indicator_values.get(avain, [])) if val is not None]
if len(valid_datapoints) < 2:
continue
x_arvot = [p[0] for p in valid_datapoints]
y_arvot = [p[1] for p in valid_datapoints]
viikottainen_kehitys, _, _, _, _ = stats.linregress(x_arvot, y_arvot)
keskiverto_taso = sum(y_arvot) / len(y_arvot)
tavoite = (asetukset['hyva'] - keskiverto_taso) / (asetukset['hyva'] - asetukset['huono'])
tavoite = max(0.0, min(tavoite, 1.0))
kehitys_suhteessa_tasoon = viikottainen_kehitys / asetukset['muutos'] - tavoite
kerroin = math.exp(kehitys_suhteessa_tasoon * EKSPONENTIN_SKAALAUSKERROIN)
kokonaiskerroin *= kerroin
kokonaiskerroin = min(kokonaiskerroin, 1 + VAKUUS_KASVU_MAX_PROSENTTI_PER_VIIKKO)
kokonaiskerroin **= jakson_pituus_viikkoina
kokonaiskerroin *= (1 - PUUTTUVA_PISTE_SAKKO_KERROIN) ** puuttuvien_pisteiden_lkm
lopullinen_vakuus = alkuperainen_vakuus * kokonaiskerroin
return lopullinen_vakuus
def laske_viikkoarvio(is_female: bool, pituus_cm: float, viikkomittaukset: List[Dict[str, Any]], alkuperainen_vakuus: float, offset: float) -> float:
"""Laskee uuden vakuuden arvon annettujen mittausten perusteella. Datan tulee edustaa kunkin viikon keskimääräisiä mittauksia."""
pituus_m = pituus_cm / 100.0
puuttuvien_pisteiden_lkm = 0
indicator_values = {
'RHR': [None] * len(viikkomittaukset),
'FFMI': [None] * len(viikkomittaukset),
'WHtR': [None] * len(viikkomittaukset)
}
jakson_pituus_viikkoina = len(viikkomittaukset) - 1
for i, mittaus in enumerate(viikkomittaukset):
if 'leposyke' in mittaus and mittaus['leposyke'] is not None:
rhr_val = mittaus['leposyke']
rhr_val += KORJAUKSET_NAISILLE['RHR'] if is_female else 0
indicator_values['RHR'][i] = rhr_val
else:
puuttuvien_pisteiden_lkm += 1
if 'paino_kg' in mittaus and mittaus['paino_kg'] is not None and \
'rasvaprosentti' in mittaus and mittaus['rasvaprosentti'] is not None:
ffmi_val = mittaus['paino_kg'] * (1 - (mittaus['rasvaprosentti']+offset) / 100) / (pituus_m ** 2)
ffmi_val += KORJAUKSET_NAISILLE['FFMI'] if is_female else 0
indicator_values['FFMI'][i] = ffmi_val
else:
puuttuvien_pisteiden_lkm += 1
if 'vyotaronymarys_cm' in mittaus and mittaus['vyotaronymarys_cm'] is not None:
whtr_val = mittaus['vyotaronymarys_cm'] / pituus_cm
whtr_val += KORJAUKSET_NAISILLE['WHtR'] if is_female else 0
indicator_values['WHtR'][i] = whtr_val
else:
puuttuvien_pisteiden_lkm += 1
return _calculate_collateral_from_indicators(jakson_pituus_viikkoina, alkuperainen_vakuus, puuttuvien_pisteiden_lkm, indicator_values)