Skip to content

Commit

Permalink
feat: remove dependency on deprecated facebook analytics package (#337)
Browse files Browse the repository at this point in the history
* feat: move IG analytics to Graph API calls

* feat: rm dependency on the old FB client

* fix: make IG report work without warnings
  • Loading branch information
alexeyqu authored Dec 2, 2024
1 parent bf60dd8 commit 153d5de
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 31 deletions.
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ deepdiff==4.3.2
Deprecated==1.2.14
distlib==0.3.6
distro==1.4.0
-e git+https://github.com/mobolic/facebook-sdk.git@ffd9980700be48964d6a6a61144edb1c3ea29cff#egg=facebook_sdk
filelock==3.9.0
fonttools==4.38.0
freezegun==0.3.15
Expand Down
6 changes: 3 additions & 3 deletions src/analytics/api_instagram_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ def get_total_subscribers_count(self) -> int:

def get_new_subscribers_count(self, since: datetime, until: datetime) -> int:
new_subscribers = self._ig_client.get_new_subscribers(since, until)
return sum(map(lambda day: day["value"], new_subscribers["data"][0]["values"]))
return sum(map(lambda day: day[1], new_subscribers))

def get_reach(self, since: datetime, until: datetime) -> int:
new_subscribers = self._ig_client.get_reach(since, until)
return sum(map(lambda day: day["value"], new_subscribers["data"][0]["values"]))
reach = self._ig_client.get_reach(since, until)
return sum(map(lambda day: day[1], reach))

def get_interactions_count(self, since: datetime, until: datetime) -> int:
likes_count = self._ig_client.get_likes_count(since, until)
Expand Down
2 changes: 0 additions & 2 deletions src/facebook/facebook_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from urllib.parse import parse_qs, urlparse

import dateutil.parser as dateparser
import facebook
import requests

from ..consts import ReportPeriod
Expand All @@ -32,7 +31,6 @@ def update_config(self, new_facebook_config: dict):
self._update_from_config()

def _update_from_config(self):
self._api_client = facebook.GraphAPI(self._facebook_config["token"], 7.0)
self._page_id = self._facebook_config["page_id"]

def _make_graph_api_call(self, uri: str, params: dict) -> dict:
Expand Down
70 changes: 45 additions & 25 deletions src/instagram/instagram_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
from urllib.parse import parse_qs, urlparse

import dateutil.parser as dateparser
import facebook
import requests

from ..consts import ReportPeriod
from ..utils.singleton import Singleton
from .instagram_objects import InstagramMedia, InstagramPage

logger = logging.getLogger(__name__)

BASE_URL = "https://graph.facebook.com"
API_VERSION = "v19.0"


class InstagramClient(Singleton):
def __init__(self, facebook_config=None):
Expand All @@ -29,23 +32,32 @@ def update_config(self, new_facebook_config: dict):
self._update_from_config()

def _update_from_config(self):
self._api_client = facebook.GraphAPI(self._facebook_config["token"], 10.0)
self._page_id = self._facebook_config.get("ig_page_id")

def _make_graph_api_call(self, uri: str, params: dict) -> dict:
params["access_token"] = self._facebook_config["token"]
response = requests.get(
"/".join([BASE_URL, API_VERSION, uri])
+ "?"
+ "&".join(f"{key}={value}" for key, value in params.items())
)
return response.json()

def get_page(self) -> InstagramPage:
"""
Get the Instagram page
"""
page_dict = self._api_client.get_object(self._page_id, fields="username,name")
page_dict = self._make_graph_api_call(
str(self._page_id), {"fields": "username, name"}
)
return InstagramPage.from_dict(page_dict)

def _get_all_posts(self) -> List[InstagramMedia]:
"""
Get all media (posts, stories, IGTV etc) on the page
"""
media_dicts = self._api_client.get_all_connections(
self._page_id,
"media",
media_dicts = self._get_all_batches(
connection_name="media",
fields=(
"id,ig_id,media_url,timestamp,media_type,like_count,comments_count"
),
Expand Down Expand Up @@ -73,36 +85,35 @@ def get_total_subscribers(self) -> int:
"""
Get the total number of subscribers.
"""
return self._api_client.get_object(self._page_id, fields="followers_count")[
"followers_count"
]
return self._make_graph_api_call(
str(self._page_id), {"fields": "followers_count"}
)["followers_count"]

def get_new_subscribers(self, since: datetime, until: datetime) -> dict:
"""
Get the number of new subscribers for the period.
"""
new_followers = self._api_client.get_connections(
self._page_id,
"insights",
batches = self._get_all_batches(
connection_name="insights",
since=since,
until=until,
metric="follower_count",
period="day",
)
return new_followers
return self._get_values_from_batches(batches, since, until)

def get_reach(self, since: datetime, until: datetime) -> dict:
"""
Get the total reach for the period.
"""
return self._api_client.get_connections(
self._page_id,
"insights",
batches = self._get_all_batches(
connection_name="insights",
since=since,
until=until,
metric="reach",
period="day",
)
return self._get_values_from_batches(batches, since, until)

def get_likes_count(self, since: datetime, until: datetime) -> int:
"""
Expand Down Expand Up @@ -148,17 +159,22 @@ def _get_post_insights(self, post_id: str) -> dict:
Get all insights for the post.
https://developers.facebook.com/docs/instagram-api/reference/ig-media/insights
"""
return self._api_client.get_connections(
post_id, "insights", metric="engagement,impressions,reach,saved"
return self._get_all_batches(
connection_name="insights",
metric="engagement,impressions,reach,saved",
)

def _get_all_batches(
self, connection_name: str, since: datetime, until: datetime, **args
self, connection_name: str, since: datetime = None, until: datetime = None, **kwargs
) -> List[dict]:
result = []
args["since"] = since
args["until"] = until
page = self._api_client.get_connections(self._page_id, connection_name, **args)
params = {}
if since:
params["since"] = int(datetime.timestamp(since))
if until:
params["until"] = int(datetime.timestamp(until))
params.update(kwargs)
page = self._make_graph_api_call(f"{self._page_id}/{connection_name}", params)
result += page["data"]
# process next
result += self._iterate_over_pages(connection_name, since, until, page, True)
Expand Down Expand Up @@ -201,10 +217,14 @@ def _iterate_over_pages(
):
break
args.pop("access_token", None)
current_page = self._api_client.get_connections(
self._page_id, connection_name, **args
current_page = self._make_graph_api_call(
f"{self._page_id}/{connection_name}", args
)
result += current_page["data"]
if "data" in current_page:
result += current_page["data"]
else:
logger.error(f"Error in pagination: {current_page}")
break
return result

@staticmethod
Expand Down

0 comments on commit 153d5de

Please sign in to comment.