Skip to content

Commit

Permalink
Merge pull request #247 from MerleLiuKun/feat-v18
Browse files Browse the repository at this point in the history
Feat v18
  • Loading branch information
MerleLiuKun authored Nov 10, 2023
2 parents 81861d8 + fb41775 commit 40f329e
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 244 deletions.
516 changes: 279 additions & 237 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions pyfacebook/api/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@

class GraphAPI:
VALID_API_VERSIONS = [
"v10.0",
"v11.0",
"v12.0",
"v13.0",
"v14.0",
"v15.0",
"v16.0",
"v17.0",
"v18.0",
]
GRAPH_URL = "https://graph.facebook.com/"
AUTHORIZATION_URL = "https://www.facebook.com/dialog/oauth"
Expand Down
14 changes: 10 additions & 4 deletions pyfacebook/api/instagram_business/resource/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,22 +147,28 @@ def get_insights(
self,
media_id: str,
metric: Union[str, list, tuple],
breakdown: Optional[Union[str, list, tuple]] = None,
return_json: bool = False,
) -> Union[IgBusInsightsResponse, dict]:
"""
Get insights data on a media
Get insights data on a media.
Notice: Different type media may have different metrics.
:param media_id: ID for the media.
:param metric: A comma-separated list of Metrics you want returned.
You can also pass this with an id list, tuple.
:param breakdown: If specified, the results will be broken down into smaller sets based on this.
:param return_json: Set to false will return a dataclass for IgBusInsightsResponse.
Or return json data. Default is false.
:return: Media insights response information.
"""
args = {"metric": enf_comma_separated(field="metric", value=metric)}
if breakdown:
args["breakdown"] = enf_comma_separated(field="breakdown", value=breakdown)

data = self.client.get_connection(
object_id=media_id,
connection="insights",
metric=enf_comma_separated(field="metric", value=metric),
object_id=media_id, connection="insights", **args
)

