""" Reports Routes ============== Business analytics and reporting endpoints. """ from datetime import datetime, date from flask import render_template, url_for from flask_login import login_required from sqlalchemy import func from sqlalchemy.orm import joinedload from . import bp from database import SessionLocal, Company, Category, CompanySocialMedia @bp.route('/', endpoint='reports_index') @login_required def index(): """Lista dostępnych raportów.""" reports = [ { 'id': 'staz-czlonkostwa', 'title': 'Staż członkostwa w Izbie NORDA', 'description': 'Zestawienie firm według daty przystąpienia do Izby. Pokazuje historię i lojalność członków.', 'icon': '🏆', 'url': url_for('.report_membership') }, { 'id': 'social-media', 'title': 'Pokrycie Social Media', 'description': 'Analiza obecności firm w mediach społecznościowych: Facebook, Instagram, LinkedIn, YouTube, TikTok, X.', 'icon': '📱', 'url': url_for('.report_social_media') }, { 'id': 'struktura-branzowa', 'title': 'Struktura branżowa', 'description': 'Rozkład firm według kategorii działalności: IT, Budownictwo, Usługi, Produkcja, Handel.', 'icon': '🏢', 'url': url_for('.report_categories') }, ] return render_template('reports/index.html', reports=reports) @bp.route('/staz-czlonkostwa', endpoint='report_membership') @login_required def membership(): """Raport: Staż członkostwa w Izbie NORDA.""" db = SessionLocal() try: # Firmy z member_since, posortowane od najstarszego companies = db.query(Company).filter( Company.member_since.isnot(None) ).order_by(Company.member_since.asc()).all() # Statystyki today = date.today() stats = { 'total_with_date': len(companies), 'total_without_date': db.query(Company).filter( Company.member_since.is_(None) ).count(), 'oldest': companies[0] if companies else None, 'newest': companies[-1] if companies else None, 'avg_years': sum( (today - c.member_since).days / 365.25 for c in companies ) / len(companies) if companies else 0 } # Dodaj obliczony staż do każdej firmy for c in companies: c.membership_years = int((today - c.member_since).days / 365.25) # Dodaj też do oldest i newest if stats['oldest']: stats['oldest'].membership_years = int((today - stats['oldest'].member_since).days / 365.25) return render_template( 'reports/membership.html', companies=companies, stats=stats, generated_at=datetime.now() ) finally: db.close() @bp.route('/social-media', endpoint='report_social_media') @login_required def social_media(): """Raport: Pokrycie Social Media.""" db = SessionLocal() try: # Wszystkie firmy z ich profilami social media companies = db.query(Company).options( joinedload(Company.social_media_profiles) ).order_by(Company.name).all() platforms = ['facebook', 'instagram', 'linkedin', 'youtube', 'tiktok', 'twitter'] # Statystyki platform platform_stats = {} for platform in platforms: count = db.query(CompanySocialMedia).filter_by( platform=platform ).count() platform_stats[platform] = { 'count': count, 'percent': round(count / len(companies) * 100, 1) if companies else 0 } # Firmy z min. 1 profilem companies_with_social = [ c for c in companies if c.social_media_profiles ] stats = { 'total_companies': len(companies), 'with_social': len(companies_with_social), 'without_social': len(companies) - len(companies_with_social), 'coverage_percent': round( len(companies_with_social) / len(companies) * 100, 1 ) if companies else 0 } return render_template( 'reports/social_media.html', companies=companies, platforms=platforms, platform_stats=platform_stats, stats=stats, generated_at=datetime.now() ) finally: db.close() @bp.route('/struktura-branzowa', endpoint='report_categories') @login_required def categories(): """Raport: Struktura branżowa.""" db = SessionLocal() try: # Grupowanie po category_id (kolumna FK, nie relacja) category_counts = db.query( Company.category_id, func.count(Company.id).label('count') ).group_by(Company.category_id).all() total = sum(c.count for c in category_counts) # Pobierz mapę kategorii (id -> name) jednym zapytaniem category_map = {cat.id: cat.name for cat in db.query(Category).all()} categories_list = [] for cat in category_counts: cat_id = cat.category_id cat_name = category_map.get(cat_id, 'Brak kategorii') if cat_id else 'Brak kategorii' examples = db.query(Company.name).filter( Company.category_id == cat_id ).limit(3).all() categories_list.append({ 'name': cat_name, 'count': cat.count, 'percent': round(cat.count / total * 100, 1) if total else 0, 'examples': [e.name for e in examples] }) # Sortuj od największej categories_list.sort(key=lambda x: x['count'], reverse=True) return render_template( 'reports/categories.html', categories=categories_list, total=total, generated_at=datetime.now() ) finally: db.close()