feat(agent): migrate compliance-check results to banner + documents (M1-M5)
After a compliance-check run finishes, the user can now apply the
extracted vendor inventory directly to their own:
- CookieBanner config (admin /sdk/einwilligungen)
- Cookie-Policy / VVT-Register / Privacy-Policy templates
(admin /sdk/document-generator)
Backend:
- migration_to_banner.py: vendor list -> CookieBannerConfig with
ESSENTIAL/PERFORMANCE/PERSONALIZATION/EXTERNAL_MEDIA buckets +
review flags (broken opt-out URLs, missing expiry, no cookies listed)
- migration_to_document.py: vendor list -> pre-fills for 3 doc
templates, recipient-type aware (INTERNAL/GROUP/PROCESSOR/CONTROLLER)
- agent_migration_routes.py: GET /banner-preview, /document-preview,
/summary keyed on check_id
- compliance_audit_log: new check_payloads table persists cmp_vendors +
extracted_profile so the preview survives an app restart
- tests: 9 mapper units + 4 endpoint integration tests
Frontend:
- MigrationPanel.tsx: modal showing banner-config diff + document
pre-fills, plus links into the existing editors
- ComplianceCheckTab.tsx: replaces standalone audit link with the
panel; net -3 lines, stays at the 500-cap
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -58,9 +58,63 @@ def _ensure_db() -> None:
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_mc_check ON mc_results(check_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_mc_reg ON mc_results(regulation, passed);
|
||||
|
||||
-- Migration-source payloads (cmp_vendors + extracted_profile),
|
||||
-- kept as JSON blobs so the /migration/* endpoints can rebuild
|
||||
-- a banner config or document pre-fill after the in-memory
|
||||
-- _compliance_check_jobs entry is gone.
|
||||
CREATE TABLE IF NOT EXISTS check_payloads (
|
||||
check_id TEXT PRIMARY KEY,
|
||||
vendors TEXT, -- JSON list[dict]
|
||||
profile TEXT -- JSON dict
|
||||
);
|
||||
""")
|
||||
|
||||
|
||||
def record_check_payload(
|
||||
check_id: str,
|
||||
vendors: list[dict] | None,
|
||||
profile: dict | None,
|
||||
) -> None:
|
||||
"""Persist cmp_vendors + extracted_profile for later migration use."""
|
||||
try:
|
||||
_ensure_db()
|
||||
with sqlite3.connect(DB_PATH) as conn:
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO check_payloads "
|
||||
"(check_id, vendors, profile) VALUES (?, ?, ?)",
|
||||
(
|
||||
check_id,
|
||||
json.dumps(vendors or [], ensure_ascii=False),
|
||||
json.dumps(profile or {}, ensure_ascii=False),
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
except Exception as e:
|
||||
logger.warning("record_check_payload failed for %s: %s", check_id, e)
|
||||
|
||||
|
||||
def get_check_payload(check_id: str) -> dict | None:
|
||||
"""Load cmp_vendors + extracted_profile for a previous check."""
|
||||
try:
|
||||
_ensure_db()
|
||||
with sqlite3.connect(DB_PATH) as conn:
|
||||
conn.row_factory = sqlite3.Row
|
||||
row = conn.execute(
|
||||
"SELECT vendors, profile FROM check_payloads WHERE check_id=?",
|
||||
(check_id,),
|
||||
).fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return {
|
||||
"vendors": json.loads(row["vendors"] or "[]"),
|
||||
"profile": json.loads(row["profile"] or "{}"),
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning("get_check_payload failed: %s", e)
|
||||
return None
|
||||
|
||||
|
||||
def record_check_run(
|
||||
check_id: str,
|
||||
tenant_id: str,
|
||||
|
||||
Reference in New Issue
Block a user