feat: add cursor-based pagination to Facebook posts 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
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
Previously get_page_posts returned a flat list with no pagination support. Now returns dict with posts and next_cursor, enabling infinite scrolling through all Facebook page posts via the after query parameter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9444c3484e
commit
779f0b0b73
@ -388,7 +388,8 @@ def social_publisher_fb_posts(company_id):
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
result = social_publisher.get_page_recent_posts(company_id)
|
||||
after = request.args.get('after')
|
||||
result = social_publisher.get_page_recent_posts(company_id, after=after)
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
|
||||
@ -273,22 +273,28 @@ class FacebookGraphService:
|
||||
'reactions_total': result.get('reactions', {}).get('summary', {}).get('total_count', 0),
|
||||
}
|
||||
|
||||
def get_page_posts(self, page_id: str, limit: int = 10) -> Optional[List[Dict]]:
|
||||
def get_page_posts(self, page_id: str, limit: int = 10,
|
||||
after: str = None) -> Optional[Dict]:
|
||||
"""Get recent posts from a Facebook Page with engagement metrics.
|
||||
|
||||
Args:
|
||||
page_id: Facebook Page ID
|
||||
limit: Number of posts to fetch (max 100)
|
||||
after: Pagination cursor for next page
|
||||
|
||||
Returns:
|
||||
List of post dicts or None on failure
|
||||
Dict with 'posts' list and 'next_cursor' (or None), or None on failure
|
||||
"""
|
||||
fields = (
|
||||
'id,message,created_time,full_picture,permalink_url,status_type,'
|
||||
'likes.summary(true).limit(0),comments.summary(true).limit(0),'
|
||||
'shares,reactions.summary(true).limit(0)'
|
||||
)
|
||||
result = self._get(f'{page_id}/posts', {'fields': fields, 'limit': limit})
|
||||
params = {'fields': fields, 'limit': limit}
|
||||
if after:
|
||||
params['after'] = after
|
||||
|
||||
result = self._get(f'{page_id}/posts', params)
|
||||
if not result:
|
||||
return None
|
||||
|
||||
@ -306,7 +312,14 @@ class FacebookGraphService:
|
||||
'shares': item.get('shares', {}).get('count', 0) if item.get('shares') else 0,
|
||||
'reactions_total': item.get('reactions', {}).get('summary', {}).get('total_count', 0),
|
||||
})
|
||||
return posts
|
||||
|
||||
# Extract next page cursor
|
||||
next_cursor = None
|
||||
paging = result.get('paging', {})
|
||||
if paging.get('next'):
|
||||
next_cursor = paging.get('cursors', {}).get('after')
|
||||
|
||||
return {'posts': posts, 'next_cursor': next_cursor}
|
||||
|
||||
def get_post_insights_metrics(self, post_id: str) -> Optional[Dict]:
|
||||
"""Get detailed insights metrics for a specific post.
|
||||
|
||||
@ -603,10 +603,11 @@ class SocialPublisherService:
|
||||
|
||||
# ---- Facebook Page Posts (read from API) ----
|
||||
|
||||
def get_page_recent_posts(self, company_id: int, limit: int = 10) -> Dict:
|
||||
def get_page_recent_posts(self, company_id: int, limit: int = 10,
|
||||
after: str = None) -> Dict:
|
||||
"""Fetch recent posts from company's Facebook page with engagement metrics.
|
||||
|
||||
Uses in-memory cache with 5-minute TTL.
|
||||
Uses in-memory cache with 5-minute TTL (first page only).
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
@ -615,31 +616,41 @@ class SocialPublisherService:
|
||||
return {'success': False, 'error': 'Brak konfiguracji Facebook dla tej firmy.'}
|
||||
|
||||
page_id = config.page_id
|
||||
cache_key = (company_id, page_id)
|
||||
|
||||
# Check cache
|
||||
# Cache only first page (no cursor)
|
||||
if not after:
|
||||
cache_key = (company_id, page_id)
|
||||
cached = _posts_cache.get(cache_key)
|
||||
if cached and (time.time() - cached['ts']) < _CACHE_TTL:
|
||||
return {
|
||||
'success': True,
|
||||
'posts': cached['data'],
|
||||
'next_cursor': cached.get('next_cursor'),
|
||||
'page_name': config.page_name or '',
|
||||
'cached': True,
|
||||
}
|
||||
|
||||
from facebook_graph_service import FacebookGraphService
|
||||
fb = FacebookGraphService(access_token)
|
||||
posts = fb.get_page_posts(page_id, limit)
|
||||
result = fb.get_page_posts(page_id, limit, after=after)
|
||||
|
||||
if posts is None:
|
||||
if result is None:
|
||||
return {'success': False, 'error': 'Nie udało się pobrać postów z Facebook API.'}
|
||||
|
||||
# Update cache
|
||||
_posts_cache[cache_key] = {'data': posts, 'ts': time.time()}
|
||||
posts = result['posts']
|
||||
next_cursor = result.get('next_cursor')
|
||||
|
||||
# Update cache for first page only
|
||||
if not after:
|
||||
cache_key = (company_id, page_id)
|
||||
_posts_cache[cache_key] = {
|
||||
'data': posts, 'next_cursor': next_cursor, 'ts': time.time()
|
||||
}
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'posts': posts,
|
||||
'next_cursor': next_cursor,
|
||||
'page_name': config.page_name or '',
|
||||
'cached': False,
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user