Implement real player active time intervals and historical KPM

This commit is contained in:
devRaGonSa
2026-06-11 12:09:23 +02:00
parent 9e52be98de
commit a9a7e88411
8 changed files with 952 additions and 61 deletions

View File

@@ -5,6 +5,7 @@ from __future__ import annotations
import json
from datetime import datetime, timedelta, timezone
from .config import get_kpm_min_active_seconds
from .historical_storage import ALL_SERVERS_SLUG
from .normalizers import normalize_map_name
from .player_external_profiles import build_external_player_profile_fields
@@ -293,6 +294,12 @@ def _build_player_row(
) -> dict[str, object]:
kills = _coerce_optional_int(row.get("kills")) or 0
deaths = _coerce_optional_int(row.get("deaths")) or 0
player_active_seconds = _coerce_optional_int(row.get("player_active_seconds"))
active_time_payload = _build_player_active_time_payload(
kills=kills,
player_active_seconds=player_active_seconds,
active_time_source=row.get("active_time_source"),
)
return {
"player_name": row.get("player_name"),
"team": row.get("team"),
@@ -303,10 +310,60 @@ def _build_player_row(
"top_weapons": _top_counter(row.get("weapons_json")),
"most_killed": _top_counter(row.get("most_killed_json")),
"death_by": _top_counter(row.get("death_by_json")),
**active_time_payload,
**build_external_player_profile_fields(player_id=row.get("player_id")),
}
def _build_player_active_time_payload(
*,
kills: int,
player_active_seconds: int | None,
active_time_source: object,
) -> dict[str, object]:
min_active_seconds = get_kpm_min_active_seconds()
resolved_source = str(active_time_source or "").strip() or None
is_real_connection_time = resolved_source in {
"connection_intervals",
"connection_intervals_carryover",
}
if player_active_seconds is None:
return {
"player_active_seconds": None,
"player_active_minutes": None,
"kpm": None,
"kpm_status": "missing_active_time",
"active_time_source": resolved_source,
}
player_active_minutes = round(player_active_seconds / 60, 3)
if not is_real_connection_time:
return {
"player_active_seconds": player_active_seconds,
"player_active_minutes": player_active_minutes,
"kpm": None,
"kpm_status": "missing_connection_intervals",
"active_time_source": resolved_source or "event_span_fallback",
}
if player_active_seconds < min_active_seconds:
return {
"player_active_seconds": player_active_seconds,
"player_active_minutes": player_active_minutes,
"kpm": None,
"kpm_status": "insufficient_active_time",
"active_time_source": resolved_source or "event_log",
}
kpm = round(kills / (player_active_seconds / 60), 2)
return {
"player_active_seconds": player_active_seconds,
"player_active_minutes": player_active_minutes,
"kpm": kpm,
"kpm_status": "ready",
"active_time_source": resolved_source or "event_log",
}
def _top_counter(raw_value: object, *, limit: int = 5) -> list[dict[str, object]]:
if not isinstance(raw_value, str) or not raw_value.strip():
return []