You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

42 lines
2.0 KiB

"""Pure eligibility rules for Phase 5 voice prompting."""
from __future__ import annotations
from .config_loader import VoicePromptSettings
from .models import TrackEvent, TrackedPerson, TurnTrackState, VoicePromptTrackState
def trigger_matches(
config: VoicePromptSettings, person: TrackedPerson, events: list[TrackEvent],
) -> bool:
"""Return whether the configured spatial/track trigger occurred."""
own_events = {event.event_type for event in events if event.track_id == person.track_id}
if config.trigger_strategy == "trigger_line_crossing":
return "trigger_crossed" in own_events
if config.trigger_strategy == "region_entry":
return "region_entered" in own_events
if config.trigger_strategy == "first_confirmed_track":
return person.is_confirmed
value = person.foot_x if config.fixed_position_axis == "x" else person.foot_y
return value >= config.fixed_position_value if config.fixed_position_direction == "increasing" else value <= config.fixed_position_value
def eligibility_reason(
config: VoicePromptSettings, person: TrackedPerson, state: VoicePromptTrackState,
turn_state: TurnTrackState | None, cooldown_remaining: float, nominal_fps: float = 30.0,
) -> str:
if config.one_prompt_per_track and (state.prompt_attempted or state.pseudo_prompt):
return "already_prompted"
if config.require_confirmed_track and not person.is_confirmed:
return "unconfirmed_track"
if config.require_inside_region and not person.inside_evaluation_region:
return "outside_region"
if person.missed_frames > 0:
return "stale_track"
if person.track_age_frames <= 0 or person.track_age_frames / max(nominal_fps, 1.0) < config.minimum_track_age_sec:
return "track_too_young"
if config.skip_if_already_turned and turn_state is not None and turn_state.turn_confirmed:
return "already_turned"
if cooldown_remaining > 0:
return "global_cooldown"
return "eligible"