nordabiz/blueprints/admin/routes_insights.py
Maciej Pienczyn 4181a2e760 refactor: Migrate access control from is_admin to role-based system
Replace ~170 manual `if not current_user.is_admin` checks with:
- @role_required(SystemRole.ADMIN) for user management, security, ZOPK
- @role_required(SystemRole.OFFICE_MANAGER) for content management
- current_user.can_access_admin_panel() for admin UI access
- current_user.can_moderate_forum() for forum moderation
- current_user.can_edit_company(id) for company permissions

Add @office_manager_required decorator shortcut.
Add SQL migration to sync existing users' role field.

Role hierarchy: UNAFFILIATED(10) < MEMBER(20) < EMPLOYEE(30) < MANAGER(40) < OFFICE_MANAGER(50) < ADMIN(100)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 21:05:22 +01:00

232 lines
7.2 KiB
Python

"""
Admin Insights Routes
======================
Development insights from forum and chat for roadmap planning.
"""
import logging
from flask import render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_required, current_user
from database import SystemRole
from utils.decorators import role_required
from . import bp
logger = logging.getLogger(__name__)
# ============================================================
# INSIGHTS DASHBOARD
# ============================================================
@bp.route('/insights')
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def admin_insights():
"""Admin dashboard for development insights from forum and chat"""
return render_template('admin/insights.html')
@bp.route('/insights-api', methods=['GET'])
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_get_insights():
"""Get development insights for roadmap"""
try:
from norda_knowledge_service import get_knowledge_service
service = get_knowledge_service()
status = request.args.get('status')
insights = service.get_development_insights(status=status)
return jsonify({
'success': True,
'insights': insights,
'count': len(insights)
})
except ImportError:
return jsonify({
'success': False,
'error': 'Knowledge service not available'
}), 500
except Exception as e:
logger.error(f"Error getting insights: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
@bp.route('/insights-api/<int:insight_id>/status', methods=['PUT'])
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_update_insight_status(insight_id):
"""Update insight status (for roadmap planning)"""
try:
from norda_knowledge_service import get_knowledge_service
service = get_knowledge_service()
data = request.get_json()
status = data.get('status')
note = data.get('note')
if not status:
return jsonify({'success': False, 'error': 'Status is required'}), 400
success = service.update_insight_status(insight_id, status, note)
return jsonify({'success': success})
except Exception as e:
logger.error(f"Error updating insight status: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
@bp.route('/insights-api/sync', methods=['POST'])
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_sync_insights():
"""Manually trigger knowledge sync from forum and chat"""
try:
from norda_knowledge_service import get_knowledge_service
service = get_knowledge_service()
data = request.get_json() or {}
days_back = data.get('days_back', 30)
results = {
'forum': service.sync_forum_knowledge(days_back),
'chat': service.sync_chat_knowledge(days_back),
'questions': service.analyze_user_questions(days_back)
}
return jsonify({
'success': True,
'results': results
})
except ImportError:
return jsonify({
'success': False,
'error': 'Knowledge service not available'
}), 500
except Exception as e:
logger.error(f"Error syncing insights: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
@bp.route('/insights-api/stats', methods=['GET'])
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_insights_stats():
"""Get knowledge base statistics"""
try:
from norda_knowledge_service import get_knowledge_service
service = get_knowledge_service()
stats = service.get_knowledge_stats()
return jsonify({
'success': True,
'stats': stats
})
except ImportError:
return jsonify({
'success': False,
'error': 'Knowledge service not available'
}), 500
except Exception as e:
logger.error(f"Error getting stats: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
# ============================================================
# AI LEARNING STATUS API
# ============================================================
@bp.route('/ai-learning-status')
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_ai_learning_status():
"""API: Get AI feedback learning status and examples"""
try:
from feedback_learning_service import get_feedback_learning_service
service = get_feedback_learning_service()
context = service.get_learning_context()
# Format examples for JSON response
positive_examples = []
for ex in context.get('positive_examples', []):
positive_examples.append({
'query': ex.query,
'response': ex.response[:300] + '...' if len(ex.response) > 300 else ex.response,
'companies': ex.companies_mentioned or []
})
negative_examples = []
for ex in context.get('negative_examples', []):
negative_examples.append({
'query': ex.query,
'response': ex.response,
'comment': ex.feedback_comment
})
return jsonify({
'success': True,
'learning_active': True,
'stats': context.get('stats', {}),
'using_seed_examples': context.get('stats', {}).get('using_seed_examples', False),
'positive_examples_count': len(positive_examples),
'negative_examples_count': len(negative_examples),
'positive_examples': positive_examples,
'negative_examples': negative_examples,
'negative_patterns': context.get('negative_patterns', []),
'generated_at': context.get('generated_at')
})
except ImportError:
return jsonify({
'success': True,
'learning_active': False,
'message': 'Feedback learning service not available'
})
except Exception as e:
logger.error(f"Error getting AI learning status: {e}")
return jsonify({
'success': False,
'error': str(e)
}), 500
# ============================================================
# CHAT STATS API
# ============================================================
@bp.route('/chat-stats')
@login_required
@role_required(SystemRole.OFFICE_MANAGER)
def api_chat_stats():
"""API: Get chat statistics for dashboard"""
from datetime import datetime, timedelta
from database import SessionLocal, AIChatMessage
db = SessionLocal()
try:
from sqlalchemy import func
# Stats for last 7 days
week_ago = datetime.now() - timedelta(days=7)
daily_stats = db.query(
func.date(AIChatMessage.created_at).label('date'),
func.count(AIChatMessage.id).label('count')
).filter(
AIChatMessage.created_at >= week_ago,
AIChatMessage.role == 'user'
).group_by(
func.date(AIChatMessage.created_at)
).order_by('date').all()
return jsonify({
'success': True,
'daily_queries': [{'date': str(d.date), 'count': d.count} for d in daily_stats]
})
finally:
db.close()