diff --git a/blueprints/admin/routes_pej.py b/blueprints/admin/routes_pej.py index f59e78e..f03cf3c 100644 --- a/blueprints/admin/routes_pej.py +++ b/blueprints/admin/routes_pej.py @@ -8,7 +8,7 @@ from flask import Response from flask_login import login_required, current_user from . import bp -from database import db_session, ZOPKCompanyLink, Company +from database import SessionLocal, ZOPKCompanyLink, Company from blueprints.pej_constants import get_nuclear_project_ids, LINK_TYPE_LABELS @@ -19,44 +19,48 @@ def pej_export_csv(): if not current_user.can_access_admin_panel(): return "Brak uprawnień", 403 - nuclear_ids = get_nuclear_project_ids() + db = SessionLocal() + try: + nuclear_ids = get_nuclear_project_ids(db) - results = db_session.query(ZOPKCompanyLink, Company).join( - Company, ZOPKCompanyLink.company_id == Company.id - ).filter( - ZOPKCompanyLink.project_id.in_(nuclear_ids), - ZOPKCompanyLink.relevance_score >= 25, - Company.status == 'active' - ).order_by(ZOPKCompanyLink.relevance_score.desc()).all() + results = db.query(ZOPKCompanyLink, Company).join( + Company, ZOPKCompanyLink.company_id == Company.id + ).filter( + ZOPKCompanyLink.project_id.in_(nuclear_ids), + ZOPKCompanyLink.relevance_score >= 25, + Company.status == 'active' + ).order_by(ZOPKCompanyLink.relevance_score.desc()).all() - output = io.StringIO() - # UTF-8 BOM for Excel - output.write('\ufeff') + output = io.StringIO() + # UTF-8 BOM for Excel + output.write('\ufeff') - writer = csv.writer(output, delimiter=';', quoting=csv.QUOTE_ALL) - writer.writerow([ - 'Nazwa firmy', 'Email', 'Telefon', 'Branża', 'PKD (główny)', - 'Usługi', 'Typ współpracy PEJ', 'Opis współpracy', 'Score', 'Miasto' - ]) - - for link, company in results: + writer = csv.writer(output, delimiter=';', quoting=csv.QUOTE_ALL) writer.writerow([ - company.name or '', - company.email or '', - company.phone or '', - company.category.name if company.category else '', - company.pkd_code or '', - company.services_offered or '', - LINK_TYPE_LABELS.get(link.link_type, link.link_type or ''), - link.collaboration_description or '', - link.relevance_score or 0, - company.address_city or '' + 'Nazwa firmy', 'Email', 'Telefon', 'Branża', 'PKD (główny)', + 'Usługi', 'Typ współpracy PEJ', 'Opis współpracy', 'Score', 'Miasto' ]) - filename = f'pej-local-content-{date.today().isoformat()}.csv' + for link, company in results: + writer.writerow([ + company.name or '', + company.email or '', + company.phone or '', + company.category.name if company.category else '', + company.pkd_code or '', + company.services_offered or '', + LINK_TYPE_LABELS.get(link.link_type, link.link_type or ''), + link.collaboration_description or '', + link.relevance_score or 0, + company.address_city or '' + ]) - return Response( - output.getvalue(), - mimetype='text/csv; charset=utf-8', - headers={'Content-Disposition': f'attachment; filename="{filename}"'} - ) + filename = f'pej-local-content-{date.today().isoformat()}.csv' + + return Response( + output.getvalue(), + mimetype='text/csv; charset=utf-8', + headers={'Content-Disposition': f'attachment; filename="{filename}"'} + ) + finally: + db.close() diff --git a/blueprints/pej_constants.py b/blueprints/pej_constants.py index 5c16577..6156fcb 100644 --- a/blueprints/pej_constants.py +++ b/blueprints/pej_constants.py @@ -1,6 +1,6 @@ """Shared constants and helpers for PEJ section.""" -from database import db_session, ZOPKProject +from database import SessionLocal, ZOPKProject # Explicit slug list — easy to extend with SMR projects later NUCLEAR_PROJECT_SLUGS = ['nuclear-plant'] @@ -13,10 +13,18 @@ LINK_TYPE_LABELS = { } -def get_nuclear_project_ids(): +def get_nuclear_project_ids(db=None): """Return IDs of nuclear projects from ZOPK.""" - projects = db_session.query(ZOPKProject.id).filter( - ZOPKProject.slug.in_(NUCLEAR_PROJECT_SLUGS), - ZOPKProject.project_type == 'energy' - ).all() - return [p.id for p in projects] + close = False + if db is None: + db = SessionLocal() + close = True + try: + projects = db.query(ZOPKProject.id).filter( + ZOPKProject.slug.in_(NUCLEAR_PROJECT_SLUGS), + ZOPKProject.project_type == 'energy' + ).all() + return [p.id for p in projects] + finally: + if close: + db.close() diff --git a/blueprints/public/routes_pej.py b/blueprints/public/routes_pej.py index 1040cbf..6dfb809 100644 --- a/blueprints/public/routes_pej.py +++ b/blueprints/public/routes_pej.py @@ -8,7 +8,7 @@ from sqlalchemy import func from . import bp from database import ( - db_session, ZOPKNews, ZOPKMilestone, + SessionLocal, ZOPKNews, ZOPKMilestone, ZOPKCompanyLink, Company, Announcement, Category ) from blueprints.pej_constants import get_nuclear_project_ids, LINK_TYPE_LABELS @@ -18,158 +18,170 @@ from blueprints.pej_constants import get_nuclear_project_ids, LINK_TYPE_LABELS @login_required def pej_index(): """PEJ landing page — hero, stats, news, timeline, top companies, announcements.""" - nuclear_ids = get_nuclear_project_ids() - if not nuclear_ids: - abort(404) + db = SessionLocal() + try: + nuclear_ids = get_nuclear_project_ids(db) + if not nuclear_ids: + abort(404) - # Stats - companies_count = db_session.query(func.count(ZOPKCompanyLink.id)).filter( - ZOPKCompanyLink.project_id.in_(nuclear_ids), - ZOPKCompanyLink.relevance_score >= 25 - ).scalar() or 0 + # Stats + companies_count = db.query(func.count(ZOPKCompanyLink.id)).filter( + ZOPKCompanyLink.project_id.in_(nuclear_ids), + ZOPKCompanyLink.relevance_score >= 25 + ).scalar() or 0 - news_count = db_session.query(func.count(ZOPKNews.id)).filter( - ZOPKNews.project_id.in_(nuclear_ids), - ZOPKNews.status.in_(['approved', 'auto_approved']) - ).scalar() or 0 + news_count = db.query(func.count(ZOPKNews.id)).filter( + ZOPKNews.project_id.in_(nuclear_ids), + ZOPKNews.status.in_(['approved', 'auto_approved']) + ).scalar() or 0 - milestones_count = db_session.query(func.count(ZOPKMilestone.id)).filter( - ZOPKMilestone.category == 'nuclear' - ).scalar() or 0 + milestones_count = db.query(func.count(ZOPKMilestone.id)).filter( + ZOPKMilestone.category == 'nuclear' + ).scalar() or 0 - # Latest news (4) - news = db_session.query(ZOPKNews).filter( - ZOPKNews.project_id.in_(nuclear_ids), - ZOPKNews.status.in_(['approved', 'auto_approved']) - ).order_by(ZOPKNews.published_at.desc()).limit(4).all() + # Latest news (4) + news = db.query(ZOPKNews).filter( + ZOPKNews.project_id.in_(nuclear_ids), + ZOPKNews.status.in_(['approved', 'auto_approved']) + ).order_by(ZOPKNews.published_at.desc()).limit(4).all() - # Nuclear milestones - milestones = db_session.query(ZOPKMilestone).filter( - ZOPKMilestone.category == 'nuclear' - ).order_by(ZOPKMilestone.target_date.asc()).all() + # Nuclear milestones + milestones = db.query(ZOPKMilestone).filter( + ZOPKMilestone.category == 'nuclear' + ).order_by(ZOPKMilestone.target_date.asc()).all() - # Top 6 companies by relevance - top_companies = db_session.query(ZOPKCompanyLink, Company).join( - Company, ZOPKCompanyLink.company_id == Company.id - ).filter( - ZOPKCompanyLink.project_id.in_(nuclear_ids), - ZOPKCompanyLink.relevance_score >= 25, - Company.status == 'active' - ).order_by(ZOPKCompanyLink.relevance_score.desc()).limit(6).all() + # Top 6 companies by relevance + top_companies = db.query(ZOPKCompanyLink, Company).join( + Company, ZOPKCompanyLink.company_id == Company.id + ).filter( + ZOPKCompanyLink.project_id.in_(nuclear_ids), + ZOPKCompanyLink.relevance_score >= 25, + Company.status == 'active' + ).order_by(ZOPKCompanyLink.relevance_score.desc()).limit(6).all() - # PEJ announcements (status='published' in Announcement model) - announcements = db_session.query(Announcement).filter( - Announcement.categories.contains(['pej']), - Announcement.status == 'published' - ).order_by(Announcement.created_at.desc()).limit(3).all() + # PEJ announcements (status='published' in Announcement model) + announcements = db.query(Announcement).filter( + Announcement.categories.contains(['pej']), + Announcement.status == 'published' + ).order_by(Announcement.created_at.desc()).limit(3).all() - return render_template('pej/index.html', - companies_count=companies_count, - news_count=news_count, - milestones_count=milestones_count, - news=news, - milestones=milestones, - top_companies=top_companies, - announcements=announcements, - link_type_labels=LINK_TYPE_LABELS - ) + return render_template('pej/index.html', + companies_count=companies_count, + news_count=news_count, + milestones_count=milestones_count, + news=news, + milestones=milestones, + top_companies=top_companies, + announcements=announcements, + link_type_labels=LINK_TYPE_LABELS + ) + finally: + db.close() @bp.route('/pej/local-content') @login_required def pej_local_content(): """Full list of Norda companies matched to nuclear projects.""" - nuclear_ids = get_nuclear_project_ids() - if not nuclear_ids: - abort(404) + db = SessionLocal() + try: + nuclear_ids = get_nuclear_project_ids(db) + if not nuclear_ids: + abort(404) - page = request.args.get('page', 1, type=int) - per_page = 20 - category_filter = request.args.get('category', 0, type=int) - link_type_filter = request.args.get('link_type', '') - search_query = request.args.get('q', '') + page = request.args.get('page', 1, type=int) + per_page = 20 + category_filter = request.args.get('category', 0, type=int) + link_type_filter = request.args.get('link_type', '') + search_query = request.args.get('q', '') - query = db_session.query(ZOPKCompanyLink, Company).join( - Company, ZOPKCompanyLink.company_id == Company.id - ).filter( - ZOPKCompanyLink.project_id.in_(nuclear_ids), - ZOPKCompanyLink.relevance_score >= 25, - Company.status == 'active' - ) + query = db.query(ZOPKCompanyLink, Company).join( + Company, ZOPKCompanyLink.company_id == Company.id + ).filter( + ZOPKCompanyLink.project_id.in_(nuclear_ids), + ZOPKCompanyLink.relevance_score >= 25, + Company.status == 'active' + ) - if category_filter: - query = query.filter(Company.category_id == category_filter) - if link_type_filter: - query = query.filter(ZOPKCompanyLink.link_type == link_type_filter) - if search_query: - query = query.filter(Company.name.ilike(f'%{search_query}%')) + if category_filter: + query = query.filter(Company.category_id == category_filter) + if link_type_filter: + query = query.filter(ZOPKCompanyLink.link_type == link_type_filter) + if search_query: + query = query.filter(Company.name.ilike(f'%{search_query}%')) - total = query.count() - results = query.order_by( - ZOPKCompanyLink.relevance_score.desc() - ).offset((page - 1) * per_page).limit(per_page).all() + total = query.count() + results = query.order_by( + ZOPKCompanyLink.relevance_score.desc() + ).offset((page - 1) * per_page).limit(per_page).all() - # Get distinct categories for filter dropdown - category_ids = db_session.query(Company.category_id).join( - ZOPKCompanyLink, Company.id == ZOPKCompanyLink.company_id - ).filter( - ZOPKCompanyLink.project_id.in_(nuclear_ids), - ZOPKCompanyLink.relevance_score >= 25, - Company.status == 'active', - Company.category_id.isnot(None) - ).distinct().all() - category_ids = [c[0] for c in category_ids if c[0]] - categories = db_session.query(Category).filter( - Category.id.in_(category_ids) - ).order_by(Category.name).all() if category_ids else [] + # Get distinct categories for filter dropdown + category_ids = db.query(Company.category_id).join( + ZOPKCompanyLink, Company.id == ZOPKCompanyLink.company_id + ).filter( + ZOPKCompanyLink.project_id.in_(nuclear_ids), + ZOPKCompanyLink.relevance_score >= 25, + Company.status == 'active', + Company.category_id.isnot(None) + ).distinct().all() + category_ids = [c[0] for c in category_ids if c[0]] + categories = db.query(Category).filter( + Category.id.in_(category_ids) + ).order_by(Category.name).all() if category_ids else [] - link_types = db_session.query(ZOPKCompanyLink.link_type).filter( - ZOPKCompanyLink.project_id.in_(nuclear_ids), - ZOPKCompanyLink.relevance_score >= 25 - ).distinct().all() - link_types = sorted([lt[0] for lt in link_types if lt[0]]) + link_types = db.query(ZOPKCompanyLink.link_type).filter( + ZOPKCompanyLink.project_id.in_(nuclear_ids), + ZOPKCompanyLink.relevance_score >= 25 + ).distinct().all() + link_types = sorted([lt[0] for lt in link_types if lt[0]]) - total_pages = math.ceil(total / per_page) if total > 0 else 1 + total_pages = math.ceil(total / per_page) if total > 0 else 1 - return render_template('pej/local_content.html', - results=results, - total=total, - page=page, - per_page=per_page, - total_pages=total_pages, - categories=categories, - link_types=link_types, - link_type_labels=LINK_TYPE_LABELS, - category_filter=category_filter, - link_type_filter=link_type_filter, - search_query=search_query - ) + return render_template('pej/local_content.html', + results=results, + total=total, + page=page, + per_page=per_page, + total_pages=total_pages, + categories=categories, + link_types=link_types, + link_type_labels=LINK_TYPE_LABELS, + category_filter=category_filter, + link_type_filter=link_type_filter, + search_query=search_query + ) + finally: + db.close() @bp.route('/pej/aktualnosci') @login_required def pej_news(): """Nuclear news list with pagination.""" - nuclear_ids = get_nuclear_project_ids() - if not nuclear_ids: - abort(404) + db = SessionLocal() + try: + nuclear_ids = get_nuclear_project_ids(db) + if not nuclear_ids: + abort(404) - page = request.args.get('page', 1, type=int) - per_page = 20 + page = request.args.get('page', 1, type=int) + per_page = 20 - query = db_session.query(ZOPKNews).filter( - ZOPKNews.project_id.in_(nuclear_ids), - ZOPKNews.status.in_(['approved', 'auto_approved']) - ).order_by(ZOPKNews.published_at.desc()) + query = db.query(ZOPKNews).filter( + ZOPKNews.project_id.in_(nuclear_ids), + ZOPKNews.status.in_(['approved', 'auto_approved']) + ).order_by(ZOPKNews.published_at.desc()) - total = query.count() - news = query.offset((page - 1) * per_page).limit(per_page).all() + total = query.count() + news = query.offset((page - 1) * per_page).limit(per_page).all() - total_pages = math.ceil(total / per_page) if total > 0 else 1 + total_pages = math.ceil(total / per_page) if total > 0 else 1 - return render_template('pej/news.html', - news=news, - page=page, - total=total, - total_pages=total_pages - ) + return render_template('pej/news.html', + news=news, + page=page, + total=total, + total_pages=total_pages + ) + finally: + db.close()