nordabiz/blueprints/api/routes_audit_actions.py
Maciej Pienczyn 7383ec74a5
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
feat(audit): Add AI-powered audit analysis and action generation
Add Gemini AI integration to SEO, GBP, and Social Media audits that
generates contextual analysis summaries and prioritized action items
with ready-to-use content (Schema.org, meta descriptions, social posts,
GBP descriptions, review responses, content calendars).

New files:
- audit_ai_service.py: Central AI service with caching (7-day TTL)
- blueprints/api/routes_audit_actions.py: 4 API endpoints
- database/migrations/056_audit_actions.sql: 3 new tables
- templates/partials/audit_ai_actions.html: Reusable UI component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 12:41:26 +01:00

202 lines
5.7 KiB
Python

"""
Audit AI Actions API Routes - API blueprint
Endpoints for AI-powered audit analysis and content generation.
Uses audit_ai_service.py for Gemini integration.
"""
import logging
from flask import jsonify, request
from flask_login import current_user, login_required
from database import SessionLocal, Company
from . import bp
logger = logging.getLogger(__name__)
@bp.route('/audit/analyze', methods=['POST'])
@login_required
def api_audit_analyze():
"""
Generate AI analysis for an audit.
Request JSON:
company_id: int (required)
audit_type: str - 'seo', 'gbp', or 'social' (required)
force: bool - Force regeneration even if cache valid (optional)
Returns:
JSON with summary and actions list
"""
import audit_ai_service
data = request.get_json()
if not data:
return jsonify({'success': False, 'error': 'Brak danych w żądaniu'}), 400
company_id = data.get('company_id')
audit_type = data.get('audit_type')
force = data.get('force', False)
if not company_id or not audit_type:
return jsonify({'success': False, 'error': 'Wymagane: company_id i audit_type'}), 400
if audit_type not in ('seo', 'gbp', 'social'):
return jsonify({'success': False, 'error': 'audit_type musi być: seo, gbp lub social'}), 400
# Access control
if not current_user.can_edit_company(company_id):
return jsonify({'success': False, 'error': 'Brak uprawnień do analizy tej firmy'}), 403
result = audit_ai_service.generate_analysis(
company_id=company_id,
audit_type=audit_type,
user_id=current_user.id,
force=force,
)
if 'error' in result:
return jsonify({'success': False, 'error': result['error']}), 422
return jsonify({
'success': True,
'summary': result.get('summary', ''),
'actions': result.get('actions', []),
'cached': result.get('cached', False),
'generated_at': result.get('generated_at'),
})
@bp.route('/audit/generate-content', methods=['POST'])
@login_required
def api_audit_generate_content():
"""
Generate specific content for an audit action.
Request JSON:
company_id: int (required)
action_type: str (required) - e.g. 'generate_schema_org', 'generate_gbp_post'
context: dict (optional) - Extra context like platform, review_text
Returns:
JSON with generated content
"""
import audit_ai_service
data = request.get_json()
if not data:
return jsonify({'success': False, 'error': 'Brak danych w żądaniu'}), 400
company_id = data.get('company_id')
action_type = data.get('action_type')
context = data.get('context', {})
if not company_id or not action_type:
return jsonify({'success': False, 'error': 'Wymagane: company_id i action_type'}), 400
# Access control
if not current_user.can_edit_company(company_id):
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
result = audit_ai_service.generate_content(
company_id=company_id,
action_type=action_type,
context=context,
user_id=current_user.id,
)
if 'error' in result:
return jsonify({'success': False, 'error': result['error']}), 422
return jsonify({
'success': True,
'content': result.get('content', ''),
'action_type': result.get('action_type'),
'model': result.get('model'),
})
@bp.route('/audit/actions/<slug>')
@login_required
def api_audit_actions_by_slug(slug):
"""
Get audit actions for a company.
Query params:
audit_type: str (optional) - Filter by 'seo', 'gbp', or 'social'
Returns:
JSON with list of actions
"""
import audit_ai_service
audit_type = request.args.get('audit_type')
db = SessionLocal()
try:
company = db.query(Company).filter_by(slug=slug, status='active').first()
if not company:
return jsonify({'success': False, 'error': 'Firma nie znaleziona'}), 404
if not current_user.can_edit_company(company.id):
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
actions = audit_ai_service.get_actions_for_company(
company_id=company.id,
audit_type=audit_type,
)
return jsonify({
'success': True,
'company_id': company.id,
'actions': actions,
'count': len(actions),
})
finally:
db.close()
@bp.route('/audit/actions/<int:action_id>/status', methods=['POST'])
@login_required
def api_audit_action_update_status(action_id):
"""
Update the status of an audit action.
Request JSON:
status: str - 'implemented' or 'dismissed'
Returns:
JSON with updated status
"""
import audit_ai_service
from database import AuditAction
data = request.get_json()
if not data:
return jsonify({'success': False, 'error': 'Brak danych'}), 400
new_status = data.get('status')
if not new_status:
return jsonify({'success': False, 'error': 'Wymagane: status'}), 400
# Verify access
db = SessionLocal()
try:
action = db.query(AuditAction).filter_by(id=action_id).first()
if not action:
return jsonify({'success': False, 'error': 'Akcja nie znaleziona'}), 404
if not current_user.can_edit_company(action.company_id):
return jsonify({'success': False, 'error': 'Brak uprawnień'}), 403
finally:
db.close()
result = audit_ai_service.update_action_status(action_id, new_status)
if 'error' in result:
return jsonify({'success': False, 'error': result['error']}), 422
return jsonify({'success': True, **result})