fix(quality): Ruff/CVE/TS-Fixes, 104 neue Tests, Complexity-Refactoring
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 30s
CI / test-python-backend-compliance (push) Successful in 30s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 17s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 30s
CI / test-python-backend-compliance (push) Successful in 30s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 17s
- Ruff: 144 auto-fixes (unused imports, == None → is None), F821/F811/F841 manuell - CVEs: python-multipart>=0.0.22, weasyprint>=68.0, pillow>=12.1.1, npm audit fix (0 vulns) - TS: 5 tote Drafting-Engine-Dateien entfernt, allowed-facts/sanitizer/StepHeader/context fixes - Tests: +104 (ISMS 58, Evidence 18, VVT 14, Generation 14) → 1449 passed - Refactoring: collect_ci_evidence (F→A), row_to_response (E→A), extract_requirements (E→A) - Dead Code: pca-platform, 7 Go-Handler, dsr_api.py, duplicate Schemas entfernt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,6 @@ Endpoints:
|
||||
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Header
|
||||
@@ -127,16 +126,68 @@ class AuditListResponse(BaseModel):
|
||||
# SQL column lists — keep in sync with SELECT/INSERT
|
||||
# =============================================================================
|
||||
|
||||
_BASE_COLUMNS = """id, tenant_id, company_name, legal_form, industry, founded_year,
|
||||
business_model, offerings, company_size, employee_count, annual_revenue,
|
||||
headquarters_country, headquarters_city, has_international_locations,
|
||||
international_countries, target_markets, primary_jurisdiction,
|
||||
is_data_controller, is_data_processor, uses_ai, ai_use_cases,
|
||||
dpo_name, dpo_email, legal_contact_name, legal_contact_email,
|
||||
machine_builder, is_complete, completed_at, created_at, updated_at,
|
||||
repos, document_sources, processing_systems, ai_systems, technical_contacts,
|
||||
subject_to_nis2, subject_to_ai_act, subject_to_iso27001,
|
||||
supervisory_authority, review_cycle_months"""
|
||||
_BASE_COLUMNS_LIST = [
|
||||
"id", "tenant_id", "company_name", "legal_form", "industry", "founded_year",
|
||||
"business_model", "offerings", "company_size", "employee_count", "annual_revenue",
|
||||
"headquarters_country", "headquarters_city", "has_international_locations",
|
||||
"international_countries", "target_markets", "primary_jurisdiction",
|
||||
"is_data_controller", "is_data_processor", "uses_ai", "ai_use_cases",
|
||||
"dpo_name", "dpo_email", "legal_contact_name", "legal_contact_email",
|
||||
"machine_builder", "is_complete", "completed_at", "created_at", "updated_at",
|
||||
"repos", "document_sources", "processing_systems", "ai_systems", "technical_contacts",
|
||||
"subject_to_nis2", "subject_to_ai_act", "subject_to_iso27001",
|
||||
"supervisory_authority", "review_cycle_months",
|
||||
]
|
||||
|
||||
_BASE_COLUMNS = ", ".join(_BASE_COLUMNS_LIST)
|
||||
|
||||
# Per-field defaults and type coercions for row_to_response.
|
||||
# Each entry is (field_name, default_value, expected_type_or_None).
|
||||
# - expected_type: if set, the value is checked with isinstance; if it fails,
|
||||
# default_value is used instead.
|
||||
# - Special sentinels: "STR" means str(value), "STR_OR_NONE" means str(v) if v else None.
|
||||
_FIELD_DEFAULTS = {
|
||||
"id": (None, "STR"),
|
||||
"tenant_id": (None, None),
|
||||
"company_name": ("", None),
|
||||
"legal_form": ("GmbH", None),
|
||||
"industry": ("", None),
|
||||
"founded_year": (None, None),
|
||||
"business_model": ("B2B", None),
|
||||
"offerings": ([], list),
|
||||
"company_size": ("small", None),
|
||||
"employee_count": ("1-9", None),
|
||||
"annual_revenue": ("< 2 Mio", None),
|
||||
"headquarters_country": ("DE", None),
|
||||
"headquarters_city": ("", None),
|
||||
"has_international_locations": (False, None),
|
||||
"international_countries": ([], list),
|
||||
"target_markets": (["DE"], list),
|
||||
"primary_jurisdiction": ("DE", None),
|
||||
"is_data_controller": (True, None),
|
||||
"is_data_processor": (False, None),
|
||||
"uses_ai": (False, None),
|
||||
"ai_use_cases": ([], list),
|
||||
"dpo_name": (None, None),
|
||||
"dpo_email": (None, None),
|
||||
"legal_contact_name": (None, None),
|
||||
"legal_contact_email": (None, None),
|
||||
"machine_builder": (None, dict),
|
||||
"is_complete": (False, None),
|
||||
"completed_at": (None, "STR_OR_NONE"),
|
||||
"created_at": (None, "STR"),
|
||||
"updated_at": (None, "STR"),
|
||||
"repos": ([], list),
|
||||
"document_sources": ([], list),
|
||||
"processing_systems": ([], list),
|
||||
"ai_systems": ([], list),
|
||||
"technical_contacts": ([], list),
|
||||
"subject_to_nis2": (False, None),
|
||||
"subject_to_ai_act": (False, None),
|
||||
"subject_to_iso27001": (False, None),
|
||||
"supervisory_authority": (None, None),
|
||||
"review_cycle_months": (12, None),
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -144,50 +195,29 @@ _BASE_COLUMNS = """id, tenant_id, company_name, legal_form, industry, founded_ye
|
||||
# =============================================================================
|
||||
|
||||
def row_to_response(row) -> CompanyProfileResponse:
|
||||
"""Convert a DB row to response model."""
|
||||
return CompanyProfileResponse(
|
||||
id=str(row[0]),
|
||||
tenant_id=row[1],
|
||||
company_name=row[2] or "",
|
||||
legal_form=row[3] or "GmbH",
|
||||
industry=row[4] or "",
|
||||
founded_year=row[5],
|
||||
business_model=row[6] or "B2B",
|
||||
offerings=row[7] if isinstance(row[7], list) else [],
|
||||
company_size=row[8] or "small",
|
||||
employee_count=row[9] or "1-9",
|
||||
annual_revenue=row[10] or "< 2 Mio",
|
||||
headquarters_country=row[11] or "DE",
|
||||
headquarters_city=row[12] or "",
|
||||
has_international_locations=row[13] or False,
|
||||
international_countries=row[14] if isinstance(row[14], list) else [],
|
||||
target_markets=row[15] if isinstance(row[15], list) else ["DE"],
|
||||
primary_jurisdiction=row[16] or "DE",
|
||||
is_data_controller=row[17] if row[17] is not None else True,
|
||||
is_data_processor=row[18] or False,
|
||||
uses_ai=row[19] or False,
|
||||
ai_use_cases=row[20] if isinstance(row[20], list) else [],
|
||||
dpo_name=row[21],
|
||||
dpo_email=row[22],
|
||||
legal_contact_name=row[23],
|
||||
legal_contact_email=row[24],
|
||||
machine_builder=row[25] if isinstance(row[25], dict) else None,
|
||||
is_complete=row[26] or False,
|
||||
completed_at=str(row[27]) if row[27] else None,
|
||||
created_at=str(row[28]),
|
||||
updated_at=str(row[29]),
|
||||
# Phase 2 fields (indices 30-39)
|
||||
repos=row[30] if isinstance(row[30], list) else [],
|
||||
document_sources=row[31] if isinstance(row[31], list) else [],
|
||||
processing_systems=row[32] if isinstance(row[32], list) else [],
|
||||
ai_systems=row[33] if isinstance(row[33], list) else [],
|
||||
technical_contacts=row[34] if isinstance(row[34], list) else [],
|
||||
subject_to_nis2=row[35] or False,
|
||||
subject_to_ai_act=row[36] or False,
|
||||
subject_to_iso27001=row[37] or False,
|
||||
supervisory_authority=row[38],
|
||||
review_cycle_months=row[39] or 12,
|
||||
)
|
||||
"""Convert a DB row to response model using zip-based column mapping."""
|
||||
raw = dict(zip(_BASE_COLUMNS_LIST, row))
|
||||
coerced: dict = {}
|
||||
|
||||
for col in _BASE_COLUMNS_LIST:
|
||||
default, expected_type = _FIELD_DEFAULTS[col]
|
||||
value = raw[col]
|
||||
|
||||
if expected_type == "STR":
|
||||
coerced[col] = str(value)
|
||||
elif expected_type == "STR_OR_NONE":
|
||||
coerced[col] = str(value) if value else None
|
||||
elif expected_type is not None:
|
||||
# Type-checked field (list / dict): use value only if it matches
|
||||
coerced[col] = value if isinstance(value, expected_type) else default
|
||||
else:
|
||||
# is_data_controller needs special None-check (True when NULL)
|
||||
if col == "is_data_controller":
|
||||
coerced[col] = value if value is not None else default
|
||||
else:
|
||||
coerced[col] = value or default if default is not None else value
|
||||
|
||||
return CompanyProfileResponse(**coerced)
|
||||
|
||||
|
||||
def log_audit(db, tenant_id: str, action: str, changed_fields: Optional[dict], changed_by: Optional[str]):
|
||||
|
||||
Reference in New Issue
Block a user