refactor(docs+comments): add Google-style docstrings and inline comments across backend

Task D — Google-style docstrings (Args/Returns) on every public function,
method, and class across all 158 Python files in the backend. Zero ruff D
violations (pydocstyle Google convention).

Task E — Explanatory one-line comment before every code line (~11600 new
comments). ruff check passes clean after isort re-sort.
This commit is contained in:
kitos
2026-06-10 12:37:15 +02:00
parent 394d5d9056
commit c99cc4946a
158 changed files with 14861 additions and 248 deletions
+141 -6
View File
@@ -3,20 +3,37 @@
Provides granular scoring with breakdowns and configurable weights.
"""
# Import Optional from typing
from typing import Optional
# Import APIRouter, Depends, Query from fastapi
from fastapi import APIRouter, Depends, Query
# Import BaseModel from pydantic
from pydantic import BaseModel
# Import Session from sqlalchemy.orm
from sqlalchemy.orm import Session
# Import get_db from app.database
from app.database import get_db
# Import get_current_user, require_role from app.dependencies.auth
from app.dependencies.auth import get_current_user, require_role
# Import UnitOfWork from app.domain.unit_of_work
from app.domain.unit_of_work import UnitOfWork
# Import User from app.models.user
from app.models.user import User
# Import from app.services.scoring_config_service
from app.services.scoring_config_service import (
get_weights_dict,
update_scoring_weights,
)
# Import from app.services.scoring_service
from app.services.scoring_service import (
calculate_tactic_score,
get_score_history,
@@ -24,6 +41,7 @@ from app.services.scoring_service import (
score_technique_by_mitre_id,
)
# Assign router = APIRouter(prefix="/scores", tags=["scores"])
router = APIRouter(prefix="/scores", tags=["scores"])
@@ -31,12 +49,26 @@ router = APIRouter(prefix="/scores", tags=["scores"])
@router.get("/technique/{mitre_id}")
# Define function score_technique
def score_technique(
# Entry: mitre_id
mitre_id: str,
# Entry: db
db: Session = Depends(get_db),
# Entry: current_user
current_user: User = Depends(get_current_user),
) -> dict:
"""Get detailed score with breakdown for a specific technique."""
"""Get detailed score with breakdown for a specific technique.
Args:
mitre_id (str): MITRE ATT&CK technique ID (e.g. ``T1059``).
db (Session): SQLAlchemy database session.
current_user (User): Authenticated user making the request.
Returns:
dict: Score value and component breakdown (tests, detection rules, recency, etc.).
"""
# Return score_technique_by_mitre_id(db, mitre_id)
return score_technique_by_mitre_id(db, mitre_id)
@@ -44,12 +76,26 @@ def score_technique(
@router.get("/tactic/{tactic}")
# Define function score_tactic
def score_tactic(
# Entry: tactic
tactic: str,
# Entry: db
db: Session = Depends(get_db),
# Entry: current_user
current_user: User = Depends(get_current_user),
) -> dict:
"""Get average score for a tactic."""
"""Get average score for a tactic.
Args:
tactic (str): MITRE ATT&CK tactic slug (e.g. ``initial-access``).
db (Session): SQLAlchemy database session.
current_user (User): Authenticated user making the request.
Returns:
dict: Average score and per-technique breakdown for the tactic.
"""
# Return calculate_tactic_score(tactic, db)
return calculate_tactic_score(tactic, db)
@@ -57,12 +103,26 @@ def score_tactic(
@router.get("/threat-actor/{actor_id}")
# Define function score_threat_actor
def score_threat_actor(
# Entry: actor_id
actor_id: str,
# Entry: db
db: Session = Depends(get_db),
# Entry: current_user
current_user: User = Depends(get_current_user),
) -> dict:
"""Get coverage score against a specific threat actor."""
"""Get coverage score against a specific threat actor.
Args:
actor_id (str): UUID string of the threat actor to score against.
db (Session): SQLAlchemy database session.
current_user (User): Authenticated user making the request.
Returns:
dict: Coverage score and per-technique breakdown for the threat actor.
"""
# Return score_actor_by_id(db, actor_id)
return score_actor_by_id(db, actor_id)
@@ -70,13 +130,26 @@ def score_threat_actor(
@router.get("/organization")
# Define function score_organization
def score_organization(
# Entry: db
db: Session = Depends(get_db),
# Entry: current_user
current_user: User = Depends(get_current_user),
) -> dict:
"""Get the overall organization security score (cached for 5 min)."""
"""Get the overall organization security score (cached for 5 min).
Args:
db (Session): SQLAlchemy database session.
current_user (User): Authenticated user making the request.
Returns:
dict: Aggregate organization score with tactic-level breakdowns.
"""
# Import get_organization_score_cached from app.services.score_cache
from app.services.score_cache import get_organization_score_cached
# Return get_organization_score_cached(db)
return get_organization_score_cached(db)
@@ -84,12 +157,26 @@ def score_organization(
@router.get("/history")
# Define function score_history
def score_history(
# Entry: period
period: str = Query("90d", pattern="^(30d|90d|1y)$"),
# Entry: db
db: Session = Depends(get_db),
# Entry: current_user
current_user: User = Depends(get_current_user),
) -> dict:
"""Get historical score data points (weekly)."""
"""Get historical score data points (weekly).
Args:
period (str): Time window for history — one of ``30d``, ``90d``, or ``1y``.
db (Session): SQLAlchemy database session.
current_user (User): Authenticated user making the request.
Returns:
dict: Weekly score data points for the requested period.
"""
# Return get_score_history(db, period)
return get_score_history(db, period)
@@ -97,11 +184,23 @@ def score_history(
@router.get("/config")
# Define function get_scoring_config
def get_scoring_config(
# Entry: db
db: Session = Depends(get_db),
# Entry: current_user
current_user: User = Depends(require_role("admin")),
) -> dict:
"""Get current scoring weights (admin only)."""
"""Get current scoring weights (admin only).
Args:
db (Session): SQLAlchemy database session.
current_user (User): Authenticated admin user.
Returns:
dict: Current weight values for each scoring component.
"""
# Return get_weights_dict(db)
return get_weights_dict(db)
@@ -109,41 +208,77 @@ def get_scoring_config(
class ScoringConfigUpdate(BaseModel):
"""Partial update payload for the scoring weight configuration."""
# Assign tests = None
tests: Optional[float] = None
# Assign detection_rules = None
detection_rules: Optional[float] = None
# Assign d3fend = None
d3fend: Optional[float] = None
# Assign recency = None
recency: Optional[float] = None
# Assign severity = None
severity: Optional[float] = None
# Assign freshness = None
freshness: Optional[float] = None
# Assign platform_diversity = None
platform_diversity: Optional[float] = None
# Apply the @router.patch decorator
@router.patch("/config")
# Define function update_scoring_config
def update_scoring_config(
# Entry: payload
payload: ScoringConfigUpdate,
# Entry: db
db: Session = Depends(get_db),
# Entry: current_user
current_user: User = Depends(require_role("admin")),
) -> dict:
"""Update scoring weights (admin only).
Weights are persisted in the database and survive restarts.
Validation enforces that all weights are non-negative and sum to 100.
Args:
payload (ScoringConfigUpdate): Partial weight update; only set fields are changed.
db (Session): SQLAlchemy database session.
current_user (User): Authenticated admin user.
Returns:
dict: Confirmation message plus the full updated weight configuration.
"""
# Open context manager
with UnitOfWork(db) as uow:
# Assign result = update_scoring_weights(
result = update_scoring_weights(
db,
# Keyword argument: tests
tests=payload.tests,
# Keyword argument: detection_rules
detection_rules=payload.detection_rules,
# Keyword argument: d3fend
d3fend=payload.d3fend,
# Keyword argument: recency
recency=payload.recency,
# Keyword argument: severity
severity=payload.severity,
# Keyword argument: freshness
freshness=payload.freshness,
# Keyword argument: platform_diversity
platform_diversity=payload.platform_diversity,
# Keyword argument: updated_by
updated_by=current_user.id,
)
# Call uow.commit()
uow.commit()
# Import invalidate from app.services.score_cache
from app.services.score_cache import invalidate
# Call invalidate()
invalidate()
# Return {"message": "Scoring config updated", **result}
return {"message": "Scoring config updated", **result}