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
- Add @role_required to 2 missing routes (krs_api PDF download, zopk milestones) - Add role-based menu visibility in admin bar (hide Users, Security, Benefits, Model Comparison, Debug from OFFICE_MANAGER users) - Inject SystemRole into Jinja2 context processor for template role checks - Replace is_admin checkbox with role select dropdown in user creation form - Migrate routes.py and routes_users_api.py from is_admin to SystemRole-based role assignment via set_role() - Add deprecation notice to is_admin database column - Add 23 RBAC unit tests (hierarchy, has_role, set_role, permissions) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
209 lines
6.7 KiB
Python
209 lines
6.7 KiB
Python
"""
|
|
ZOPK Timeline Routes - Admin blueprint
|
|
|
|
Migrated from app.py as part of the blueprint refactoring.
|
|
Contains routes for ZOPK timeline and milestones management.
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
|
|
from flask import flash, jsonify, redirect, render_template, request, url_for
|
|
from flask_login import current_user, login_required
|
|
|
|
from database import (
|
|
SessionLocal,
|
|
SystemRole,
|
|
ZOPKMilestone
|
|
)
|
|
from utils.decorators import role_required
|
|
from . import bp
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@bp.route('/zopk/timeline')
|
|
@login_required
|
|
@role_required(SystemRole.ADMIN)
|
|
def admin_zopk_timeline():
|
|
"""Panel Timeline ZOPK."""
|
|
return render_template('admin/zopk_timeline.html')
|
|
|
|
|
|
@bp.route('/zopk-api/milestones')
|
|
@login_required
|
|
@role_required(SystemRole.OFFICE_MANAGER)
|
|
def api_zopk_milestones():
|
|
"""API - lista kamieni milowych ZOPK."""
|
|
db = SessionLocal()
|
|
try:
|
|
milestones = db.query(ZOPKMilestone).order_by(ZOPKMilestone.target_date).all()
|
|
return jsonify({
|
|
'success': True,
|
|
'milestones': [{
|
|
'id': m.id,
|
|
'title': m.title,
|
|
'description': m.description,
|
|
'category': m.category,
|
|
'target_date': m.target_date.isoformat() if m.target_date else None,
|
|
'actual_date': m.actual_date.isoformat() if m.actual_date else None,
|
|
'status': m.status,
|
|
'source_url': m.source_url
|
|
} for m in milestones]
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/zopk-api/milestones', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.ADMIN)
|
|
def api_zopk_milestone_create():
|
|
"""API - utworzenie kamienia milowego."""
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
data = request.get_json()
|
|
milestone = ZOPKMilestone(
|
|
title=data['title'],
|
|
description=data.get('description'),
|
|
category=data.get('category', 'other'),
|
|
target_date=datetime.strptime(data['target_date'], '%Y-%m-%d').date() if data.get('target_date') else None,
|
|
actual_date=datetime.strptime(data['actual_date'], '%Y-%m-%d').date() if data.get('actual_date') else None,
|
|
status=data.get('status', 'planned'),
|
|
source_url=data.get('source_url'),
|
|
source_news_id=data.get('source_news_id')
|
|
)
|
|
db.add(milestone)
|
|
db.commit()
|
|
return jsonify({'success': True, 'id': milestone.id})
|
|
except Exception as e:
|
|
db.rollback()
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/zopk-api/milestones/<int:milestone_id>', methods=['PUT'])
|
|
@login_required
|
|
@role_required(SystemRole.ADMIN)
|
|
def api_zopk_milestone_update(milestone_id):
|
|
"""API - aktualizacja kamienia milowego."""
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
milestone = db.query(ZOPKMilestone).get(milestone_id)
|
|
if not milestone:
|
|
return jsonify({'error': 'Not found'}), 404
|
|
|
|
data = request.get_json()
|
|
if 'title' in data:
|
|
milestone.title = data['title']
|
|
if 'description' in data:
|
|
milestone.description = data['description']
|
|
if 'category' in data:
|
|
milestone.category = data['category']
|
|
if 'target_date' in data:
|
|
milestone.target_date = datetime.strptime(data['target_date'], '%Y-%m-%d').date() if data['target_date'] else None
|
|
if 'actual_date' in data:
|
|
milestone.actual_date = datetime.strptime(data['actual_date'], '%Y-%m-%d').date() if data['actual_date'] else None
|
|
if 'status' in data:
|
|
milestone.status = data['status']
|
|
if 'source_url' in data:
|
|
milestone.source_url = data['source_url']
|
|
|
|
db.commit()
|
|
return jsonify({'success': True})
|
|
except Exception as e:
|
|
db.rollback()
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/zopk-api/milestones/<int:milestone_id>', methods=['DELETE'])
|
|
@login_required
|
|
@role_required(SystemRole.ADMIN)
|
|
def api_zopk_milestone_delete(milestone_id):
|
|
"""API - usunięcie kamienia milowego."""
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
milestone = db.query(ZOPKMilestone).get(milestone_id)
|
|
if not milestone:
|
|
return jsonify({'error': 'Not found'}), 404
|
|
|
|
db.delete(milestone)
|
|
db.commit()
|
|
return jsonify({'success': True})
|
|
except Exception as e:
|
|
db.rollback()
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/zopk-api/timeline/suggestions')
|
|
@login_required
|
|
@role_required(SystemRole.ADMIN)
|
|
def api_zopk_timeline_suggestions():
|
|
"""API - sugestie kamieni milowych z bazy wiedzy."""
|
|
|
|
from zopk_knowledge_service import get_timeline_suggestions
|
|
|
|
limit = request.args.get('limit', 30, type=int)
|
|
only_verified = request.args.get('only_verified', 'false').lower() == 'true'
|
|
use_ai = request.args.get('use_ai', 'false').lower() == 'true'
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
result = get_timeline_suggestions(db, limit=limit, only_verified=only_verified)
|
|
|
|
if result['success'] and use_ai and result.get('suggestions'):
|
|
from zopk_knowledge_service import categorize_milestones_with_ai
|
|
result['suggestions'] = categorize_milestones_with_ai(db, result['suggestions'])
|
|
|
|
return jsonify(result)
|
|
except Exception as e:
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/zopk-api/timeline/suggestions/approve', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.ADMIN)
|
|
def api_zopk_timeline_suggestion_approve():
|
|
"""API - zatwierdzenie sugestii i utworzenie kamienia milowego."""
|
|
|
|
from zopk_knowledge_service import create_milestone_from_suggestion
|
|
|
|
data = request.get_json()
|
|
if not data:
|
|
return jsonify({'error': 'No data provided'}), 400
|
|
|
|
fact_id = data.get('fact_id')
|
|
if not fact_id:
|
|
return jsonify({'error': 'fact_id is required'}), 400
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
result = create_milestone_from_suggestion(
|
|
db_session=db,
|
|
fact_id=fact_id,
|
|
title=data.get('title', 'Kamień milowy'),
|
|
description=data.get('description'),
|
|
category=data.get('category', 'other'),
|
|
target_date=data.get('target_date'),
|
|
status=data.get('status', 'planned'),
|
|
source_url=data.get('source_url')
|
|
)
|
|
return jsonify(result)
|
|
except Exception as e:
|
|
db.rollback()
|
|
return jsonify({'success': False, 'error': str(e)}), 500
|
|
finally:
|
|
db.close()
|