fix: enrichment now detects OAuth config and syncs via Graph API
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

Instead of checking profile.source (which was never 'facebook_api'),
now checks SocialMediaConfig for companies with OAuth page_id.
Added progress indicator with spinner and percentage during scan.
After API sync, page auto-refreshes to show new data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-12 12:48:59 +01:00
parent 60dd646989
commit f4d5d17d75
2 changed files with 76 additions and 25 deletions

View File

@ -884,6 +884,50 @@ def _run_enrichment_background(company_ids):
'has_changes': False,
}
# Check if company has Facebook OAuth config — sync via Graph API
fb_config = db.query(SocialMediaConfig).filter(
SocialMediaConfig.company_id == company_id,
SocialMediaConfig.platform == 'facebook',
SocialMediaConfig.page_id.isnot(None)
).first()
fb_synced = False
if fb_config:
try:
from facebook_graph_service import sync_facebook_to_social_media
sync_result = sync_facebook_to_social_media(db, company_id)
if sync_result.get('success'):
data = sync_result.get('data', {})
fb_synced = True
# Add a virtual profile result for the API sync
company_result['profiles'].append({
'profile_id': None,
'platform': 'facebook',
'url': f"Facebook Page: {fb_config.page_name or fb_config.page_id}",
'source': 'facebook_api',
'status': 'synced_api',
'reason': f"Graph API: {data.get('followers_count', 0)} obserwujących, engagement: {data.get('engagement_rate', 0):.1f}%",
})
company_result['has_changes'] = True
else:
company_result['profiles'].append({
'profile_id': None,
'platform': 'facebook',
'url': f"Facebook Page: {fb_config.page_name or fb_config.page_id}",
'source': 'facebook_api',
'status': 'error',
'reason': f"Graph API: {sync_result.get('message', sync_result.get('error', 'nieznany błąd'))}",
})
except Exception as e:
logger.warning(f"Facebook API sync failed for {company.name}: {e}")
company_result['profiles'].append({
'profile_id': None,
'platform': 'facebook',
'url': f"Facebook Page: {fb_config.page_name or fb_config.page_id}",
'source': 'facebook_api',
'status': 'error',
'reason': f"Graph API: {str(e)[:100]}",
})
for profile in profiles:
profile_result = {
'profile_id': profile.id,
@ -892,23 +936,10 @@ def _run_enrichment_background(company_ids):
'source': profile.source,
}
if profile.source in ('facebook_api',):
# Sync via Graph API instead of scraping
try:
from facebook_graph_service import sync_facebook_to_social_media
sync_result = sync_facebook_to_social_media(db, company_id)
if sync_result.get('success'):
data = sync_result.get('data', {})
profile_result['status'] = 'synced_api'
profile_result['reason'] = f"Zsynchronizowano przez Graph API ({data.get('followers_count', 0)} obserwujących, engagement: {data.get('engagement_rate', 0):.1f}%)"
company_result['has_changes'] = True
else:
profile_result['status'] = 'error'
profile_result['reason'] = f"Graph API: {sync_result.get('error', 'nieznany błąd')}"
except Exception as e:
logger.warning(f"Facebook API sync failed for {company.name}: {e}")
profile_result['status'] = 'error'
profile_result['reason'] = f"Graph API sync error: {str(e)[:100]}"
# Skip scraping Facebook if we already synced via API
if fb_synced and profile.platform.lower() == 'facebook':
profile_result['status'] = 'no_changes'
profile_result['reason'] = 'Zsynchronizowano przez Graph API'
company_result['profiles'].append(profile_result)
continue
@ -1092,7 +1123,7 @@ def admin_social_audit_enrichment_status():
profiles_summary = []
for p in r.get('profiles', []):
status = p.get('status', 'unknown')
icon = {'changes': '+', 'no_changes': '=', 'skipped': '~', 'error': '!', 'no_data': '-'}.get(status, '?')
icon = {'changes': '+', 'no_changes': '=', 'skipped': '~', 'error': '!', 'no_data': '-', 'synced_api': ''}.get(status, '?')
platform = p.get('platform', '?')
change_count = len(p.get('changes', []))
desc = ''
@ -1104,6 +1135,8 @@ def admin_social_audit_enrichment_status():
desc = p.get('reason', 'błąd')[:40]
elif status == 'no_data':
desc = 'brak dostępu'
elif status == 'synced_api':
desc = p.get('reason', 'zsynchronizowano')[:40]
elif status == 'no_changes':
desc = 'aktualne'
profiles_summary.append({
@ -1120,6 +1153,7 @@ def admin_social_audit_enrichment_status():
})
last_run = state.get('last_run')
api_synced = sum(1 for r in results for p in r.get('profiles', []) if p.get('status') == 'synced_api')
return jsonify({
'running': state.get('running', False),
@ -1129,6 +1163,7 @@ def admin_social_audit_enrichment_status():
'errors': state.get('errors', 0),
'last_run': last_run,
'pending_count': len(pending),
'api_synced_count': api_synced,
'approved': state.get('approved', False),
'feed': feed,
'results_count': len(results),

View File

@ -4,6 +4,17 @@
{% block extra_css %}
<style>
.enrichment-spinner {
display: inline-block;
width: 12px; height: 12px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
vertical-align: middle;
margin-right: 4px;
}
@keyframes spin { to { transform: rotate(360deg); } }
.audit-detail {
max-width: 1000px;
margin: 0 auto;
@ -952,7 +963,7 @@
function runSingleEnrichment(companyId) {
var btn = document.getElementById('enrichSingleBtn');
btn.disabled = true;
btn.textContent = 'Audytowanie...';
btn.innerHTML = '<span class="enrichment-spinner"></span> Audytowanie...';
fetch('{{ url_for("admin.admin_social_audit_run_enrichment") }}', {
method: 'POST',
@ -964,28 +975,33 @@ function runSingleEnrichment(companyId) {
if (data.status === 'started') {
pollSingleEnrichment();
} else {
alert(data.error || 'Błąd');
btn.disabled = false;
btn.textContent = 'Uruchom audyt';
btn.textContent = data.error || 'Błąd';
setTimeout(function() { btn.textContent = 'Uruchom audyt'; }, 3000);
}
})
.catch(function(e) {
alert('Błąd: ' + e.message);
btn.disabled = false;
btn.textContent = 'Uruchom audyt';
btn.textContent = 'Błąd: ' + e.message;
setTimeout(function() { btn.textContent = 'Uruchom audyt'; }, 3000);
});
}
function pollSingleEnrichment() {
var btn = document.getElementById('enrichSingleBtn');
fetch('{{ url_for("admin.admin_social_audit_enrichment_status") }}')
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.running) {
setTimeout(pollSingleEnrichment, 2000);
var pct = data.progress || 0;
btn.innerHTML = '<span class="enrichment-spinner"></span> ' + pct + '% (' + data.completed + '/' + data.total + ')';
setTimeout(pollSingleEnrichment, 1500);
} else if (data.pending_count > 0) {
window.location.href = '{{ url_for("admin.admin_social_audit_enrichment_review") }}';
} else if (data.api_synced_count > 0) {
btn.textContent = 'Zsynchronizowano! Odświeżam...';
setTimeout(function() { window.location.reload(); }, 1000);
} else {
var btn = document.getElementById('enrichSingleBtn');
btn.disabled = false;
btn.textContent = 'Brak nowych danych';
setTimeout(function() { btn.textContent = 'Uruchom audyt'; }, 5000);