- 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>
188 lines
5.9 KiB
Python
188 lines
5.9 KiB
Python
"""
|
|
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()
|