if return_json:
Expand Down
15 changes: 15 additions & 0 deletions pyfacebook/api/instagram_business/resource/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ def get_insights(
self,
metric: Union[str, list, tuple],
period: str,
breakdown: Optional[Union[str, list, tuple]] = None,
metric_type: Optional[str] = None,
timeframe: Optional[str] = None,
since: Optional[str] = None,
until: Optional[str] = None,
user_id: Optional[str] = None,
Expand All @@ -181,6 +184,12 @@ def get_insights(
You can also pass this with an id list, tuple.
:param period: Period to aggregation data.
Accepted parameters: lifetime,day,week,days_28
:param breakdown: If specified, the results will be broken down into smaller sets based on this.
Accepted parameters: contact_button_type, follow_type, media_product_type
:param metric_type: Designate how you want results aggregated.
Accepted parameters: time_series, total_value
:param timeframe: Tells the API how far to look back for data when requesting demographic-related metrics.
This value overrides the `since` and `until` parameters.
:param since: Lower bound of the time range to fetch data. Need Unix timestamps.
:param until: Upper bound of the time range to fetch data. Need Unix timestamps.
Notice: time range may not more than 30 days.
Expand All @@ -200,6 +209,12 @@ def get_insights(
"since": since,
"until": until,
}
if breakdown:
args["breakdown"] = enf_comma_separated(field="breakdown", value=breakdown)
if metric_type:
args["metric_type"] = metric_type
if timeframe:
args["timeframe"] = timeframe
if access_token:
args["access_token"] = access_token

Expand Down
39 changes: 38 additions & 1 deletion pyfacebook/models/ig_business_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class IgBusUser(BaseModel):
followers_count: Optional[int] = field()
follows_count: Optional[int] = field()
media_count: Optional[int] = field()
name: Optional[int] = field()
name: Optional[str] = field()
profile_picture_url: Optional[str] = field()
shopping_product_tag_eligibility: Optional[bool] = field()
username: Optional[str] = field(repr=True)
Expand Down Expand Up @@ -223,6 +223,42 @@ class IgBusInsightValue(BaseModel):
end_time: Optional[str] = field(repr=True)


@dataclass
class IgBusInsightTotalValueBreakdownResult(BaseModel):
"""
A class representing the Instagram insight total value breakdown result info.
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights#--------
"""

dimension_values: Optional[List[str]] = field(repr=True)
value: Optional[int] = field(repr=True)


@dataclass
class IgBusInsightTotalValueBreakdown(BaseModel):
"""
A class representing the Instagram insight total value breakdown info.
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights#--------
"""

dimension_keys: Optional[List[str]] = field(repr=True)
results: Optional[List[IgBusInsightTotalValueBreakdownResult]] = field()


@dataclass
class IgBusInsightTotalValue(BaseModel):
"""
A class representing the Instagram insight total value info.
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights#--------
"""

value: Optional[int] = field(repr=True)
breakdowns: Optional[List[IgBusInsightTotalValueBreakdown]] = field()


@dataclass
class IgBusInsight(BaseModel):
"""
Expand All @@ -237,6 +273,7 @@ class IgBusInsight(BaseModel):
title: Optional[str] = field()
description: Optional[str] = field()
values: Optional[List[IgBusInsightValue]] = field()
total_value: Optional[IgBusInsightTotalValue] = field()


@dataclass
Expand Down
1 change: 1 addition & 0 deletions testdata/instagram/apidata/medias/insights_new.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"data":[{"name":"profile_activity","period":"lifetime","values":[{"value":11}],"title":"Profile activity","description":"[IG Insights] This header is the name of a metric that appears on an educational info sheet for a particular for a particular post, story, video or promotion. This metric is the sum of all profile actions people take when they engage with this content.","total_value":{"value":11,"breakdowns":[{"dimension_keys":["action_type"],"results":[{"dimension_values":["bio_link_clicked"],"value":11}]}]},"id":"18035507131611621/insights/profile_activity/lifetime"}]}
1 change: 1 addition & 0 deletions testdata/instagram/apidata/users/user_insights_new.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"data":[{"name":"reach","period":"day","title":"Accounts reached","description":"The number of unique accounts that have seen your content, at least once, including in ads. Content includes posts, stories, reels, videos and live videos. Reach is different from impressions, which may include multiple views of your content by the same accounts. This metric is estimated and in development.","total_value":{"value":14516883,"breakdowns":[{"dimension_keys":["media_product_type","follow_type"],"results":[{"dimension_values":["IGTV","FOLLOWER"],"value":9},{"dimension_values":["CAROUSEL_CONTAINER","NON_FOLLOWER"],"value":109},{"dimension_values":["AD","FOLLOWER"],"value":67608},{"dimension_values":["AD","NON_FOLLOWER"],"value":14357147},{"dimension_values":["POST","FOLLOWER"],"value":78567},{"dimension_values":["POST","NON_FOLLOWER"],"value":26514},{"dimension_values":["CAROUSEL_CONTAINER","FOLLOWER"],"value":191},{"dimension_values":["STORY","FOLLOWER"],"value":31274},{"dimension_values":["REEL","FOLLOWER"],"value":58345},{"dimension_values":["REEL","NON_FOLLOWER"],"value":23079},{"dimension_values":["IGTV","NON_FOLLOWER"],"value":39},{"dimension_values":["STORY","NON_FOLLOWER"],"value":3279},{"dimension_values":["AR_EFFECT_PREVIEW","FOLLOWER"],"value":6},{"dimension_values":["AR_EFFECT_PREVIEW","NON_FOLLOWER"],"value":7}]}]},"id":"17841454149711910/insights/reach/day"}],"paging":{"previous":"https://graph.facebook.com/v18.0/17841454149711910/insights?access_token=access_token&metric=reach&metric_type=total_value&period=day&breakdown=media_product_type%2Cfollow_type&timeframe=last_14_days&since=1699257550&until=1699430350","next":"https://graph.facebook.com/v18.0/17841454149711910/insights?access_token=access_token&metric=reach&metric_type=total_value&period=day&breakdown=media_product_type%2Cfollow_type&timeframe=last_14_days&since=1699603152&until=1699775952"}}
14 changes: 14 additions & 0 deletions tests/instagram_business/test_media.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ def test_get_insights(helpers, api):
)
assert insights_json["data"][0]["name"] == "impressions"

with responses.RequestsMock() as m:
m.add(
method=responses.GET,
url=f"https://graph.facebook.com/{api.version}/{media_id}/insights",
json=helpers.load_json(
"testdata/instagram/apidata/medias/insights_new.json"
),
)

insights = api.media.get_insights(
media_id=media_id, metric="profile_activity", breakdown=["action_type"]
)
assert insights.data[0].total_value.breakdowns[0].results[0].value == 11


def test_get_product_tags(helpers, api):
media_id = "90010778325754"
Expand Down
18 changes: 18 additions & 0 deletions tests/instagram_business/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ def test_get_user_insights(helpers, api):
)
assert len(insights_json["data"]) == 3

with responses.RequestsMock() as m:
m.add(
method=responses.GET,
url=f"https://graph.facebook.com/{api.version}/{api.instagram_business_id}/insights",
json=helpers.load_json(
"testdata/instagram/apidata/users/user_insights_new.json"
),
)
insights = api.user.get_insights(
metric="reach",
period="day",
metric_type="total_value",
timeframe="last_14_days",
breakdown=["media_product_type", "follow_type"],
access_token=api.access_token,
)
assert insights.data[0].total_value.value == 14516883


def test_get_user_media(helpers, api):
with responses.RequestsMock() as m:
Expand Down

0 comments on commit 40f329e

Please sign in to comment.