From 7197af393397b7a4c84fda9b657d8863972cfcf5 Mon Sep 17 00:00:00 2001 From: Maciej Pienczyn Date: Sat, 7 Feb 2026 18:05:17 +0100 Subject: [PATCH] feat(audit): Add previous vs current AI analysis comparison Store previous analysis before regeneration and show comparison table with priority breakdown, new/removed actions diff. Co-Authored-By: Claude Opus 4.6 --- audit_ai_service.py | 22 ++- database.py | 3 + .../migrations/057_audit_cache_previous.sql | 6 + templates/gbp_audit.html | 3 + templates/partials/audit_ai_actions.html | 154 ++++++++++++++++++ templates/seo_audit.html | 3 + templates/social_audit.html | 3 + 7 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 database/migrations/057_audit_cache_previous.sql diff --git a/audit_ai_service.py b/audit_ai_service.py index dbc51df..80ce868 100644 --- a/audit_ai_service.py +++ b/audit_ai_service.py @@ -600,12 +600,19 @@ def generate_analysis(company_id: int, audit_type: str, user_id: int = None, for if cache and cache.audit_data_hash == data_hash and cache.expires_at and cache.expires_at > datetime.now(): logger.info(f"AI analysis cache hit for company {company_id} audit_type={audit_type}") - return { + result = { 'summary': cache.analysis_summary, 'actions': cache.actions_json or [], 'cached': True, 'generated_at': cache.generated_at.isoformat() if cache.generated_at else None, } + if cache.previous_summary or cache.previous_actions_json: + result['previous'] = { + 'summary': cache.previous_summary, + 'actions': cache.previous_actions_json or [], + 'generated_at': cache.previous_generated_at.isoformat() if cache.previous_generated_at else None, + } + return result # Build prompt prompt_builders = { @@ -656,6 +663,10 @@ def generate_analysis(company_id: int, audit_type: str, user_id: int = None, for ).first() if cache: + # Preserve previous analysis for comparison + cache.previous_summary = cache.analysis_summary + cache.previous_actions_json = cache.actions_json + cache.previous_generated_at = cache.generated_at cache.analysis_summary = summary cache.actions_json = actions cache.audit_data_hash = data_hash @@ -693,12 +704,19 @@ def generate_analysis(company_id: int, audit_type: str, user_id: int = None, for db.commit() - return { + result = { 'summary': summary, 'actions': actions, 'cached': False, 'generated_at': datetime.now().isoformat(), } + if cache and (cache.previous_summary or cache.previous_actions_json): + result['previous'] = { + 'summary': cache.previous_summary, + 'actions': cache.previous_actions_json or [], + 'generated_at': cache.previous_generated_at.isoformat() if cache.previous_generated_at else None, + } + return result except Exception as e: db.rollback() diff --git a/database.py b/database.py index 2f69d4d..9933c39 100644 --- a/database.py +++ b/database.py @@ -5135,6 +5135,9 @@ class AuditAICache(Base): audit_data_hash = Column(String(64)) generated_at = Column(DateTime, default=datetime.now) expires_at = Column(DateTime) + previous_summary = Column(Text) + previous_actions_json = Column(JSONB) + previous_generated_at = Column(DateTime) # Relationships company = relationship('Company', backref='audit_ai_caches') diff --git a/database/migrations/057_audit_cache_previous.sql b/database/migrations/057_audit_cache_previous.sql new file mode 100644 index 0000000..4d63419 --- /dev/null +++ b/database/migrations/057_audit_cache_previous.sql @@ -0,0 +1,6 @@ +-- Migration 057: Add previous analysis columns to audit_ai_cache +-- Stores the previous AI analysis before regeneration for comparison + +ALTER TABLE audit_ai_cache ADD COLUMN IF NOT EXISTS previous_summary TEXT; +ALTER TABLE audit_ai_cache ADD COLUMN IF NOT EXISTS previous_actions_json JSONB; +ALTER TABLE audit_ai_cache ADD COLUMN IF NOT EXISTS previous_generated_at TIMESTAMP; diff --git a/templates/gbp_audit.html b/templates/gbp_audit.html index a6a8c6a..89ac22d 100644 --- a/templates/gbp_audit.html +++ b/templates/gbp_audit.html @@ -2049,6 +2049,9 @@ function renderAIResults(data) { actionsList.appendChild(card); }); + // Render comparison with previous analysis if available + if (typeof renderAIComparison === 'function') renderAIComparison(data); + results.style.display = 'block'; document.getElementById('aiActionsSection').scrollIntoView({behavior: 'smooth', block: 'start'}); window._aiActions = actions; diff --git a/templates/partials/audit_ai_actions.html b/templates/partials/audit_ai_actions.html index 61c3a56..7d0dddc 100644 --- a/templates/partials/audit_ai_actions.html +++ b/templates/partials/audit_ai_actions.html @@ -55,6 +55,30 @@ + + +
Priorytetowe akcje @@ -160,6 +184,48 @@ .ai-action-card.dismissed { display: none; } + + .ai-comparison-table { + width: 100%; + border-collapse: collapse; + font-size: var(--font-size-sm); + } + .ai-comparison-table th { + background: var(--bg-tertiary); + padding: 8px 12px; + text-align: left; + font-weight: 600; + color: var(--text-secondary); + border-bottom: 2px solid var(--border); + } + .ai-comparison-table td { + padding: 8px 12px; + border-bottom: 1px solid var(--border); + color: var(--text-primary); + vertical-align: top; + } + .ai-comparison-table tr:last-child td { + border-bottom: none; + } + .ai-diff-added { + display: inline-block; + background: #dcfce7; + color: #166534; + padding: 2px 8px; + border-radius: var(--radius-sm); + font-size: var(--font-size-xs); + margin: 2px; + } + .ai-diff-removed { + display: inline-block; + background: #fee2e2; + color: #991b1b; + padding: 2px 8px; + border-radius: var(--radius-sm); + font-size: var(--font-size-xs); + margin: 2px; + text-decoration: line-through; + } diff --git a/templates/seo_audit.html b/templates/seo_audit.html index 4cfcdf6..f1d3c27 100644 --- a/templates/seo_audit.html +++ b/templates/seo_audit.html @@ -1099,6 +1099,9 @@ function renderAIResults(data) { actionsList.appendChild(card); }); + // Render comparison with previous analysis if available + if (typeof renderAIComparison === 'function') renderAIComparison(data); + results.style.display = 'block'; document.getElementById('aiActionsSection').scrollIntoView({behavior: 'smooth', block: 'start'}); diff --git a/templates/social_audit.html b/templates/social_audit.html index 59db869..4e2b3c7 100644 --- a/templates/social_audit.html +++ b/templates/social_audit.html @@ -1499,6 +1499,9 @@ function renderAIResults(data) { actionsList.appendChild(card); }); + // Render comparison with previous analysis if available + if (typeof renderAIComparison === 'function') renderAIComparison(data); + results.style.display = 'block'; document.getElementById('aiActionsSection').scrollIntoView({behavior: 'smooth', block: 'start'}); window._aiActions = actions;