"""Scoring configuration persistence service.""" # Enable future language features for compatibility from __future__ import annotations # Import uuid import uuid # Import Any from typing from typing import Any # Import Session from sqlalchemy.orm from sqlalchemy.orm import Session # Import settings from app.config from app.config import settings # Import ScoringWeights from app.domain.value_objects.scoring_weights from app.domain.value_objects.scoring_weights import ScoringWeights # Import ScoringConfig from app.models.scoring_config from app.models.scoring_config import ScoringConfig # Define function _row_recency def _row_recency(row: ScoringConfig) -> float: # Return float(getattr(row, "weight_recency", None) or getattr(row, "weight_... return float(getattr(row, "weight_recency", None) or getattr(row, "weight_freshness", 10.0)) # Define function _row_severity def _row_severity(row: ScoringConfig) -> float: # Return float( return float( getattr(row, "weight_severity", None) or getattr(row, "weight_platform_diversity", 10.0) ) # Define function get_scoring_weights def get_scoring_weights(db: Session) -> ScoringWeights: """Return the active scoring weights from the database or env defaults.""" # Assign row = db.query(ScoringConfig).first() row = db.query(ScoringConfig).first() # Check: row is not None if row is not None: # Return ScoringWeights( return ScoringWeights( # Keyword argument: tests tests=row.weight_tests, # Keyword argument: detection_rules detection_rules=row.weight_detection_rules, # Keyword argument: d3fend d3fend=row.weight_d3fend, # Keyword argument: recency recency=_row_recency(row), # Keyword argument: severity severity=_row_severity(row), ) # Return ScoringWeights( return ScoringWeights( # Keyword argument: tests tests=float(settings.SCORING_WEIGHT_TESTS), # Keyword argument: detection_rules detection_rules=float(settings.SCORING_WEIGHT_DETECTION_RULES), # Keyword argument: d3fend d3fend=float(settings.SCORING_WEIGHT_D3FEND), # Keyword argument: recency recency=float( getattr(settings, "SCORING_WEIGHT_RECENCY", settings.SCORING_WEIGHT_FRESHNESS) ), # Keyword argument: severity severity=float( getattr(settings, "SCORING_WEIGHT_SEVERITY", settings.SCORING_WEIGHT_PLATFORM_DIVERSITY) ), ) # Define function update_scoring_weights def update_scoring_weights( # Entry: db db: Session, *, # Entry: tests tests: float | None = None, # Entry: detection_rules detection_rules: float | None = None, # Entry: d3fend d3fend: float | None = None, # Entry: recency recency: float | None = None, # Entry: severity severity: float | None = None, # Entry: freshness freshness: float | None = None, # Entry: platform_diversity platform_diversity: float | None = None, # Entry: updated_by updated_by: uuid.UUID | None = None, ) -> dict[str, Any]: """Upsert scoring weights. Does not commit.""" # Check: freshness is not None and recency is None if freshness is not None and recency is None: # Assign recency = freshness recency = freshness # Check: platform_diversity is not None and severity is None if platform_diversity is not None and severity is None: # Assign severity = platform_diversity severity = platform_diversity # Assign current = get_scoring_weights(db) current = get_scoring_weights(db) # Assign new = ScoringWeights( new = ScoringWeights( # Keyword argument: tests tests=tests if tests is not None else current.tests, # Keyword argument: detection_rules detection_rules=detection_rules if detection_rules is not None else current.detection_rules, # Keyword argument: d3fend d3fend=d3fend if d3fend is not None else current.d3fend, # Keyword argument: recency recency=recency if recency is not None else current.recency, # Keyword argument: severity severity=severity if severity is not None else current.severity, ) # Assign row = db.query(ScoringConfig).first() row = db.query(ScoringConfig).first() # Check: row is None if row is None: # Assign row = ScoringConfig() row = ScoringConfig() # Stage new record(s) for database insertion db.add(row) # Assign row.weight_tests = new.tests row.weight_tests = new.tests # Assign row.weight_detection_rules = new.detection_rules row.weight_detection_rules = new.detection_rules # Assign row.weight_d3fend = new.d3fend row.weight_d3fend = new.d3fend # Check: hasattr(row, "weight_recency") if hasattr(row, "weight_recency"): # Assign row.weight_recency = new.recency row.weight_recency = new.recency # Alternative: hasattr(row, "weight_freshness") elif hasattr(row, "weight_freshness"): # Assign row.weight_freshness = new.recency row.weight_freshness = new.recency # Check: hasattr(row, "weight_severity") if hasattr(row, "weight_severity"): # Assign row.weight_severity = new.severity row.weight_severity = new.severity # Alternative: hasattr(row, "weight_platform_diversity") elif hasattr(row, "weight_platform_diversity"): # Assign row.weight_platform_diversity = new.severity row.weight_platform_diversity = new.severity # Check: updated_by is not None and hasattr(row, "updated_by") if updated_by is not None and hasattr(row, "updated_by"): # Assign row.updated_by = updated_by row.updated_by = updated_by # Return _weights_dict(new) return _weights_dict(new) # Define function get_weights_dict def get_weights_dict(db: Session) -> dict[str, Any]: """Return current weights as a serialisable dict.""" # Return _weights_dict(get_scoring_weights(db)) return _weights_dict(get_scoring_weights(db)) # Define function _weights_dict def _weights_dict(w: ScoringWeights) -> dict[str, Any]: # Assign weights = { weights = { # Literal argument value "tests": w.tests, # Literal argument value "detection_rules": w.detection_rules, # Literal argument value "d3fend": w.d3fend, # Literal argument value "recency": w.recency, # Literal argument value "severity": w.severity, # Legacy keys for older clients "freshness": w.recency, # Literal argument value "platform_diversity": w.severity, } # Return { return { # Literal argument value "weights": weights, # Literal argument value "total": sum( [w.tests, w.detection_rules, w.d3fend, w.recency, w.severity] ), }