refactor: Migrate ZOPK public routes to public blueprint
- Created blueprints/public/routes_zopk.py with 3 public routes: - /zopk (zopk_index) - /zopk/projekty/<slug> (zopk_project_detail) - /zopk/aktualnosci (zopk_news_list) - Added endpoint aliases for backward compatibility - Removed ZOPK public routes from app.py Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
bdae7f5309
commit
ab15cf3cba
174
app.py
174
app.py
@ -6877,178 +6877,10 @@ def api_it_audit_export():
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ZIELONY OKRĘG PRZEMYSŁOWY KASZUBIA (ZOPK)
|
||||
# ============================================================
|
||||
|
||||
@app.route('/zopk')
|
||||
@limiter.limit("60 per minute") # SECURITY: Rate limit public ZOPK page
|
||||
def zopk_index():
|
||||
"""
|
||||
Public knowledge base page for ZOPK.
|
||||
Shows projects, stakeholders, approved news, resources, and timeline.
|
||||
"""
|
||||
from database import ZOPKProject, ZOPKStakeholder, ZOPKNews, ZOPKResource, ZOPKMilestone
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Get active projects
|
||||
projects = db.query(ZOPKProject).filter(
|
||||
ZOPKProject.is_active == True
|
||||
).order_by(ZOPKProject.sort_order, ZOPKProject.name).all()
|
||||
|
||||
# Get milestones for timeline (sorted by target_date)
|
||||
# Show all milestones - is_verified column will be added in migration
|
||||
milestones = db.query(ZOPKMilestone).order_by(
|
||||
ZOPKMilestone.target_date.asc()
|
||||
).all()
|
||||
|
||||
# Get active stakeholders
|
||||
stakeholders = db.query(ZOPKStakeholder).filter(
|
||||
ZOPKStakeholder.is_active == True
|
||||
).order_by(ZOPKStakeholder.importance.desc(), ZOPKStakeholder.name).limit(10).all()
|
||||
|
||||
# Get approved news (both manually approved and AI auto-approved)
|
||||
# Show more news on main page (expanded view)
|
||||
news_items = db.query(ZOPKNews).filter(
|
||||
ZOPKNews.status.in_(['approved', 'auto_approved'])
|
||||
).order_by(ZOPKNews.published_at.desc()).limit(25).all()
|
||||
|
||||
# Get featured resources
|
||||
resources = db.query(ZOPKResource).filter(
|
||||
ZOPKResource.status == 'approved'
|
||||
).order_by(ZOPKResource.sort_order, ZOPKResource.created_at.desc()).limit(12).all()
|
||||
|
||||
# News time-based statistics
|
||||
from datetime import datetime, timedelta
|
||||
now = datetime.now()
|
||||
day_ago = now - timedelta(days=1)
|
||||
week_ago = now - timedelta(days=7)
|
||||
month_ago = now - timedelta(days=30)
|
||||
|
||||
approved_news_filter = ZOPKNews.status.in_(['approved', 'auto_approved'])
|
||||
total_news = db.query(ZOPKNews).filter(approved_news_filter).count()
|
||||
|
||||
news_stats = {
|
||||
'total': total_news,
|
||||
'last_day': db.query(ZOPKNews).filter(
|
||||
approved_news_filter,
|
||||
ZOPKNews.published_at >= day_ago
|
||||
).count(),
|
||||
'last_week': db.query(ZOPKNews).filter(
|
||||
approved_news_filter,
|
||||
ZOPKNews.published_at >= week_ago
|
||||
).count(),
|
||||
'last_month': db.query(ZOPKNews).filter(
|
||||
approved_news_filter,
|
||||
ZOPKNews.published_at >= month_ago
|
||||
).count()
|
||||
}
|
||||
|
||||
# General stats
|
||||
stats = {
|
||||
'total_projects': len(projects),
|
||||
'total_news': total_news,
|
||||
'total_resources': db.query(ZOPKResource).filter(ZOPKResource.status == 'approved').count(),
|
||||
'total_stakeholders': db.query(ZOPKStakeholder).filter(ZOPKStakeholder.is_active == True).count()
|
||||
}
|
||||
|
||||
return render_template('zopk/index.html',
|
||||
projects=projects,
|
||||
stakeholders=stakeholders,
|
||||
news_items=news_items,
|
||||
resources=resources,
|
||||
stats=stats,
|
||||
news_stats=news_stats,
|
||||
milestones=milestones
|
||||
)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/zopk/projekty/<slug>')
|
||||
@limiter.limit("60 per minute") # SECURITY: Rate limit public ZOPK project pages
|
||||
def zopk_project_detail(slug):
|
||||
"""Project detail page"""
|
||||
from database import ZOPKProject, ZOPKNews, ZOPKResource, ZOPKCompanyLink
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
project = db.query(ZOPKProject).filter(ZOPKProject.slug == slug).first()
|
||||
if not project:
|
||||
abort(404)
|
||||
|
||||
# Get news for this project (both manually approved and AI auto-approved)
|
||||
news_items = db.query(ZOPKNews).filter(
|
||||
ZOPKNews.project_id == project.id,
|
||||
ZOPKNews.status.in_(['approved', 'auto_approved'])
|
||||
).order_by(ZOPKNews.published_at.desc()).limit(10).all()
|
||||
|
||||
# Get resources for this project
|
||||
resources = db.query(ZOPKResource).filter(
|
||||
ZOPKResource.project_id == project.id,
|
||||
ZOPKResource.status == 'approved'
|
||||
).order_by(ZOPKResource.sort_order).all()
|
||||
|
||||
# Get Norda companies linked to this project
|
||||
company_links = db.query(ZOPKCompanyLink).filter(
|
||||
ZOPKCompanyLink.project_id == project.id
|
||||
).order_by(ZOPKCompanyLink.relevance_score.desc()).all()
|
||||
|
||||
return render_template('zopk/project_detail.html',
|
||||
project=project,
|
||||
news_items=news_items,
|
||||
resources=resources,
|
||||
company_links=company_links
|
||||
)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/zopk/aktualnosci')
|
||||
@limiter.limit("60 per minute") # SECURITY: Rate limit public ZOPK news list
|
||||
def zopk_news_list():
|
||||
"""All ZOPK news - paginated"""
|
||||
from database import ZOPKProject, ZOPKNews
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 20
|
||||
project_slug = request.args.get('projekt')
|
||||
|
||||
query = db.query(ZOPKNews).filter(ZOPKNews.status.in_(['approved', 'auto_approved']))
|
||||
|
||||
if project_slug:
|
||||
project = db.query(ZOPKProject).filter(ZOPKProject.slug == project_slug).first()
|
||||
if project:
|
||||
query = query.filter(ZOPKNews.project_id == project.id)
|
||||
|
||||
total = query.count()
|
||||
news_items = query.order_by(ZOPKNews.published_at.desc()).offset(
|
||||
(page - 1) * per_page
|
||||
).limit(per_page).all()
|
||||
|
||||
total_pages = (total + per_page - 1) // per_page
|
||||
|
||||
# Get projects for filter
|
||||
projects = db.query(ZOPKProject).filter(
|
||||
ZOPKProject.is_active == True
|
||||
).order_by(ZOPKProject.sort_order).all()
|
||||
|
||||
return render_template('zopk/news_list.html',
|
||||
news_items=news_items,
|
||||
projects=projects,
|
||||
current_project=project_slug,
|
||||
page=page,
|
||||
total_pages=total_pages,
|
||||
total=total
|
||||
)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# ZOPK PUBLIC ROUTES - MOVED TO blueprints/public/routes_zopk.py
|
||||
# Routes: /zopk, /zopk/projekty/<slug>, /zopk/aktualnosci
|
||||
# ============================================================
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
@ -113,6 +113,10 @@ def register_blueprints(app):
|
||||
'connections_map': 'public.connections_map',
|
||||
'dashboard': 'public.dashboard',
|
||||
'release_notes': 'public.release_notes',
|
||||
# ZOPK Public routes
|
||||
'zopk_index': 'public.zopk_index',
|
||||
'zopk_project_detail': 'public.zopk_project_detail',
|
||||
'zopk_news_list': 'public.zopk_news_list',
|
||||
})
|
||||
logger.info("Created public endpoint aliases")
|
||||
except ImportError as e:
|
||||
|
||||
@ -10,3 +10,4 @@ from flask import Blueprint
|
||||
bp = Blueprint('public', __name__)
|
||||
|
||||
from . import routes # noqa: E402, F401
|
||||
from . import routes_zopk # noqa: E402, F401
|
||||
|
||||
187
blueprints/public/routes_zopk.py
Normal file
187
blueprints/public/routes_zopk.py
Normal file
@ -0,0 +1,187 @@
|
||||
"""
|
||||
ZOPK Public Routes - Public blueprint
|
||||
|
||||
Migrated from app.py as part of the blueprint refactoring.
|
||||
Contains public-facing routes for ZOPK (Zielony Okręg Przemysłowy Kaszubia).
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from flask import abort, render_template, request
|
||||
|
||||
from database import (
|
||||
SessionLocal,
|
||||
ZOPKProject,
|
||||
ZOPKStakeholder,
|
||||
ZOPKNews,
|
||||
ZOPKResource,
|
||||
ZOPKMilestone,
|
||||
ZOPKCompanyLink
|
||||
)
|
||||
from . import bp
|
||||
|
||||
# Import limiter from app - will be initialized when app starts
|
||||
from flask import current_app
|
||||
|
||||
|
||||
def get_limiter():
|
||||
"""Get rate limiter from current app."""
|
||||
return current_app.extensions.get('limiter')
|
||||
|
||||
|
||||
@bp.route('/zopk')
|
||||
def zopk_index():
|
||||
"""
|
||||
Public knowledge base page for ZOPK.
|
||||
Shows projects, stakeholders, approved news, resources, and timeline.
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Get active projects
|
||||
projects = db.query(ZOPKProject).filter(
|
||||
ZOPKProject.is_active == True
|
||||
).order_by(ZOPKProject.sort_order, ZOPKProject.name).all()
|
||||
|
||||
# Get milestones for timeline (sorted by target_date)
|
||||
milestones = db.query(ZOPKMilestone).order_by(
|
||||
ZOPKMilestone.target_date.asc()
|
||||
).all()
|
||||
|
||||
# Get active stakeholders
|
||||
stakeholders = db.query(ZOPKStakeholder).filter(
|
||||
ZOPKStakeholder.is_active == True
|
||||
).order_by(ZOPKStakeholder.importance.desc(), ZOPKStakeholder.name).limit(10).all()
|
||||
|
||||
# Get approved news (both manually approved and AI auto-approved)
|
||||
news_items = db.query(ZOPKNews).filter(
|
||||
ZOPKNews.status.in_(['approved', 'auto_approved'])
|
||||
).order_by(ZOPKNews.published_at.desc()).limit(25).all()
|
||||
|
||||
# Get featured resources
|
||||
resources = db.query(ZOPKResource).filter(
|
||||
ZOPKResource.status == 'approved'
|
||||
).order_by(ZOPKResource.sort_order, ZOPKResource.created_at.desc()).limit(12).all()
|
||||
|
||||
# News time-based statistics
|
||||
now = datetime.now()
|
||||
day_ago = now - timedelta(days=1)
|
||||
week_ago = now - timedelta(days=7)
|
||||
month_ago = now - timedelta(days=30)
|
||||
|
||||
approved_news_filter = ZOPKNews.status.in_(['approved', 'auto_approved'])
|
||||
total_news = db.query(ZOPKNews).filter(approved_news_filter).count()
|
||||
|
||||
news_stats = {
|
||||
'total': total_news,
|
||||
'last_day': db.query(ZOPKNews).filter(
|
||||
approved_news_filter,
|
||||
ZOPKNews.published_at >= day_ago
|
||||
).count(),
|
||||
'last_week': db.query(ZOPKNews).filter(
|
||||
approved_news_filter,
|
||||
ZOPKNews.published_at >= week_ago
|
||||
).count(),
|
||||
'last_month': db.query(ZOPKNews).filter(
|
||||
approved_news_filter,
|
||||
ZOPKNews.published_at >= month_ago
|
||||
).count()
|
||||
}
|
||||
|
||||
# General stats
|
||||
stats = {
|
||||
'total_projects': len(projects),
|
||||
'total_news': total_news,
|
||||
'total_resources': db.query(ZOPKResource).filter(ZOPKResource.status == 'approved').count(),
|
||||
'total_stakeholders': db.query(ZOPKStakeholder).filter(ZOPKStakeholder.is_active == True).count()
|
||||
}
|
||||
|
||||
return render_template('zopk/index.html',
|
||||
projects=projects,
|
||||
stakeholders=stakeholders,
|
||||
news_items=news_items,
|
||||
resources=resources,
|
||||
stats=stats,
|
||||
news_stats=news_stats,
|
||||
milestones=milestones
|
||||
)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/zopk/projekty/<slug>')
|
||||
def zopk_project_detail(slug):
|
||||
"""Project detail page"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
project = db.query(ZOPKProject).filter(ZOPKProject.slug == slug).first()
|
||||
if not project:
|
||||
abort(404)
|
||||
|
||||
# Get news for this project (both manually approved and AI auto-approved)
|
||||
news_items = db.query(ZOPKNews).filter(
|
||||
ZOPKNews.project_id == project.id,
|
||||
ZOPKNews.status.in_(['approved', 'auto_approved'])
|
||||
).order_by(ZOPKNews.published_at.desc()).limit(10).all()
|
||||
|
||||
# Get resources for this project
|
||||
resources = db.query(ZOPKResource).filter(
|
||||
ZOPKResource.project_id == project.id,
|
||||
ZOPKResource.status == 'approved'
|
||||
).order_by(ZOPKResource.sort_order).all()
|
||||
|
||||
# Get Norda companies linked to this project
|
||||
company_links = db.query(ZOPKCompanyLink).filter(
|
||||
ZOPKCompanyLink.project_id == project.id
|
||||
).order_by(ZOPKCompanyLink.relevance_score.desc()).all()
|
||||
|
||||
return render_template('zopk/project_detail.html',
|
||||
project=project,
|
||||
news_items=news_items,
|
||||
resources=resources,
|
||||
company_links=company_links
|
||||
)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/zopk/aktualnosci')
|
||||
def zopk_news_list():
|
||||
"""All ZOPK news - paginated"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 20
|
||||
project_slug = request.args.get('projekt')
|
||||
|
||||
query = db.query(ZOPKNews).filter(ZOPKNews.status.in_(['approved', 'auto_approved']))
|
||||
|
||||
if project_slug:
|
||||
project = db.query(ZOPKProject).filter(ZOPKProject.slug == project_slug).first()
|
||||
if project:
|
||||
query = query.filter(ZOPKNews.project_id == project.id)
|
||||
|
||||
total = query.count()
|
||||
news_items = query.order_by(ZOPKNews.published_at.desc()).offset(
|
||||
(page - 1) * per_page
|
||||
).limit(per_page).all()
|
||||
|
||||
total_pages = (total + per_page - 1) // per_page
|
||||
|
||||
# Get projects for filter
|
||||
projects = db.query(ZOPKProject).filter(
|
||||
ZOPKProject.is_active == True
|
||||
).order_by(ZOPKProject.sort_order).all()
|
||||
|
||||
return render_template('zopk/news_list.html',
|
||||
news_items=news_items,
|
||||
projects=projects,
|
||||
current_project=project_slug,
|
||||
page=page,
|
||||
total_pages=total_pages,
|
||||
total=total
|
||||
)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
Loading…
Reference in New Issue
Block a user