Skip to content

Commit 01be19b

Browse files
committed
feat(insights): ✨ add insights for business user
1 parent 813ee56 commit 01be19b

5 files changed

Lines changed: 180 additions & 27 deletions

File tree

pyfacebook/api/instagram_business/resource/user.py

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
from pyfacebook.api.base_resource import BaseResource
99
from pyfacebook.models.ig_business_models import (
1010
IgBusUser,
11-
IgBusMediaResponse,
12-
IgBusPublishLimit,
11+
IgBusDiscoveryUserResponse,
12+
IgBusDiscoveryUserMediaResponse,
13+
IgBusPublishLimitResponse,
14+
IgBusInsightsResponse,
1315
)
1416
from pyfacebook.utils.params_utils import enf_comma_separated
1517

@@ -50,16 +52,16 @@ def discovery_user(
5052
username: str,
5153
fields: Optional[Union[str, list, tuple]] = None,
5254
return_json: bool = False,
53-
) -> Union[IgBusUser, dict]:
55+
) -> Union[IgBusDiscoveryUserResponse, dict]:
5456
"""
5557
Get other business account info by username.
5658
5759
:param username: Username to get data.
5860
:param fields: Comma-separated id string for data fields which you want.
5961
You can also pass this with an id list, tuple.
60-
:param return_json: Set to false will return a dataclass for IgBusUser.
62+
:param return_json: Set to false will return a dataclass for IgBusDiscoveryUserResponse.
6163
Or return json data. Default is false.
62-
:return: IG Business User information.
64+
:return: Business discovery user response information.
6365
"""
6466

6567
if fields is None:
@@ -72,9 +74,9 @@ def discovery_user(
7274
fields=f"business_discovery.username({username}){{{metric}}}",
7375
)
7476
if return_json:
75-
return data["business_discovery"]
77+
return data
7678
else:
77-
return IgBusUser.new_from_json_dict(data=data["business_discovery"])
79+
return IgBusDiscoveryUserResponse.new_from_json_dict(data=data)
7880

7981
def discovery_user_medias(
8082
self,
@@ -84,7 +86,7 @@ def discovery_user_medias(
8486
after: Optional[str] = None,
8587
before: Optional[str] = None,
8688
return_json: bool = False,
87-
) -> Union[IgBusMediaResponse, dict]:
89+
) -> Union[IgBusDiscoveryUserMediaResponse, dict]:
8890
"""
8991
Get other business account's media by username.
9092
@@ -95,9 +97,9 @@ def discovery_user_medias(
9597
It should no more than 100. Default is None will use api default limit.
9698
:param after: The cursor that points to the end of the page of data that has been returned.
9799
:param before: The cursor that points to the start of the page of data that has been returned.
98-
:param return_json: Set to false will return a dataclass for IgBusMediaResponse.
100+
:param return_json: Set to false will return a dataclass for IgBusDiscoveryUserMediaResponse.
99101
Or return json data. Default is false.
100-
:return: Media response information.
102+
:return: Business discovery media response information.
101103
"""
102104

103105
if fields is None:
@@ -119,25 +121,24 @@ def discovery_user_medias(
119121
fields=f"business_discovery.username({username}){{media{after_str}{before_str}{limit_str}{{{metric}}}}}",
120122
)
121123

122-
media = data["business_discovery"]["media"]
123124
if return_json:
124-
return media
125+
return data
125126
else:
126-
return IgBusMediaResponse.new_from_json_dict(media)
127+
return IgBusDiscoveryUserMediaResponse.new_from_json_dict(data)
127128

128129
def get_content_publishing_limit(
129130
self,
130131
fields: Optional[Union[str, list, tuple]] = None,
131132
return_json: bool = False,
132-
) -> Union[IgBusPublishLimit, dict]:
133+
) -> Union[IgBusPublishLimitResponse, dict]:
133134
"""
134135
Get user's current content publishing usage.
135136
136137
:param fields: Comma-separated id string for data fields which you want.
137138
You can also pass this with an id list, tuple.
138-
:param return_json: Set to false will return a dataclass for PublishLimit.
139+
:param return_json: Set to false will return a dataclass for IgBusPublishLimitResponse.
139140
Or return json data. Default is false.
140-
:return: IG Business content publish limit information.
141+
:return: Business content publish limit response information.
141142
"""
142143

