feat: move all remaining inline logic from routers to services (Tier 2)
This commit is contained in:
@@ -15,6 +15,7 @@ from typing import Optional
|
||||
from sqlalchemy import case, func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.domain.errors import EntityNotFoundError
|
||||
from app.models.technique import Technique
|
||||
from app.models.test import Test
|
||||
from app.models.detection_rule import DetectionRule
|
||||
@@ -232,6 +233,29 @@ def bulk_technique_scores(db: Session) -> dict:
|
||||
# ── Technique-level scoring (single technique — preserved API) ────────
|
||||
|
||||
|
||||
def score_technique_by_mitre_id(db: Session, mitre_id: str) -> dict:
|
||||
"""Get detailed score with breakdown for a technique by MITRE ID."""
|
||||
technique = db.query(Technique).filter(Technique.mitre_id == mitre_id).first()
|
||||
if not technique:
|
||||
raise EntityNotFoundError("Technique", mitre_id)
|
||||
result = calculate_technique_score(technique, db)
|
||||
return {
|
||||
"mitre_id": technique.mitre_id,
|
||||
"name": technique.name,
|
||||
"tactic": technique.tactic,
|
||||
"status_global": technique.status_global.value if technique.status_global else None,
|
||||
**result,
|
||||
}
|
||||
|
||||
|
||||
def score_actor_by_id(db: Session, actor_id: str) -> dict:
|
||||
"""Get coverage score for a threat actor by ID."""
|
||||
actor = db.query(ThreatActor).filter(ThreatActor.id == actor_id).first()
|
||||
if not actor:
|
||||
raise EntityNotFoundError("ThreatActor", actor_id)
|
||||
return calculate_actor_coverage_score(actor_id, db)
|
||||
|
||||
|
||||
def calculate_technique_score(technique: Technique, db: Session) -> dict:
|
||||
"""Calculate a 0-100 score for a technique with detailed breakdown.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user