diff --git a/app.py b/app.py index 4c0e494..ae2b320 100644 --- a/app.py +++ b/app.py @@ -107,7 +107,9 @@ from database import ( MembershipFee, MembershipFeeConfig, Person, - CompanyPerson + CompanyPerson, + GBPAudit, + ITAudit ) # Import services @@ -632,6 +634,16 @@ def company_detail(company_id): Person.nazwisko ).all() + # Load GBP audit (most recent) + gbp_audit = db.query(GBPAudit).filter_by( + company_id=company_id + ).order_by(GBPAudit.audit_date.desc()).first() + + # Load IT audit (most recent) + it_audit = db.query(ITAudit).filter_by( + company_id=company_id + ).order_by(ITAudit.audit_date.desc()).first() + return render_template('company_detail.html', company=company, maturity_data=maturity_data, @@ -643,7 +655,9 @@ def company_detail(company_id): social_media=social_media, contacts=contacts, recommendations=recommendations, - people=people + people=people, + gbp_audit=gbp_audit, + it_audit=it_audit ) finally: db.close() @@ -8109,7 +8123,8 @@ def admin_zopk_news_approve(news_id): except Exception as e: db.rollback() - return jsonify({'success': False, 'error': str(e)}), 500 + logger.error(f"Error approving ZOPK news {news_id}: {e}") + return jsonify({'success': False, 'error': 'Wystąpił błąd podczas zatwierdzania'}), 500 finally: db.close() @@ -8143,7 +8158,8 @@ def admin_zopk_news_reject(news_id): except Exception as e: db.rollback() - return jsonify({'success': False, 'error': str(e)}), 500 + logger.error(f"Error rejecting ZOPK news {news_id}: {e}") + return jsonify({'success': False, 'error': 'Wystąpił błąd podczas odrzucania'}), 500 finally: db.close() @@ -8274,7 +8290,8 @@ def admin_zopk_reject_old_news(): except Exception as e: db.rollback() - return jsonify({'success': False, 'error': str(e)}), 500 + logger.error(f"Error rejecting old ZOPK news: {e}") + return jsonify({'success': False, 'error': 'Wystąpił błąd podczas odrzucania starych newsów'}), 500 finally: db.close() @@ -8308,7 +8325,8 @@ def admin_zopk_evaluate_ai(): except Exception as e: db.rollback() - return jsonify({'success': False, 'error': str(e)}), 500 + logger.error(f"Error evaluating ZOPK news with AI: {e}") + return jsonify({'success': False, 'error': 'Wystąpił błąd podczas oceny AI'}), 500 finally: db.close() @@ -8342,7 +8360,8 @@ def admin_zopk_reevaluate_scores(): except Exception as e: db.rollback() - return jsonify({'success': False, 'error': str(e)}), 500 + logger.error(f"Error reevaluating ZOPK news scores: {e}") + return jsonify({'success': False, 'error': 'Wystąpił błąd podczas ponownej oceny'}), 500 finally: db.close() @@ -8425,13 +8444,13 @@ def api_zopk_search_news(): # Update job status on error try: fetch_job.status = 'failed' - fetch_job.error_message = str(e) + fetch_job.error_message = str(e) # Keep internal log fetch_job.completed_at = datetime.now() db.commit() except: pass - return jsonify({'success': False, 'error': str(e)}), 500 + return jsonify({'success': False, 'error': 'Wystąpił błąd podczas wyszukiwania newsów'}), 500 finally: db.close() diff --git a/database.py b/database.py index 77dcc17..548bb9f 100644 --- a/database.py +++ b/database.py @@ -45,6 +45,57 @@ DATABASE_URL = os.getenv( IS_SQLITE = DATABASE_URL.startswith('sqlite') +def normalize_social_url(url: str, platform: str = None) -> str: + """ + Normalize social media URLs to prevent duplicates. + + Handles: + - www vs non-www (removes www.) + - http vs https (forces https) + - Trailing slashes (removes) + - Platform-specific canonicalization + + Examples: + normalize_social_url('http://www.facebook.com/inpipl/') + -> 'https://facebook.com/inpipl' + + normalize_social_url('https://www.instagram.com/user/') + -> 'https://instagram.com/user' + """ + if not url: + return url + + url = url.strip() + + # Force https + if url.startswith('http://'): + url = 'https://' + url[7:] + elif not url.startswith('https://'): + url = 'https://' + url + + # Remove www. prefix + url = url.replace('https://www.', 'https://') + + # Remove trailing slash + url = url.rstrip('/') + + # Platform-specific normalization + if platform == 'facebook' or 'facebook.com' in url: + # fb.com -> facebook.com + url = url.replace('https://fb.com/', 'https://facebook.com/') + url = url.replace('https://m.facebook.com/', 'https://facebook.com/') + + if platform == 'twitter' or 'twitter.com' in url or 'x.com' in url: + # x.com -> twitter.com (or vice versa, pick one canonical) + url = url.replace('https://x.com/', 'https://twitter.com/') + + if platform == 'linkedin' or 'linkedin.com' in url: + # Remove locale prefix + url = url.replace('/pl/', '/').replace('/en/', '/') + + return url + + class StringArray(TypeDecorator): """ Platform-agnostic array type. diff --git a/scripts/social_media_audit.py b/scripts/social_media_audit.py index 6ba9a0f..f67a138 100644 --- a/scripts/social_media_audit.py +++ b/scripts/social_media_audit.py @@ -54,6 +54,25 @@ import whois from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker +# Add parent directory to path for imports +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) +try: + from database import normalize_social_url +except ImportError: + # Fallback: define locally if import fails + def normalize_social_url(url: str, platform: str = None) -> str: + """Normalize social media URLs to prevent duplicates.""" + if not url: + return url + url = url.strip() + if url.startswith('http://'): + url = 'https://' + url[7:] + elif not url.startswith('https://'): + url = 'https://' + url + url = url.replace('https://www.', 'https://') + url = url.rstrip('/') + return url + # Configure logging logging.basicConfig( level=logging.INFO, @@ -1097,6 +1116,9 @@ class SocialMediaAuditor: # Save social media for platform, url in result.get('social_media', {}).items(): + # Normalize URL to prevent www vs non-www duplicates + normalized_url = normalize_social_url(url, platform) + upsert_social = text(""" INSERT INTO company_social_media ( company_id, platform, url, verified_at, source, is_valid @@ -1112,7 +1134,7 @@ class SocialMediaAuditor: session.execute(upsert_social, { 'company_id': company_id, 'platform': platform, - 'url': url, + 'url': normalized_url, 'verified_at': result['audit_date'], 'source': 'website_scrape', 'is_valid': True, diff --git a/templates/company_detail.html b/templates/company_detail.html index 137daed..0f62caf 100755 --- a/templates/company_detail.html +++ b/templates/company_detail.html @@ -2261,6 +2261,441 @@ {% endif %} + +{% if gbp_audit and gbp_audit.completeness_score is not none %} +
+ Audyt Social Media +
+ + + {% set active_platforms = social_media|selectattr('is_valid', 'equalto', true)|list|length %} + {% set total_followers = social_media|selectattr('followers_count')|sum(attribute='followers_count') %} +