143144
if fields is None:
@@ -149,6 +150,55 @@ def get_content_publishing_limit(
149150
fields=enf_comma_separated(field="fields", value=fields),
150151
)
151152
if return_json:
152-
return data["data"][0]
153+
return data
154+
else:
155+
return IgBusPublishLimitResponse.new_from_json_dict(data)
156+
157+
def get_insights(
158+
self,
159+
metric: Union[str, list, tuple],
160+
period: str,
161+
since: Optional[str] = None,
162+
until: Optional[str] = None,
163+
user_id: Optional[str] = None,
164+
access_token: Optional[str] = None,
165+
return_json: bool = False,
166+
) -> Union[IgBusInsightsResponse, dict]:
167+
"""
168+
Get social interaction metrics on an IG User.
169+
170+
:param metric: Comma-separated id string for insights metrics which you want.
171+
You can also pass this with an id list, tuple.
172+
:param period: Period to aggregation data.
173+
Accepted parameters: lifetime,day,week,days_28
174+
:param since: Lower bound of the time range to fetch data. Need Unix timestamps.
175+
:param until: Upper bound of the time range to fetch data. Need Unix timestamps.
176+
Notice: time range may not more than 30 days.
177+
:param user_id: ID for business user to get insights.
178+
:param access_token: Access token with permissions for user_id.
179+
:param return_json: Set to false will return a dataclass for Insights.
180+
Or return json data. Default is false.
181+
:return: Business user insights response information.
182+
"""
183+
184+
if user_id is None:
185+
user_id = self.client.instagram_business_id
186+
187+
args = {
188+
"metric": enf_comma_separated(field="metric", value=metric),
189+
"period": period,
190+
"since": since,
191+
"until": until,
192+
}
193+
if access_token:
194+
args["access_token"] = access_token
195+
196+
data = self.client.get_connection(
197+
object_id=user_id,
198+
connection="insights",
199+
**args,
200+
)
201+
if return_json:
202+
return data
153203
else:
154-
return IgBusPublishLimit.new_from_json_dict(data["data"][0])
204+
return IgBusInsightsResponse.new_from_json_dict(data)

pyfacebook/models/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,9 @@
2121
IgBusHashtag,
2222
IgBusContainer,
2323
IgBusPublishLimit,
24+
IgBusPublishLimitResponse,
25+
IgBusInsight,
26+
IgBusInsightsResponse,
27+
IgBusDiscoveryUserResponse,
28+
IgBusDiscoveryUserMediaResponse,
2429
)

pyfacebook/models/ig_business_models.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
from dataclasses import dataclass
6-
from typing import List, Optional
6+
from typing import List, Optional, Union
77

88
from pyfacebook.models.base import BaseModel, field
99
from pyfacebook.models.extensions import Paging
@@ -180,3 +180,84 @@ class IgBusPublishLimit(BaseModel):
180180

181181
config: Optional[IgBusPublishLimitConfig] = field(repr=True)
182182
quota_usage: Optional[int] = field(repr=True)
183+
184+
185+
@dataclass
186+
class IgBusPublishLimitResponse(BaseModel):
187+
"""
188+
A class representing the content publish limit response.
189+
190+
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/content_publishing_limit
191+
"""
192+
193+
data: List[IgBusPublishLimit] = field(repr=True)
194+
195+
196+
@dataclass
197+
class IgBusInsightValue(BaseModel):
198+
"""
199+
A class representing the Instagram insight value info.
200+
201+
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights
202+
"""
203+
204+
value: Optional[Union[int, dict]] = field(repr=True)
205+
end_time: Optional[str] = field(repr=True)
206+
207+
208+
@dataclass
209+
class IgBusInsight(BaseModel):
210+
"""
211+
A class representing the Instagram insight.
212+
213+
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights
214+
"""
215+
216+
id: Optional[str] = field()
217+
name: Optional[str] = field(repr=True)
218+
period: Optional[str] = field(repr=True)
219+
title: Optional[str] = field()
220+
description: Optional[str] = field()
221+
values: Optional[List[IgBusInsightValue]] = field()
222+
223+
224+
@dataclass
225+
class IgBusInsightsResponse(BaseModel):
226+
"""
227+
A class representing the Instagram insights response.
228+
229+
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/insights
230+
"""
231+
232+
data: List[IgBusInsight] = field(repr=True)
233+
paging: Optional[Paging] = field()
234+
235+
236+
@dataclass
237+
class IgBusDiscoveryUserResponse(BaseModel):
238+
"""
239+
A class representing the response for discovery user.
240+
241+
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/business_discovery#sample-response
242+
"""
243+
244+
business_discovery: Optional[IgBusUser] = field(repr=True)
245+
id: Optional[str] = field(repr=True)
246+
247+
248+
@dataclass
249+
class IgBusDiscoveryUserMedia(BaseModel):
250+
media: Optional[IgBusMediaResponse] = field(repr=True)
251+
id: Optional[str] = field()
252+
253+
254+
@dataclass
255+
class IgBusDiscoveryUserMediaResponse(BaseModel):
256+
"""
257+
A class representing the response for discovery user.
258+
259+
Refer: https://developers.facebook.com/docs/instagram-api/reference/ig-user/business_discovery#sample-response
260+
"""
261+
262+
business_discovery: Optional[IgBusDiscoveryUserMedia] = field(repr=True)
263+
id: Optional[str] = field(repr=True)

