"""Event-level feature calculations from parser output.""" from __future__ import annotations from typing import Any from libs.schemas.types import ConfidenceOutput, GuidanceOutput, RiskFlagsOutput, SignalsOutput def guidance_direction_score(guidance: GuidanceOutput) -> float: """raised=1.0, inline_or_maintained=0.5, lowered=0.0, else=0.25.""" mapping = { "raised": 1.0, "inline_or_maintained": 0.5, "lowered": 0.0, "withdrawn": 0.0, "not_provided": 0.25, "unclear": 0.25, } return mapping.get(guidance.status, 0.25) def oneoff_penalty(risk_flags: RiskFlagsOutput) -> float: """Sum of active risk flags / total flags. Higher = more risk.""" flags = [ risk_flags.oneoff_item, risk_flags.tax_benefit, risk_flags.valuation_gain, risk_flags.non_gaap_heavy, risk_flags.financing_related, risk_flags.legal_or_regulatory_overhang, ] total = len(flags) active = sum(flags) return active / total if total > 0 else 0.0 def signal_strength_score(signals: SignalsOutput) -> float: """Composite score [0..1] of positive business signals.""" score = 0.0 weights = { "demand_strength": {"strong": 1.0, "stable": 0.5, "weakening": 0.0, "unknown": 0.0}, "pricing_power": {"present": 1.0, "mixed": 0.5, "absent": 0.0, "unknown": 0.0}, "backlog_or_bookings": {"present": 1.0, "mixed": 0.5, "absent": 0.0, "unknown": 0.0}, "customer_expansion": {"present": 1.0, "mixed": 0.5, "absent": 0.0, "unknown": 0.0}, "margin_quality": {"improving": 1.0, "stable": 0.5, "deteriorating": 0.0, "unknown": 0.0}, } total_weight = len(weights) for field, mapping in weights.items(): val = getattr(signals, field) score += mapping.get(val, 0.0) return score / total_weight if total_weight > 0 else 0.0 def document_quality_score(confidence: ConfidenceOutput) -> float: """Weighted average of confidence dimensions.""" return ( confidence.overall * 0.4 + confidence.event_type * 0.2 + confidence.event_direction * 0.2 + confidence.guidance * 0.1 + confidence.risk_flags * 0.1 ) def compute_event_features(parser_output: dict[str, Any]) -> dict[str, Any]: """Compute all event features from raw parser output dict.""" from libs.schemas.types import ( ConfidenceOutput, GuidanceOutput, RiskFlagsOutput, SignalsOutput, ) guidance = GuidanceOutput.model_validate(parser_output["guidance"]) signals = SignalsOutput.model_validate(parser_output["signals"]) risk_flags = RiskFlagsOutput.model_validate(parser_output["risk_flags"]) confidence = ConfidenceOutput.model_validate(parser_output["confidence"]) return { "guidance_direction_score": guidance_direction_score(guidance), "guidance_status": guidance.status, "oneoff_penalty": oneoff_penalty(risk_flags), "signal_strength_score": signal_strength_score(signals), "document_quality_score": document_quality_score(confidence), "event_type": parser_output.get("event_type", "unknown"), "event_direction": parser_output.get("event_direction", "unknown"), "parse_confidence_overall": confidence.overall, "parse_confidence_event_direction": confidence.event_direction, "parse_confidence_guidance": confidence.guidance, "filing_time_bucket": parser_output.get("filing_time_bucket", "unknown"), "event_date": parser_output.get("event_date", ""), }