refactor: Migrate insights routes to blueprints

- Create new blueprints/admin/routes_insights.py
- Move 5 insights routes (dashboard + API endpoints)
- Update template to use new /admin/insights-api/* paths
- Add endpoint aliases for backward compatibility

Phase 6.2b - Insights routes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-31 10:27:35 +01:00
parent 54c1878d66
commit 42f04e065f
5 changed files with 173 additions and 20 deletions

30
app.py
View File

@ -4000,9 +4000,9 @@ def test_sanitization():
# DEVELOPMENT INSIGHTS (Roadmap from user feedback)
# ============================================================
@app.route('/admin/insights')
@login_required
def admin_insights():
# @app.route('/admin/insights') # MOVED TO admin.admin_insights
# @login_required
def _old_admin_insights():
"""Admin dashboard for development insights from forum and chat"""
if not current_user.is_admin:
flash('Brak uprawnień do tej strony.', 'error')
@ -4011,9 +4011,9 @@ def admin_insights():
return render_template('admin/insights.html')
@app.route('/api/admin/insights', methods=['GET'])
@login_required
def api_get_insights():
# @app.route('/api/admin/insights', methods=['GET']) # MOVED TO admin.api_get_insights
# @login_required
def _old_api_get_insights():
"""Get development insights for roadmap"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403
@ -4040,9 +4040,9 @@ def api_get_insights():
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/admin/insights/<int:insight_id>/status', methods=['PUT'])
@login_required
def api_update_insight_status(insight_id):
# @app.route('/api/admin/insights/<int:insight_id>/status', methods=['PUT']) # MOVED TO admin.api_update_insight_status
# @login_required
def _old_api_update_insight_status(insight_id):
"""Update insight status (for roadmap planning)"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403
@ -4066,9 +4066,9 @@ def api_update_insight_status(insight_id):
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/admin/insights/sync', methods=['POST'])
@login_required
def api_sync_insights():
# @app.route('/api/admin/insights/sync', methods=['POST']) # MOVED TO admin.api_sync_insights
# @login_required
def _old_api_sync_insights():
"""Manually trigger knowledge sync from forum and chat"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403
@ -4100,9 +4100,9 @@ def api_sync_insights():
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/admin/insights/stats', methods=['GET'])
@login_required
def api_insights_stats():
# @app.route('/api/admin/insights/stats', methods=['GET']) # MOVED TO admin.api_insights_stats
# @login_required
def _old_api_insights_stats():
"""Get knowledge base statistics"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403

View File

@ -253,6 +253,12 @@ def register_blueprints(app):
'admin_announcements_publish': 'admin.admin_announcements_publish',
'admin_announcements_archive': 'admin.admin_announcements_archive',
'admin_announcements_delete': 'admin.admin_announcements_delete',
# Insights (Phase 6.2b)
'admin_insights': 'admin.admin_insights',
'api_get_insights': 'admin.api_get_insights',
'api_update_insight_status': 'admin.api_update_insight_status',
'api_sync_insights': 'admin.api_sync_insights',
'api_insights_stats': 'admin.api_insights_stats',
})
logger.info("Created admin endpoint aliases")
except ImportError as e:

View File

@ -15,3 +15,4 @@ from . import routes_status # noqa: E402, F401
from . import routes_social # noqa: E402, F401
from . import routes_security # noqa: E402, F401
from . import routes_announcements # noqa: E402, F401
from . import routes_insights # noqa: E402, F401

View File

@ -0,0 +1,146 @@
"""
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 . import bp
logger = logging.getLogger(__name__)
# ============================================================
# INSIGHTS DASHBOARD
# ============================================================
@bp.route('/insights')
@login_required
def admin_insights():
"""Admin dashboard for development insights from forum and chat"""
if not current_user.is_admin:
flash('Brak uprawnień do tej strony.', 'error')
return redirect(url_for('dashboard'))
return render_template('admin/insights.html')
@bp.route('/insights-api', methods=['GET'])
@login_required
def api_get_insights():
"""Get development insights for roadmap"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403
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
def api_update_insight_status(insight_id):
"""Update insight status (for roadmap planning)"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403
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
def api_sync_insights():
"""Manually trigger knowledge sync from forum and chat"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403
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
def api_insights_stats():
"""Get knowledge base statistics"""
if not current_user.is_admin:
return jsonify({'success': False, 'error': 'Admin access required'}), 403
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

View File

@ -268,8 +268,8 @@ async function loadInsights() {
try {
const url = currentStatus
? `/api/admin/insights?status=${currentStatus}`
: '/api/admin/insights';
? `/admin/insights-api?status=${currentStatus}`
: '/admin/insights-api';
const response = await fetch(url);
const data = await response.json();
@ -326,7 +326,7 @@ async function loadInsights() {
async function loadStats() {
try {
const response = await fetch('/api/admin/insights/stats');
const response = await fetch('/admin/insights-api/stats');
const data = await response.json();
if (data.success && data.stats) {
@ -342,7 +342,7 @@ async function updateInsightStatus(insightId, status) {
if (!status) return;
try {
const response = await fetch(`/api/admin/insights/${insightId}/status`, {
const response = await fetch(`/admin/insights-api/${insightId}/status`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status })
@ -366,7 +366,7 @@ async function syncInsights() {
btn.innerHTML = '<span class="spinner"></span> Synchronizuję...';
try {
const response = await fetch('/api/admin/insights/sync', {
const response = await fetch('/admin/insights-api/sync', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ days_back: 30 })