refactor: Move GBP and Social Audit API routes to blueprints
- Create blueprints/api/routes_gbp_audit.py (4 routes) - Create blueprints/api/routes_social_audit.py (1 route) - app.py: 6354 → 5860 lines (-494 total this batch) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
60c19ec188
commit
8e3346178d
502
app.py
502
app.py
@ -1243,355 +1243,9 @@ def api_connections():
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# GBP (GOOGLE BUSINESS PROFILE) AUDIT API
|
# GBP AUDIT API - MOVED TO: blueprints/api/routes_gbp_audit.py
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
@app.route('/api/gbp/audit/health')
|
|
||||||
def api_gbp_audit_health():
|
|
||||||
"""
|
|
||||||
API: Health check for GBP audit service.
|
|
||||||
|
|
||||||
Returns service status and version information.
|
|
||||||
Used by monitoring systems to verify service availability.
|
|
||||||
"""
|
|
||||||
if GBP_AUDIT_AVAILABLE:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'ok',
|
|
||||||
'service': 'gbp_audit',
|
|
||||||
'version': GBP_AUDIT_VERSION,
|
|
||||||
'available': True
|
|
||||||
}), 200
|
|
||||||
else:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'unavailable',
|
|
||||||
'service': 'gbp_audit',
|
|
||||||
'available': False,
|
|
||||||
'error': 'GBP audit service not loaded'
|
|
||||||
}), 503
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/gbp/audit', methods=['GET'])
|
|
||||||
def api_gbp_audit_get():
|
|
||||||
"""
|
|
||||||
API: Get GBP audit results for a company.
|
|
||||||
|
|
||||||
Query parameters:
|
|
||||||
- company_id: Company ID (integer) OR
|
|
||||||
- slug: Company slug (string)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
- Latest audit results with completeness score and recommendations
|
|
||||||
- 404 if company not found
|
|
||||||
- 404 if no audit exists for the company
|
|
||||||
|
|
||||||
Example: GET /api/gbp/audit?company_id=26
|
|
||||||
Example: GET /api/gbp/audit?slug=pixlab-sp-z-o-o
|
|
||||||
"""
|
|
||||||
if not GBP_AUDIT_AVAILABLE:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Usługa audytu GBP jest niedostępna.'
|
|
||||||
}), 503
|
|
||||||
|
|
||||||
company_id = request.args.get('company_id', type=int)
|
|
||||||
slug = request.args.get('slug')
|
|
||||||
|
|
||||||
if not company_id and not slug:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Podaj company_id lub slug firmy.'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
db = SessionLocal()
|
|
||||||
try:
|
|
||||||
# Find company
|
|
||||||
if company_id:
|
|
||||||
company = db.query(Company).filter_by(id=company_id, status='active').first()
|
|
||||||
else:
|
|
||||||
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
|
||||||
|
|
||||||
if not company:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Firma nie znaleziona lub nieaktywna.'
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
# Get latest audit
|
|
||||||
audit = gbp_get_company_audit(db, company.id)
|
|
||||||
|
|
||||||
if not audit:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Brak wyników audytu GBP dla firmy "{company.name}". Uruchom audyt używając POST /api/gbp/audit.',
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
# Build response
|
|
||||||
return jsonify({
|
|
||||||
'success': True,
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name,
|
|
||||||
'company_slug': company.slug,
|
|
||||||
'audit': {
|
|
||||||
'id': audit.id,
|
|
||||||
'audit_date': audit.audit_date.isoformat() if audit.audit_date else None,
|
|
||||||
'completeness_score': audit.completeness_score,
|
|
||||||
'score_category': audit.score_category,
|
|
||||||
'fields_status': audit.fields_status,
|
|
||||||
'recommendations': audit.recommendations,
|
|
||||||
'has_name': audit.has_name,
|
|
||||||
'has_address': audit.has_address,
|
|
||||||
'has_phone': audit.has_phone,
|
|
||||||
'has_website': audit.has_website,
|
|
||||||
'has_hours': audit.has_hours,
|
|
||||||
'has_categories': audit.has_categories,
|
|
||||||
'has_photos': audit.has_photos,
|
|
||||||
'has_description': audit.has_description,
|
|
||||||
'has_services': audit.has_services,
|
|
||||||
'has_reviews': audit.has_reviews,
|
|
||||||
'photo_count': audit.photo_count,
|
|
||||||
'review_count': audit.review_count,
|
|
||||||
'average_rating': float(audit.average_rating) if audit.average_rating else None,
|
|
||||||
'google_place_id': audit.google_place_id,
|
|
||||||
'audit_source': audit.audit_source,
|
|
||||||
'audit_version': audit.audit_version
|
|
||||||
}
|
|
||||||
}), 200
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error fetching GBP audit: {e}")
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Błąd podczas pobierania audytu: {str(e)}'
|
|
||||||
}), 500
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/gbp/audit/<slug>')
|
|
||||||
def api_gbp_audit_by_slug(slug):
|
|
||||||
"""
|
|
||||||
API: Get GBP audit results for a company by slug.
|
|
||||||
Convenience endpoint that uses slug from URL path.
|
|
||||||
|
|
||||||
Example: GET /api/gbp/audit/pixlab-sp-z-o-o
|
|
||||||
"""
|
|
||||||
if not GBP_AUDIT_AVAILABLE:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Usługa audytu GBP jest niedostępna.'
|
|
||||||
}), 503
|
|
||||||
|
|
||||||
db = SessionLocal()
|
|
||||||
try:
|
|
||||||
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
|
||||||
|
|
||||||
if not company:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Firma o slug "{slug}" nie znaleziona.'
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
audit = gbp_get_company_audit(db, company.id)
|
|
||||||
|
|
||||||
if not audit:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Brak wyników audytu GBP dla firmy "{company.name}".',
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
'success': True,
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name,
|
|
||||||
'company_slug': company.slug,
|
|
||||||
'audit': {
|
|
||||||
'id': audit.id,
|
|
||||||
'audit_date': audit.audit_date.isoformat() if audit.audit_date else None,
|
|
||||||
'completeness_score': audit.completeness_score,
|
|
||||||
'score_category': audit.score_category,
|
|
||||||
'fields_status': audit.fields_status,
|
|
||||||
'recommendations': audit.recommendations,
|
|
||||||
'photo_count': audit.photo_count,
|
|
||||||
'review_count': audit.review_count,
|
|
||||||
'average_rating': float(audit.average_rating) if audit.average_rating else None
|
|
||||||
}
|
|
||||||
}), 200
|
|
||||||
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/gbp/audit', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@limiter.limit("20 per hour")
|
|
||||||
def api_gbp_audit_trigger():
|
|
||||||
"""
|
|
||||||
API: Run GBP audit for a company.
|
|
||||||
|
|
||||||
This endpoint runs a completeness audit for Google Business Profile data,
|
|
||||||
checking fields like name, address, phone, website, hours, categories,
|
|
||||||
photos, description, services, and reviews.
|
|
||||||
|
|
||||||
Request JSON body:
|
|
||||||
- company_id: Company ID (integer) OR
|
|
||||||
- slug: Company slug (string)
|
|
||||||
- save: Whether to save results to database (default: true)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
- Success: Audit results with completeness score and recommendations
|
|
||||||
- Error: Error message with status code
|
|
||||||
|
|
||||||
Access:
|
|
||||||
- Members can audit their own company
|
|
||||||
- Admins can audit any company
|
|
||||||
|
|
||||||
Rate limited to 20 requests per hour per user.
|
|
||||||
"""
|
|
||||||
if not GBP_AUDIT_AVAILABLE:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Usługa audytu GBP jest niedostępna. Sprawdź konfigurację serwera.'
|
|
||||||
}), 503
|
|
||||||
|
|
||||||
# Parse request data
|
|
||||||
data = request.get_json()
|
|
||||||
if not data:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Brak danych w żądaniu. Podaj company_id lub slug.'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
company_id = data.get('company_id')
|
|
||||||
slug = data.get('slug')
|
|
||||||
save_result = data.get('save', True)
|
|
||||||
|
|
||||||
if not company_id and not slug:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Podaj company_id lub slug firmy do audytu.'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
db = SessionLocal()
|
|
||||||
try:
|
|
||||||
# Find company by ID or slug
|
|
||||||
if company_id:
|
|
||||||
company = db.query(Company).filter_by(id=company_id, status='active').first()
|
|
||||||
else:
|
|
||||||
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
|
||||||
|
|
||||||
if not company:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Firma nie znaleziona lub nieaktywna.'
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
# Check access: admin can audit any company, member only their own
|
|
||||||
if not current_user.is_admin:
|
|
||||||
# Check if user is associated with this company
|
|
||||||
if current_user.company_id != company.id:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Brak uprawnień. Możesz audytować tylko własną firmę.'
|
|
||||||
}), 403
|
|
||||||
|
|
||||||
logger.info(f"GBP audit triggered by {current_user.email} for company: {company.name} (ID: {company.id})")
|
|
||||||
|
|
||||||
# Option to fetch fresh Google data before audit
|
|
||||||
fetch_google = data.get('fetch_google', True)
|
|
||||||
force_refresh = data.get('force_refresh', False)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Step 1: Fetch fresh Google Business data (if enabled)
|
|
||||||
fetch_result = None
|
|
||||||
if fetch_google:
|
|
||||||
logger.info(f"Fetching Google Business data for company {company.id}...")
|
|
||||||
fetch_result = gbp_fetch_google_data(db, company.id, force_refresh=force_refresh)
|
|
||||||
if not fetch_result.get('success') and not fetch_result.get('data', {}).get('cached'):
|
|
||||||
# Log warning but continue with audit
|
|
||||||
logger.warning(f"Google fetch warning for company {company.id}: {fetch_result.get('error')}")
|
|
||||||
|
|
||||||
# Step 2: Run the audit
|
|
||||||
result = gbp_audit_company(db, company.id, save=save_result)
|
|
||||||
|
|
||||||
# Build field status for response
|
|
||||||
fields_response = {}
|
|
||||||
for field_name, field_status in result.fields.items():
|
|
||||||
fields_response[field_name] = {
|
|
||||||
'status': field_status.status,
|
|
||||||
'value': str(field_status.value) if field_status.value is not None else None,
|
|
||||||
'score': field_status.score,
|
|
||||||
'max_score': field_status.max_score,
|
|
||||||
'recommendation': field_status.recommendation
|
|
||||||
}
|
|
||||||
|
|
||||||
# Determine score category
|
|
||||||
score = result.completeness_score
|
|
||||||
if score >= 90:
|
|
||||||
score_category = 'excellent'
|
|
||||||
elif score >= 70:
|
|
||||||
score_category = 'good'
|
|
||||||
elif score >= 50:
|
|
||||||
score_category = 'needs_work'
|
|
||||||
else:
|
|
||||||
score_category = 'poor'
|
|
||||||
|
|
||||||
response_data = {
|
|
||||||
'success': True,
|
|
||||||
'message': f'Audyt GBP dla firmy "{company.name}" został zakończony pomyślnie.',
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name,
|
|
||||||
'company_slug': company.slug,
|
|
||||||
'audit_version': GBP_AUDIT_VERSION,
|
|
||||||
'triggered_by': current_user.email,
|
|
||||||
'triggered_at': datetime.now().isoformat(),
|
|
||||||
'saved': save_result,
|
|
||||||
'audit': {
|
|
||||||
'completeness_score': result.completeness_score,
|
|
||||||
'score_category': score_category,
|
|
||||||
'fields_status': fields_response,
|
|
||||||
'recommendations': result.recommendations,
|
|
||||||
'photo_count': result.photo_count,
|
|
||||||
'logo_present': result.logo_present,
|
|
||||||
'cover_photo_present': result.cover_photo_present,
|
|
||||||
'review_count': result.review_count,
|
|
||||||
'average_rating': float(result.average_rating) if result.average_rating else None,
|
|
||||||
'google_place_id': result.google_place_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Include Google fetch results if performed
|
|
||||||
if fetch_result:
|
|
||||||
response_data['google_fetch'] = {
|
|
||||||
'success': fetch_result.get('success', False),
|
|
||||||
'steps': fetch_result.get('steps', []),
|
|
||||||
'data': fetch_result.get('data', {}),
|
|
||||||
'error': fetch_result.get('error')
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonify(response_data), 200
|
|
||||||
|
|
||||||
except ValueError as e:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': str(e),
|
|
||||||
'company_id': company.id if company else None
|
|
||||||
}), 400
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"GBP audit error for company {company.id}: {e}")
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Błąd podczas wykonywania audytu: {str(e)}',
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# SEO AUDIT USER-FACING DASHBOARD
|
# SEO AUDIT USER-FACING DASHBOARD
|
||||||
@ -1755,157 +1409,9 @@ def social_audit_dashboard(slug):
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/social/audit', methods=['POST'])
|
# ============================================================
|
||||||
@login_required
|
# SOCIAL AUDIT API - MOVED TO: blueprints/api/routes_social_audit.py
|
||||||
@limiter.limit("10 per hour")
|
# ============================================================
|
||||||
def api_social_audit_trigger():
|
|
||||||
"""
|
|
||||||
API: Trigger Social Media audit for a company.
|
|
||||||
|
|
||||||
This endpoint performs a comprehensive social media audit:
|
|
||||||
- Scans company website for social media links
|
|
||||||
- Searches for profiles via Brave Search API (if configured)
|
|
||||||
- Fetches Google Business Profile data
|
|
||||||
- Updates database with discovered profiles
|
|
||||||
|
|
||||||
Request JSON body:
|
|
||||||
- company_id: Company ID (integer) OR
|
|
||||||
- slug: Company slug (string)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
- Success: Updated social media audit results
|
|
||||||
- Error: Error message with status code
|
|
||||||
|
|
||||||
Rate limited to 10 requests per hour per user.
|
|
||||||
"""
|
|
||||||
# Import the SocialMediaAuditor from scripts
|
|
||||||
try:
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
scripts_dir = Path(__file__).parent / 'scripts'
|
|
||||||
if str(scripts_dir) not in sys.path:
|
|
||||||
sys.path.insert(0, str(scripts_dir))
|
|
||||||
from social_media_audit import SocialMediaAuditor
|
|
||||||
except ImportError as e:
|
|
||||||
logger.error(f"Failed to import SocialMediaAuditor: {e}")
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Usługa audytu Social Media jest niedostępna. Sprawdź konfigurację serwera.'
|
|
||||||
}), 503
|
|
||||||
|
|
||||||
# Parse request data
|
|
||||||
data = request.get_json()
|
|
||||||
if not data:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Brak danych w żądaniu. Podaj company_id lub slug.'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
company_id = data.get('company_id')
|
|
||||||
slug = data.get('slug')
|
|
||||||
|
|
||||||
if not company_id and not slug:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Podaj company_id lub slug firmy do audytu.'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
db = SessionLocal()
|
|
||||||
try:
|
|
||||||
# Find company by ID or slug
|
|
||||||
if company_id:
|
|
||||||
company = db.query(Company).filter_by(id=company_id, status='active').first()
|
|
||||||
else:
|
|
||||||
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
|
||||||
|
|
||||||
if not company:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Firma nie znaleziona lub nieaktywna.'
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
# Access control - admin can audit all, users only their company
|
|
||||||
if not current_user.is_admin:
|
|
||||||
if current_user.company_id != company.id:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Brak uprawnień do audytu social media tej firmy.'
|
|
||||||
}), 403
|
|
||||||
|
|
||||||
logger.info(f"Social Media audit triggered by {current_user.email} for company: {company.name} (ID: {company.id})")
|
|
||||||
|
|
||||||
# Prepare company dict for auditor
|
|
||||||
company_dict = {
|
|
||||||
'id': company.id,
|
|
||||||
'name': company.name,
|
|
||||||
'slug': company.slug,
|
|
||||||
'website': company.website,
|
|
||||||
'address_city': company.address_city or 'Wejherowo'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Initialize auditor and run audit
|
|
||||||
try:
|
|
||||||
auditor = SocialMediaAuditor()
|
|
||||||
audit_result = auditor.audit_company(company_dict)
|
|
||||||
|
|
||||||
# Check for errors
|
|
||||||
if audit_result.get('errors') and not audit_result.get('social_media') and not audit_result.get('website'):
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Audyt nie powiódł się: {", ".join(audit_result["errors"][:3])}',
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name
|
|
||||||
}), 422
|
|
||||||
|
|
||||||
# Save result to database
|
|
||||||
saved = auditor.save_audit_result(audit_result)
|
|
||||||
|
|
||||||
if not saved:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': 'Audyt został wykonany, ale nie udało się zapisać wyników do bazy danych.',
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
# Get count of social media profiles found
|
|
||||||
social_media_found = audit_result.get('social_media', {})
|
|
||||||
platforms_count = len(social_media_found)
|
|
||||||
|
|
||||||
# Calculate score
|
|
||||||
all_platforms = ['facebook', 'instagram', 'linkedin', 'youtube', 'twitter', 'tiktok']
|
|
||||||
score = int((platforms_count / len(all_platforms)) * 100)
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
'success': True,
|
|
||||||
'message': f'Audyt Social Media zakończony. Znaleziono {platforms_count} profili.',
|
|
||||||
'company_id': company.id,
|
|
||||||
'company_name': company.name,
|
|
||||||
'profiles_found': platforms_count,
|
|
||||||
'platforms': list(social_media_found.keys()),
|
|
||||||
'score': score,
|
|
||||||
'google_reviews': audit_result.get('google_reviews', {}),
|
|
||||||
'errors': audit_result.get('errors') if audit_result.get('errors') else None
|
|
||||||
}), 200
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Social Media audit error for company {company.id}: {e}")
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Błąd podczas audytu: {str(e)}'
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Social Media audit error for company {slug or company_id}: {e}")
|
|
||||||
db.rollback()
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'error': f'Błąd podczas audytu: {str(e)}'
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# GBP AUDIT USER-FACING DASHBOARD
|
# GBP AUDIT USER-FACING DASHBOARD
|
||||||
|
|||||||
@ -13,3 +13,5 @@ from . import routes_analytics # noqa: E402, F401
|
|||||||
from . import routes_recommendations # noqa: E402, F401
|
from . import routes_recommendations # noqa: E402, F401
|
||||||
from . import routes_contacts # noqa: E402, F401
|
from . import routes_contacts # noqa: E402, F401
|
||||||
from . import routes_seo_audit # noqa: E402, F401
|
from . import routes_seo_audit # noqa: E402, F401
|
||||||
|
from . import routes_gbp_audit # noqa: E402, F401
|
||||||
|
from . import routes_social_audit # noqa: E402, F401
|
||||||
|
|||||||
387
blueprints/api/routes_gbp_audit.py
Normal file
387
blueprints/api/routes_gbp_audit.py
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
"""
|
||||||
|
GBP Audit API Routes - API blueprint
|
||||||
|
|
||||||
|
Migrated from app.py as part of the blueprint refactoring.
|
||||||
|
Contains API routes for Google Business Profile audit functionality.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from flask import jsonify, request, current_app
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
|
||||||
|
from database import SessionLocal, Company
|
||||||
|
from . import bp
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Check if GBP audit service is available
|
||||||
|
try:
|
||||||
|
from gbp_audit_service import (
|
||||||
|
GBPAuditResult,
|
||||||
|
audit_company as gbp_audit_company,
|
||||||
|
get_company_audit as gbp_get_company_audit,
|
||||||
|
fetch_google_business_data as gbp_fetch_google_data
|
||||||
|
)
|
||||||
|
GBP_AUDIT_AVAILABLE = True
|
||||||
|
GBP_AUDIT_VERSION = '1.0'
|
||||||
|
except ImportError:
|
||||||
|
GBP_AUDIT_AVAILABLE = False
|
||||||
|
GBP_AUDIT_VERSION = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_limiter():
|
||||||
|
"""Get rate limiter from current app."""
|
||||||
|
return current_app.extensions.get('limiter')
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# GBP AUDIT API ROUTES
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@bp.route('/gbp/audit/health')
|
||||||
|
def api_gbp_audit_health():
|
||||||
|
"""
|
||||||
|
API: Health check for GBP audit service.
|
||||||
|
|
||||||
|
Returns service status and version information.
|
||||||
|
Used by monitoring systems to verify service availability.
|
||||||
|
"""
|
||||||
|
if GBP_AUDIT_AVAILABLE:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'ok',
|
||||||
|
'service': 'gbp_audit',
|
||||||
|
'version': GBP_AUDIT_VERSION,
|
||||||
|
'available': True
|
||||||
|
}), 200
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
'status': 'unavailable',
|
||||||
|
'service': 'gbp_audit',
|
||||||
|
'available': False,
|
||||||
|
'error': 'GBP audit service not loaded'
|
||||||
|
}), 503
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/gbp/audit', methods=['GET'])
|
||||||
|
def api_gbp_audit_get():
|
||||||
|
"""
|
||||||
|
API: Get GBP audit results for a company.
|
||||||
|
|
||||||
|
Query parameters:
|
||||||
|
- company_id: Company ID (integer) OR
|
||||||
|
- slug: Company slug (string)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- Latest audit results with completeness score and recommendations
|
||||||
|
- 404 if company not found
|
||||||
|
- 404 if no audit exists for the company
|
||||||
|
|
||||||
|
Example: GET /api/gbp/audit?company_id=26
|
||||||
|
Example: GET /api/gbp/audit?slug=pixlab-sp-z-o-o
|
||||||
|
"""
|
||||||
|
if not GBP_AUDIT_AVAILABLE:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Usługa audytu GBP jest niedostępna.'
|
||||||
|
}), 503
|
||||||
|
|
||||||
|
company_id = request.args.get('company_id', type=int)
|
||||||
|
slug = request.args.get('slug')
|
||||||
|
|
||||||
|
if not company_id and not slug:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Podaj company_id lub slug firmy.'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
# Find company
|
||||||
|
if company_id:
|
||||||
|
company = db.query(Company).filter_by(id=company_id, status='active').first()
|
||||||
|
else:
|
||||||
|
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
||||||
|
|
||||||
|
if not company:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Firma nie znaleziona lub nieaktywna.'
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
# Get latest audit
|
||||||
|
audit = gbp_get_company_audit(db, company.id)
|
||||||
|
|
||||||
|
if not audit:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Brak wyników audytu GBP dla firmy "{company.name}". Uruchom audyt używając POST /api/gbp/audit.',
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
# Build response
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name,
|
||||||
|
'company_slug': company.slug,
|
||||||
|
'audit': {
|
||||||
|
'id': audit.id,
|
||||||
|
'audit_date': audit.audit_date.isoformat() if audit.audit_date else None,
|
||||||
|
'completeness_score': audit.completeness_score,
|
||||||
|
'score_category': audit.score_category,
|
||||||
|
'fields_status': audit.fields_status,
|
||||||
|
'recommendations': audit.recommendations,
|
||||||
|
'has_name': audit.has_name,
|
||||||
|
'has_address': audit.has_address,
|
||||||
|
'has_phone': audit.has_phone,
|
||||||
|
'has_website': audit.has_website,
|
||||||
|
'has_hours': audit.has_hours,
|
||||||
|
'has_categories': audit.has_categories,
|
||||||
|
'has_photos': audit.has_photos,
|
||||||
|
'has_description': audit.has_description,
|
||||||
|
'has_services': audit.has_services,
|
||||||
|
'has_reviews': audit.has_reviews,
|
||||||
|
'photo_count': audit.photo_count,
|
||||||
|
'review_count': audit.review_count,
|
||||||
|
'average_rating': float(audit.average_rating) if audit.average_rating else None,
|
||||||
|
'google_place_id': audit.google_place_id,
|
||||||
|
'audit_source': audit.audit_source,
|
||||||
|
'audit_version': audit.audit_version
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error fetching GBP audit: {e}")
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Błąd podczas pobierania audytu: {str(e)}'
|
||||||
|
}), 500
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/gbp/audit/<slug>')
|
||||||
|
def api_gbp_audit_by_slug(slug):
|
||||||
|
"""
|
||||||
|
API: Get GBP audit results for a company by slug.
|
||||||
|
Convenience endpoint that uses slug from URL path.
|
||||||
|
|
||||||
|
Example: GET /api/gbp/audit/pixlab-sp-z-o-o
|
||||||
|
"""
|
||||||
|
if not GBP_AUDIT_AVAILABLE:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Usługa audytu GBP jest niedostępna.'
|
||||||
|
}), 503
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
||||||
|
|
||||||
|
if not company:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Firma o slug "{slug}" nie znaleziona.'
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
audit = gbp_get_company_audit(db, company.id)
|
||||||
|
|
||||||
|
if not audit:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Brak wyników audytu GBP dla firmy "{company.name}".',
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name,
|
||||||
|
'company_slug': company.slug,
|
||||||
|
'audit': {
|
||||||
|
'id': audit.id,
|
||||||
|
'audit_date': audit.audit_date.isoformat() if audit.audit_date else None,
|
||||||
|
'completeness_score': audit.completeness_score,
|
||||||
|
'score_category': audit.score_category,
|
||||||
|
'fields_status': audit.fields_status,
|
||||||
|
'recommendations': audit.recommendations,
|
||||||
|
'photo_count': audit.photo_count,
|
||||||
|
'review_count': audit.review_count,
|
||||||
|
'average_rating': float(audit.average_rating) if audit.average_rating else None
|
||||||
|
}
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/gbp/audit', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def api_gbp_audit_trigger():
|
||||||
|
"""
|
||||||
|
API: Run GBP audit for a company.
|
||||||
|
|
||||||
|
This endpoint runs a completeness audit for Google Business Profile data,
|
||||||
|
checking fields like name, address, phone, website, hours, categories,
|
||||||
|
photos, description, services, and reviews.
|
||||||
|
|
||||||
|
Request JSON body:
|
||||||
|
- company_id: Company ID (integer) OR
|
||||||
|
- slug: Company slug (string)
|
||||||
|
- save: Whether to save results to database (default: true)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- Success: Audit results with completeness score and recommendations
|
||||||
|
- Error: Error message with status code
|
||||||
|
|
||||||
|
Access:
|
||||||
|
- Members can audit their own company
|
||||||
|
- Admins can audit any company
|
||||||
|
|
||||||
|
Rate limited to 20 requests per hour per user.
|
||||||
|
"""
|
||||||
|
if not GBP_AUDIT_AVAILABLE:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Usługa audytu GBP jest niedostępna. Sprawdź konfigurację serwera.'
|
||||||
|
}), 503
|
||||||
|
|
||||||
|
# Parse request data
|
||||||
|
data = request.get_json()
|
||||||
|
if not data:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Brak danych w żądaniu. Podaj company_id lub slug.'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
company_id = data.get('company_id')
|
||||||
|
slug = data.get('slug')
|
||||||
|
save_result = data.get('save', True)
|
||||||
|
|
||||||
|
if not company_id and not slug:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Podaj company_id lub slug firmy do audytu.'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
# Find company by ID or slug
|
||||||
|
if company_id:
|
||||||
|
company = db.query(Company).filter_by(id=company_id, status='active').first()
|
||||||
|
else:
|
||||||
|
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
||||||
|
|
||||||
|
if not company:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Firma nie znaleziona lub nieaktywna.'
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
# Check access: admin can audit any company, member only their own
|
||||||
|
if not current_user.is_admin:
|
||||||
|
# Check if user is associated with this company
|
||||||
|
if current_user.company_id != company.id:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Brak uprawnień. Możesz audytować tylko własną firmę.'
|
||||||
|
}), 403
|
||||||
|
|
||||||
|
logger.info(f"GBP audit triggered by {current_user.email} for company: {company.name} (ID: {company.id})")
|
||||||
|
|
||||||
|
# Option to fetch fresh Google data before audit
|
||||||
|
fetch_google = data.get('fetch_google', True)
|
||||||
|
force_refresh = data.get('force_refresh', False)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Step 1: Fetch fresh Google Business data (if enabled)
|
||||||
|
fetch_result = None
|
||||||
|
if fetch_google:
|
||||||
|
logger.info(f"Fetching Google Business data for company {company.id}...")
|
||||||
|
fetch_result = gbp_fetch_google_data(db, company.id, force_refresh=force_refresh)
|
||||||
|
if not fetch_result.get('success') and not fetch_result.get('data', {}).get('cached'):
|
||||||
|
# Log warning but continue with audit
|
||||||
|
logger.warning(f"Google fetch warning for company {company.id}: {fetch_result.get('error')}")
|
||||||
|
|
||||||
|
# Step 2: Run the audit
|
||||||
|
result = gbp_audit_company(db, company.id, save=save_result)
|
||||||
|
|
||||||
|
# Build field status for response
|
||||||
|
fields_response = {}
|
||||||
|
for field_name, field_status in result.fields.items():
|
||||||
|
fields_response[field_name] = {
|
||||||
|
'status': field_status.status,
|
||||||
|
'value': str(field_status.value) if field_status.value is not None else None,
|
||||||
|
'score': field_status.score,
|
||||||
|
'max_score': field_status.max_score,
|
||||||
|
'recommendation': field_status.recommendation
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine score category
|
||||||
|
score = result.completeness_score
|
||||||
|
if score >= 90:
|
||||||
|
score_category = 'excellent'
|
||||||
|
elif score >= 70:
|
||||||
|
score_category = 'good'
|
||||||
|
elif score >= 50:
|
||||||
|
score_category = 'needs_work'
|
||||||
|
else:
|
||||||
|
score_category = 'poor'
|
||||||
|
|
||||||
|
response_data = {
|
||||||
|
'success': True,
|
||||||
|
'message': f'Audyt GBP dla firmy "{company.name}" został zakończony pomyślnie.',
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name,
|
||||||
|
'company_slug': company.slug,
|
||||||
|
'audit_version': GBP_AUDIT_VERSION,
|
||||||
|
'triggered_by': current_user.email,
|
||||||
|
'triggered_at': datetime.now().isoformat(),
|
||||||
|
'saved': save_result,
|
||||||
|
'audit': {
|
||||||
|
'completeness_score': result.completeness_score,
|
||||||
|
'score_category': score_category,
|
||||||
|
'fields_status': fields_response,
|
||||||
|
'recommendations': result.recommendations,
|
||||||
|
'photo_count': result.photo_count,
|
||||||
|
'logo_present': result.logo_present,
|
||||||
|
'cover_photo_present': result.cover_photo_present,
|
||||||
|
'review_count': result.review_count,
|
||||||
|
'average_rating': float(result.average_rating) if result.average_rating else None,
|
||||||
|
'google_place_id': result.google_place_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Include Google fetch results if performed
|
||||||
|
if fetch_result:
|
||||||
|
response_data['google_fetch'] = {
|
||||||
|
'success': fetch_result.get('success', False),
|
||||||
|
'steps': fetch_result.get('steps', []),
|
||||||
|
'data': fetch_result.get('data', {}),
|
||||||
|
'error': fetch_result.get('error')
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify(response_data), 200
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': str(e),
|
||||||
|
'company_id': company.id if company else None
|
||||||
|
}), 400
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"GBP audit error for company {company.id}: {e}")
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Błąd podczas wykonywania audytu: {str(e)}',
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
171
blueprints/api/routes_social_audit.py
Normal file
171
blueprints/api/routes_social_audit.py
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
"""
|
||||||
|
Social Audit API Routes - API blueprint
|
||||||
|
|
||||||
|
Migrated from app.py as part of the blueprint refactoring.
|
||||||
|
Contains API routes for Social Media audit functionality.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from flask import jsonify, request
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
|
||||||
|
from database import SessionLocal, Company
|
||||||
|
from . import bp
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# SOCIAL MEDIA AUDIT API ROUTES
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
@bp.route('/social/audit', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def api_social_audit_trigger():
|
||||||
|
"""
|
||||||
|
API: Trigger Social Media audit for a company.
|
||||||
|
|
||||||
|
This endpoint performs a comprehensive social media audit:
|
||||||
|
- Scans company website for social media links
|
||||||
|
- Searches for profiles via Brave Search API (if configured)
|
||||||
|
- Fetches Google Business Profile data
|
||||||
|
- Updates database with discovered profiles
|
||||||
|
|
||||||
|
Request JSON body:
|
||||||
|
- company_id: Company ID (integer) OR
|
||||||
|
- slug: Company slug (string)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- Success: Updated social media audit results
|
||||||
|
- Error: Error message with status code
|
||||||
|
|
||||||
|
Rate limited to 10 requests per hour per user.
|
||||||
|
"""
|
||||||
|
# Import the SocialMediaAuditor from scripts
|
||||||
|
try:
|
||||||
|
scripts_dir = Path(__file__).parent.parent.parent / 'scripts'
|
||||||
|
if str(scripts_dir) not in sys.path:
|
||||||
|
sys.path.insert(0, str(scripts_dir))
|
||||||
|
from social_media_audit import SocialMediaAuditor
|
||||||
|
except ImportError as e:
|
||||||
|
logger.error(f"Failed to import SocialMediaAuditor: {e}")
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Usługa audytu Social Media jest niedostępna. Sprawdź konfigurację serwera.'
|
||||||
|
}), 503
|
||||||
|
|
||||||
|
# Parse request data
|
||||||
|
data = request.get_json()
|
||||||
|
if not data:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Brak danych w żądaniu. Podaj company_id lub slug.'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
company_id = data.get('company_id')
|
||||||
|
slug = data.get('slug')
|
||||||
|
|
||||||
|
if not company_id and not slug:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Podaj company_id lub slug firmy do audytu.'
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
# Find company by ID or slug
|
||||||
|
if company_id:
|
||||||
|
company = db.query(Company).filter_by(id=company_id, status='active').first()
|
||||||
|
else:
|
||||||
|
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
||||||
|
|
||||||
|
if not company:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Firma nie znaleziona lub nieaktywna.'
|
||||||
|
}), 404
|
||||||
|
|
||||||
|
# Access control - admin can audit all, users only their company
|
||||||
|
if not current_user.is_admin:
|
||||||
|
if current_user.company_id != company.id:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Brak uprawnień do audytu social media tej firmy.'
|
||||||
|
}), 403
|
||||||
|
|
||||||
|
logger.info(f"Social Media audit triggered by {current_user.email} for company: {company.name} (ID: {company.id})")
|
||||||
|
|
||||||
|
# Prepare company dict for auditor
|
||||||
|
company_dict = {
|
||||||
|
'id': company.id,
|
||||||
|
'name': company.name,
|
||||||
|
'slug': company.slug,
|
||||||
|
'website': company.website,
|
||||||
|
'address_city': company.address_city or 'Wejherowo'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize auditor and run audit
|
||||||
|
try:
|
||||||
|
auditor = SocialMediaAuditor()
|
||||||
|
audit_result = auditor.audit_company(company_dict)
|
||||||
|
|
||||||
|
# Check for errors
|
||||||
|
if audit_result.get('errors') and not audit_result.get('social_media') and not audit_result.get('website'):
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Audyt nie powiódł się: {", ".join(audit_result["errors"][:3])}',
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name
|
||||||
|
}), 422
|
||||||
|
|
||||||
|
# Save result to database
|
||||||
|
saved = auditor.save_audit_result(audit_result)
|
||||||
|
|
||||||
|
if not saved:
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': 'Audyt został wykonany, ale nie udało się zapisać wyników do bazy danych.',
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
# Get count of social media profiles found
|
||||||
|
social_media_found = audit_result.get('social_media', {})
|
||||||
|
platforms_count = len(social_media_found)
|
||||||
|
|
||||||
|
# Calculate score
|
||||||
|
all_platforms = ['facebook', 'instagram', 'linkedin', 'youtube', 'twitter', 'tiktok']
|
||||||
|
score = int((platforms_count / len(all_platforms)) * 100)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'message': f'Audyt Social Media zakończony. Znaleziono {platforms_count} profili.',
|
||||||
|
'company_id': company.id,
|
||||||
|
'company_name': company.name,
|
||||||
|
'profiles_found': platforms_count,
|
||||||
|
'platforms': list(social_media_found.keys()),
|
||||||
|
'score': score,
|
||||||
|
'google_reviews': audit_result.get('google_reviews', {}),
|
||||||
|
'errors': audit_result.get('errors') if audit_result.get('errors') else None
|
||||||
|
}), 200
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Social Media audit error for company {company.id}: {e}")
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Błąd podczas audytu: {str(e)}'
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Social Media audit error for company {slug or company_id}: {e}")
|
||||||
|
db.rollback()
|
||||||
|
return jsonify({
|
||||||
|
'success': False,
|
||||||
|
'error': f'Błąd podczas audytu: {str(e)}'
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
Loading…
Reference in New Issue
Block a user