tests/instagram_business/test_user.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ def test_discovery_user(helpers, api):
3636
)
3737

3838
user = api.user.discovery_user(username=username)
39-
assert user.id == "17841407673135339"
39+
assert user.business_discovery.id == "17841407673135339"
4040

4141
user_json = api.user.discovery_user(
4242
username=username,
4343
return_json=True,
4444
)
45-
assert user_json["id"] == "17841407673135339"
45+
assert user_json["business_discovery"]["id"] == "17841407673135339"
4646

4747

4848
def test_discovery_media(helpers, api):
@@ -66,10 +66,12 @@ def test_discovery_media(helpers, api):
6666

6767
media_p1 = api.user.discovery_user_medias(username=username, limit=2)
6868
media_p2 = api.user.discovery_user_medias(
69-
username=username, limit=2, after=media_p1.paging.cursors.after
69+
username=username,
70+
limit=2,
71+
after=media_p1.business_discovery.media.paging.cursors.after,
7072
)
71-
assert len(media_p1.data) == 2
72-
assert len(media_p2.data) == 2
73+
assert len(media_p1.business_discovery.media.data) == 2
74+
assert len(media_p2.business_discovery.media.data) == 2
7375

7476
with responses.RequestsMock() as m:
7577
m.add(
@@ -83,7 +85,7 @@ def test_discovery_media(helpers, api):
8385
media = api.user.discovery_user_medias(
8486
username=username, limit=2, before="before", return_json=True
8587
)
86-
assert len(media["data"]) == 2
88+
assert len(media["business_discovery"]["media"]["data"]) == 2
8789

8890

8991
def test_get_content_publishing_limit(helpers, api):
@@ -97,7 +99,7 @@ def test_get_content_publishing_limit(helpers, api):
9799
)
98100

99101
limit = api.user.get_content_publishing_limit()
100-
assert limit.config.quota_total == 25
102+
assert limit.data[0].config.quota_total == 25
101103

102104
limit_json = api.user.get_content_publishing_limit(return_json=True)
103-
assert limit_json["quota_usage"] == 0
105+
assert limit_json["data"][0]["quota_usage"] == 0

tests/test_models.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,18 @@ def test_ig_bus_container(helpers):
166166
container = md.IgBusContainer.new_from_json_dict(data)
167167
assert container.id == "17889615691921648"
168168
assert container.status_code == "FINISHED"
169+
170+
171+
def test_ig_bus_publish_limit(helpers):
172+
data = helpers.load_json("testdata/instagram/models/ig_publish_limit.json")
173+
174+
limit = md.IgBusPublishLimit.new_from_json_dict(data)
175+
assert limit.config.quota_total == 25
176+
177+
178+
def test_ig_bus_insight(helpers):
179+
data = helpers.load_json("testdata/instagram/models/ig_insight.json")
180+
181+
insight = md.IgBusInsight.new_from_json_dict(data)
182+
assert insight.name == "impressions"
183+
assert insight.values[0].value == 32

0 commit comments

Comments
 (0)