Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -14,20 +14,16 @@ import os
|
|
14 |
from openai import AzureOpenAI
|
15 |
import base64
|
16 |
|
17 |
-
|
18 |
-
# st.page_link("report.py", label="Home", icon="🏠")
|
19 |
-
# st.page_link("pages/page_1.py", label="Page 1", icon="1️⃣")
|
20 |
-
# st.page_link("pages/page_2.py", label="Page 2", icon="2️⃣", disabled=True)
|
21 |
-
|
22 |
ACCOUNT_ID = "act_416207949073936"
|
23 |
PAGE_ID = "63257509478"
|
24 |
OPENAI_API = os.getenv("OPENAI_API")
|
25 |
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
|
26 |
BIG_DATASET = None
|
27 |
|
28 |
-
print(ACCESS_TOKEN)
|
29 |
ANALYSIS_TYPE = {
|
30 |
"OUTCOME_SALES": "ROAS",
|
|
|
|
|
31 |
}
|
32 |
|
33 |
API_BASE = 'https://bestever-vision.openai.azure.com/'
|
@@ -112,7 +108,7 @@ def get_ads(adset_id):
|
|
112 |
url = f"{adset_id}/insights"
|
113 |
params = {
|
114 |
"date_preset": "last_90d",
|
115 |
-
"fields": "ad_name,ad_id,impressions,spend,video_play_actions,video_p25_watched_actions,video_p50_watched_actions,video_p75_watched_actions,video_p100_watched_actions,video_play_curve_actions,purchase_roas",
|
116 |
"breakdowns": "age,gender",
|
117 |
"limit": 1000,
|
118 |
"level": "ad",
|
@@ -130,6 +126,48 @@ def save_image_from_url(url, filename):
|
|
130 |
return True
|
131 |
return False
|
132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
def get_creative_assets(ad_id):
|
134 |
# checking if the asset already exists
|
135 |
if os.path.exists(f'assets/{ad_id}.png') or os.path.exists(f'assets/{ad_id}.mp4') or os.path.exists(f'assets/{ad_id}.jpg'):
|
@@ -141,7 +179,6 @@ def get_creative_assets(ad_id):
|
|
141 |
}
|
142 |
creative = call_graph_api(url, params)["creative"]
|
143 |
saved = False
|
144 |
-
print("-" * 10)
|
145 |
if "video_id" in creative:
|
146 |
# download video
|
147 |
video_id = creative["video_id"]
|
@@ -155,6 +192,7 @@ def get_creative_assets(ad_id):
|
|
155 |
if len(ext) > 4:
|
156 |
ext = "mp4"
|
157 |
saved = save_image_from_url(video_source, os.path.join("assets", f'{ad_id}.{ext}'))
|
|
|
158 |
|
159 |
elif "image_url" in creative:
|
160 |
image_url = creative["image_url"]
|
@@ -178,6 +216,7 @@ def get_creative_assets(ad_id):
|
|
178 |
if len(ext) > 4:
|
179 |
ext = "png"
|
180 |
saved = save_image_from_url(video_url, os.path.join("assets", f'{ad_id}.{ext}'))
|
|
|
181 |
elif "image" in media:
|
182 |
image_url = media["image"]["src"]
|
183 |
ext = image_url.split("?")[0].split(".")[-1]
|
@@ -212,22 +251,34 @@ def top_n_ads(df, n=5):
|
|
212 |
if os.path.exists(f'assets/{ad_id}.png'):
|
213 |
image_paths.append(f'assets/{ad_id}.png')
|
214 |
elif os.path.exists(f'assets/{ad_id}.mp4'):
|
215 |
-
image_paths.append(f'assets/{ad_id}.
|
216 |
elif os.path.exists(f'assets/{ad_id}.jpg'):
|
217 |
image_paths.append(f'assets/{ad_id}.jpg')
|
218 |
return image_paths
|
219 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
|
221 |
-
def
|
222 |
# - TS to CTR ratio analysis
|
223 |
-
# - ROAS analysis (I will see the better metric here to use)
|
224 |
# - Video drop off analysis
|
225 |
if ANALYSIS_TYPE[objective] == "ROAS":
|
226 |
-
# 3 analysis:
|
227 |
-
# general
|
228 |
-
# male
|
229 |
-
# female
|
230 |
-
|
231 |
df_general = df.groupby(["ad_id"]).sum()
|
232 |
df_general = df_general.reset_index()
|
233 |
df_general["relative_roas"] = df_general["purchase_roas"] / df_general["spend"]
|
@@ -277,6 +328,53 @@ def perform_analysis(df, objective):
|
|
277 |
"Female": female_output,
|
278 |
}
|
279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
def format_adsets(campaign_id):
|
281 |
st_campaigns.empty()
|
282 |
adsets = get_adsets(campaign_id)
|
@@ -308,23 +406,40 @@ def format_ads(adset_id):
|
|
308 |
for col in video_cols:
|
309 |
if col in df_ads.columns:
|
310 |
df_ads[col] = df_ads[col].apply(lambda x: float(x[0].get("value", 0)) if isinstance(x, list) else 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
311 |
|
312 |
if "purchase_roas" in df_ads.columns:
|
313 |
df_ads["purchase_roas"] = df_ads["purchase_roas"].apply(lambda x: float(x[0].get("value", 0)) if isinstance(x, list) else 0)
|
|
|
|
|
314 |
|
315 |
if BIG_DATASET is None:
|
316 |
BIG_DATASET = df_ads
|
317 |
else:
|
318 |
BIG_DATASET = pd.concat([BIG_DATASET, df_ads])
|
319 |
-
BIG_DATASET.to_csv("big_dataset.csv")
|
320 |
with st_ads.container():
|
|
|
321 |
with st.expander("See analysis", expanded=False):
|
322 |
analysis = st.empty()
|
323 |
|
324 |
for i, ad in enumerate(df_ads["ad_id"].unique()):
|
325 |
get_creative_assets(ad)
|
326 |
ad_name = df_ads[df_ads["ad_id"] == ad]["ad_name"].values[0]
|
327 |
-
|
|
|
|
|
|
|
|
|
328 |
tab1, tab2, tab3 = st.tabs(["Creative", "Analytics", "Video Analysis"])
|
329 |
df_tmp = df_ads[df_ads["ad_id"] == ad]
|
330 |
with tab2:
|
@@ -363,7 +478,6 @@ def format_ads(adset_id):
|
|
363 |
|
364 |
if "purchase_roas" in df_tmp.columns:
|
365 |
df_roas = df_tmp.groupby(options)[["spend","purchase_roas"]].sum().reset_index().sort_values("purchase_roas", ascending=False)
|
366 |
-
print(df_roas)
|
367 |
values = [str(v) for v in df_tmp[options].values]
|
368 |
fig = go.Figure(data=[
|
369 |
go.Bar(name='ROAS', x=values, y=df_roas["purchase_roas"]),
|
@@ -377,7 +491,6 @@ def format_ads(adset_id):
|
|
377 |
if "video_play_actions" in df_tmp.columns:
|
378 |
values = df_ads[["ad_id","video_play_actions","video_p25_watched_actions","video_p50_watched_actions","video_p75_watched_actions","video_p100_watched_actions"]].groupby("ad_id").get_group(ad).sum().values[1:]
|
379 |
labels = ["Total video plays","Video plays until 25%","Video plays until 50%","Video plays until 75%","Video plays until 100%"]
|
380 |
-
print(values)
|
381 |
if values[0] > 0:
|
382 |
st.plotly_chart(create_video_plays_funnel(values, labels), use_container_width=True)
|
383 |
with tab1:
|
@@ -389,13 +502,27 @@ def format_ads(adset_id):
|
|
389 |
st.image(f'assets/{ad}.jpg', caption='Creative', use_column_width=True)
|
390 |
|
391 |
with analysis.container():
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
399 |
|
400 |
def create_video_plays_funnel(funnel_data, funnel_title):
|
401 |
fig = go.Figure(go.Funnel(
|
@@ -413,7 +540,6 @@ if not st.session_state["initiated"]:
|
|
413 |
st.session_state["initiated"] = True
|
414 |
with st_campaigns.container():
|
415 |
st.title("Campaigns")
|
416 |
-
print(get_campaigns(ACCOUNT_ID))
|
417 |
for c in (get_campaigns(ACCOUNT_ID))["data"]:
|
418 |
with st.popover(c["campaign_name"]):
|
419 |
st.markdown("**Impressions**: " + str(c["impressions"]))
|
@@ -425,3 +551,4 @@ if not st.session_state["initiated"]:
|
|
425 |
on_click=format_adsets,
|
426 |
kwargs={"campaign_id": c["campaign_id"]},
|
427 |
)
|
|
|
|
14 |
from openai import AzureOpenAI
|
15 |
import base64
|
16 |
|
|
|
|
|
|
|
|
|
|
|
17 |
ACCOUNT_ID = "act_416207949073936"
|
18 |
PAGE_ID = "63257509478"
|
19 |
OPENAI_API = os.getenv("OPENAI_API")
|
20 |
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
|
21 |
BIG_DATASET = None
|
22 |
|
|
|
23 |
ANALYSIS_TYPE = {
|
24 |
"OUTCOME_SALES": "ROAS",
|
25 |
+
"OUTCOME_AWARENESS": "ENGAGEMENT",
|
26 |
+
"OUTCOME_LEADS": "ENGAGEMENT"
|
27 |
}
|
28 |
|
29 |
API_BASE = 'https://bestever-vision.openai.azure.com/'
|
|
|
108 |
url = f"{adset_id}/insights"
|
109 |
params = {
|
110 |
"date_preset": "last_90d",
|
111 |
+
"fields": "ad_name,ad_id,impressions,spend,video_play_actions,video_p25_watched_actions,video_p50_watched_actions,video_p75_watched_actions,video_p100_watched_actions,video_play_curve_actions,purchase_roas,cost_per_action_type,objective",
|
112 |
"breakdowns": "age,gender",
|
113 |
"limit": 1000,
|
114 |
"level": "ad",
|
|
|
126 |
return True
|
127 |
return False
|
128 |
|
129 |
+
|
130 |
+
def extract_specific_frame(video_path, frame_position, output_image):
|
131 |
+
# Open the video file
|
132 |
+
cap = cv2.VideoCapture(video_path)
|
133 |
+
|
134 |
+
if not cap.isOpened():
|
135 |
+
print("Error opening video file")
|
136 |
+
return
|
137 |
+
|
138 |
+
# Get the total number of frames
|
139 |
+
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
140 |
+
|
141 |
+
# Calculate the frame index based on the position
|
142 |
+
if frame_position == 'middle':
|
143 |
+
frame_index = total_frames // 2
|
144 |
+
elif frame_position == 'last':
|
145 |
+
frame_index = total_frames - 1
|
146 |
+
else: # 'first' or any other input defaults to the first frame
|
147 |
+
frame_index = 0
|
148 |
+
|
149 |
+
# Set the current frame position
|
150 |
+
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index)
|
151 |
+
|
152 |
+
# Read the frame
|
153 |
+
ret, frame = cap.read()
|
154 |
+
if ret:
|
155 |
+
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
156 |
+
frame = Image.fromarray(frame)
|
157 |
+
frame.save(output_image, "JPEG")
|
158 |
+
else:
|
159 |
+
print(f"Error reading the {frame_position} frame")
|
160 |
+
|
161 |
+
# Release the video capture object
|
162 |
+
cap.release()
|
163 |
+
|
164 |
+
def split_video_in_frames(video_path):
|
165 |
+
output_path = video_path.split(".")[0]
|
166 |
+
extract_specific_frame(video_path, 'first', output_path + "_first.jpg")
|
167 |
+
extract_specific_frame(video_path, 'middle', output_path + "_middle.jpg")
|
168 |
+
extract_specific_frame(video_path, 'last', output_path + "_last.jpg")
|
169 |
+
|
170 |
+
|
171 |
def get_creative_assets(ad_id):
|
172 |
# checking if the asset already exists
|
173 |
if os.path.exists(f'assets/{ad_id}.png') or os.path.exists(f'assets/{ad_id}.mp4') or os.path.exists(f'assets/{ad_id}.jpg'):
|
|
|
179 |
}
|
180 |
creative = call_graph_api(url, params)["creative"]
|
181 |
saved = False
|
|
|
182 |
if "video_id" in creative:
|
183 |
# download video
|
184 |
video_id = creative["video_id"]
|
|
|
192 |
if len(ext) > 4:
|
193 |
ext = "mp4"
|
194 |
saved = save_image_from_url(video_source, os.path.join("assets", f'{ad_id}.{ext}'))
|
195 |
+
split_video_in_frames(os.path.join("assets", f'{ad_id}.{ext}'))
|
196 |
|
197 |
elif "image_url" in creative:
|
198 |
image_url = creative["image_url"]
|
|
|
216 |
if len(ext) > 4:
|
217 |
ext = "png"
|
218 |
saved = save_image_from_url(video_url, os.path.join("assets", f'{ad_id}.{ext}'))
|
219 |
+
split_video_in_frames(os.path.join("assets", f'{ad_id}.{ext}'))
|
220 |
elif "image" in media:
|
221 |
image_url = media["image"]["src"]
|
222 |
ext = image_url.split("?")[0].split(".")[-1]
|
|
|
251 |
if os.path.exists(f'assets/{ad_id}.png'):
|
252 |
image_paths.append(f'assets/{ad_id}.png')
|
253 |
elif os.path.exists(f'assets/{ad_id}.mp4'):
|
254 |
+
image_paths.append(f'assets/{ad_id}_first.jpg')
|
255 |
elif os.path.exists(f'assets/{ad_id}.jpg'):
|
256 |
image_paths.append(f'assets/{ad_id}.jpg')
|
257 |
return image_paths
|
258 |
|
259 |
+
def video_dropoff_analysis(df):
|
260 |
+
if "video_play_actions" not in df.columns:
|
261 |
+
return "There is not enough data to generate insights about video dropoff."
|
262 |
+
|
263 |
+
df_general = df.groupby(["ad_id"]).sum()
|
264 |
+
df_general = df_general.reset_index()
|
265 |
+
df_general = df_general[df_general["video_play_actions"] > 0]
|
266 |
+
|
267 |
+
if df_general.shape[0] < 2:
|
268 |
+
return "There is not enough data to generate insights about video dropoff."
|
269 |
+
|
270 |
+
df_general["p100"] = df_general["video_p100_watched_actions"] / df_general["video_play_actions"]
|
271 |
+
df_general = df_general.sort_values("p100", ascending=False)
|
272 |
+
image_paths = top_n_ads(df_general)
|
273 |
+
image_paths = [path for path in image_paths if path.endswith(".mp4")]
|
274 |
+
|
275 |
+
response = call_gpt_vision(client, image_paths, f"You are given a set of the most performative videos. Your task is to evaluate and anylise these videos, getting features like type of shoot, lightinig, colors, motion, etc, and generate a paragraph explaning what makes a good video. I will also provide a list of video plays in different stages of the video. The main idea is to understand what makes people spend more time on the video. Please, try to be technical and generate insights that can be use to future videos. Dropoff stages: 25%, 50%, 75%, 100%. Dataset: {df.head(5)}")
|
276 |
+
return response.choices[0].message.content
|
277 |
|
278 |
+
def performance_analysis(df, objective):
|
279 |
# - TS to CTR ratio analysis
|
|
|
280 |
# - Video drop off analysis
|
281 |
if ANALYSIS_TYPE[objective] == "ROAS":
|
|
|
|
|
|
|
|
|
|
|
282 |
df_general = df.groupby(["ad_id"]).sum()
|
283 |
df_general = df_general.reset_index()
|
284 |
df_general["relative_roas"] = df_general["purchase_roas"] / df_general["spend"]
|
|
|
328 |
"Female": female_output,
|
329 |
}
|
330 |
|
331 |
+
elif ANALYSIS_TYPE[objective] == "ENGAGEMENT":
|
332 |
+
df_general = df.groupby(["ad_id"]).sum()
|
333 |
+
df_general = df_general.reset_index()
|
334 |
+
df_general = df_general.sort_values("cost_per_engagement", ascending=True)
|
335 |
+
|
336 |
+
image_paths = top_n_ads(df_general)
|
337 |
+
response = call_gpt_vision(client, image_paths, "You are a marketing analyst and your task is to find common features between the ads that presented more engagement. You are given the top 5 most perfomative ads, and we expect you to return 5 keywords and its explanation that defines what makes a good ad that show an excellent engagement. Return it as a list of 5 concepts and its explanation, using the provided ads as example. Try to use nice categories to describe the features (you can use some names like `minimalist design`, `Clear message`, etc). Also, pay attention if the ads are mostly images or videos, this is important to say. The output MUST contain one concept per line. For each like, follow the structure: <concept>:<explanation>.")
|
338 |
+
image_winner_concepts = parse_tags_from_content(response)
|
339 |
+
|
340 |
+
response = call_gpt_vision(client, [], f"Following, you have the key features that makes an ad a performative ad. Your task is to group this information and summarize in a nice paragraph that will be presented to the marketing team. Be concise. Features:\n{image_winner_concepts}")
|
341 |
+
insights = response.choices[0].message.content
|
342 |
+
|
343 |
+
general_output = {"keywords": [concept["name"] for concept in image_winner_concepts], "insights": insights}
|
344 |
+
|
345 |
+
# Groupby ad_id and gender
|
346 |
+
df_male = df[df["gender"] == "male"].groupby(["ad_id"]).sum()
|
347 |
+
df_male = df_male.reset_index()
|
348 |
+
df_male = df_male.sort_values("cost_per_engagement", ascending=True)
|
349 |
+
|
350 |
+
image_paths = top_n_ads(df_male)
|
351 |
+
response = call_gpt_vision(client, image_paths, "You are a marketing analyst and your task is to find common features between the ads that presented more engagement from men. You are given the top 5 most perfomative ads, and we expect you to return 5 keywords and its explanation that defines what makes a good ad that show an excellent engagement. Return it as a list of 5 concepts and its explanation, using the provided ads as example. Try to use nice categories to describe the features (you can use some names like `minimalist design`, `Clear message`, etc). Also, pay attention if the ads are mostly images or videos, this is important to say. The output MUST contain one concept per line. For each like, follow the structure: <concept>:<explanation>.")
|
352 |
+
image_winner_concepts = parse_tags_from_content(response)
|
353 |
+
|
354 |
+
response = call_gpt_vision(client, [], f"Following, you have the key features that makes an ad a performative ad. Your task is to group this information and summarize in a nice paragraph that will be presented to the marketing team. Be concise. Features:\n{image_winner_concepts}")
|
355 |
+
insights = response.choices[0].message.content
|
356 |
+
|
357 |
+
male_output = {"keywords": [concept["name"] for concept in image_winner_concepts], "insights": insights}
|
358 |
+
|
359 |
+
|
360 |
+
df_female = df[df["gender"] == "female"].groupby(["ad_id"]).sum()
|
361 |
+
df_female = df_female.reset_index()
|
362 |
+
df_female = df_female.sort_values("cost_per_engagement", ascending=True)
|
363 |
+
|
364 |
+
image_paths = top_n_ads(df_female)
|
365 |
+
response = call_gpt_vision(client, image_paths, "You are a marketing analyst and your task is to find common features between the ads that presented more engagement from women. You are given the top 5 most perfomative ads, and we expect you to return 5 keywords and its explanation that defines what makes a good ad that show an excellent engagement. Return it as a list of 5 concepts and its explanation, using the provided ads as example. Try to use nice categories to describe the features (you can use some names like `minimalist design`, `Clear message`, etc). Also, pay attention if the ads are mostly images or videos, this is important to say. The output MUST contain one concept per line. For each like, follow the structure: <concept>:<explanation>.")
|
366 |
+
image_winner_concepts = parse_tags_from_content(response)
|
367 |
+
|
368 |
+
response = call_gpt_vision(client, [], f"Following, you have the key features that makes an ad a performative ad. Your task is to group this information and summarize in a nice paragraph that will be presented to the marketing team. Be concise. Features:\n{image_winner_concepts}")
|
369 |
+
insights = response.choices[0].message.content
|
370 |
+
female_output = {"keywords": [concept["name"] for concept in image_winner_concepts], "insights": insights}
|
371 |
+
|
372 |
+
return {
|
373 |
+
"General": general_output,
|
374 |
+
"Male": male_output,
|
375 |
+
"Female": female_output,
|
376 |
+
}
|
377 |
+
|
378 |
def format_adsets(campaign_id):
|
379 |
st_campaigns.empty()
|
380 |
adsets = get_adsets(campaign_id)
|
|
|
406 |
for col in video_cols:
|
407 |
if col in df_ads.columns:
|
408 |
df_ads[col] = df_ads[col].apply(lambda x: float(x[0].get("value", 0)) if isinstance(x, list) else 0)
|
409 |
+
|
410 |
+
objective = df_ads["objective"].values[0]
|
411 |
+
def get_engagement(row):
|
412 |
+
if isinstance(row, list):
|
413 |
+
for ac in row:
|
414 |
+
if ac["action_type"] == "post_engagement":
|
415 |
+
return float(ac["value"])
|
416 |
+
return 0
|
417 |
+
if "cost_per_action_type" in df_ads.columns:
|
418 |
+
df_ads["cost_per_engagement"] = df_ads["cost_per_action_type"].apply(get_engagement)
|
419 |
+
df_ads = df_ads.sort_values("cost_per_engagement", ascending=True)
|
420 |
|
421 |
if "purchase_roas" in df_ads.columns:
|
422 |
df_ads["purchase_roas"] = df_ads["purchase_roas"].apply(lambda x: float(x[0].get("value", 0)) if isinstance(x, list) else 0)
|
423 |
+
df_ads["r_purchase_roas"] = df_ads["purchase_roas"] / df_ads["spend"]
|
424 |
+
df_ads = df_ads.sort_values("r_purchase_roas", ascending=False)
|
425 |
|
426 |
if BIG_DATASET is None:
|
427 |
BIG_DATASET = df_ads
|
428 |
else:
|
429 |
BIG_DATASET = pd.concat([BIG_DATASET, df_ads])
|
|
|
430 |
with st_ads.container():
|
431 |
+
st.title("Ads")
|
432 |
with st.expander("See analysis", expanded=False):
|
433 |
analysis = st.empty()
|
434 |
|
435 |
for i, ad in enumerate(df_ads["ad_id"].unique()):
|
436 |
get_creative_assets(ad)
|
437 |
ad_name = df_ads[df_ads["ad_id"] == ad]["ad_name"].values[0]
|
438 |
+
if i < 3:
|
439 |
+
addon = "🏆"
|
440 |
+
else:
|
441 |
+
addon = ""
|
442 |
+
with st.popover(f"{addon} {ad_name}"):
|
443 |
tab1, tab2, tab3 = st.tabs(["Creative", "Analytics", "Video Analysis"])
|
444 |
df_tmp = df_ads[df_ads["ad_id"] == ad]
|
445 |
with tab2:
|
|
|
478 |
|
479 |
if "purchase_roas" in df_tmp.columns:
|
480 |
df_roas = df_tmp.groupby(options)[["spend","purchase_roas"]].sum().reset_index().sort_values("purchase_roas", ascending=False)
|
|
|
481 |
values = [str(v) for v in df_tmp[options].values]
|
482 |
fig = go.Figure(data=[
|
483 |
go.Bar(name='ROAS', x=values, y=df_roas["purchase_roas"]),
|
|
|
491 |
if "video_play_actions" in df_tmp.columns:
|
492 |
values = df_ads[["ad_id","video_play_actions","video_p25_watched_actions","video_p50_watched_actions","video_p75_watched_actions","video_p100_watched_actions"]].groupby("ad_id").get_group(ad).sum().values[1:]
|
493 |
labels = ["Total video plays","Video plays until 25%","Video plays until 50%","Video plays until 75%","Video plays until 100%"]
|
|
|
494 |
if values[0] > 0:
|
495 |
st.plotly_chart(create_video_plays_funnel(values, labels), use_container_width=True)
|
496 |
with tab1:
|
|
|
502 |
st.image(f'assets/{ad}.jpg', caption='Creative', use_column_width=True)
|
503 |
|
504 |
with analysis.container():
|
505 |
+
v_d, p_a = st.tabs(["Video Dropoff", "Performance Analysis"])
|
506 |
+
with p_a:
|
507 |
+
if not os.path.exists(f"{adset_id}_performance.json"):
|
508 |
+
report = performance_analysis(df_ads, objective)
|
509 |
+
json.dump(report, open(f"{adset_id}_performance.json", "w"))
|
510 |
+
else:
|
511 |
+
report = json.load(open(f"{adset_id}_performance.json", "r"))
|
512 |
+
tabs = st.tabs(report.keys())
|
513 |
+
tabs_names = list(report.keys())
|
514 |
+
for i, tab in enumerate(tabs):
|
515 |
+
with tab:
|
516 |
+
st.multiselect("", report[tabs_names[i]]["keywords"], report[tabs_names[i]]["keywords"], key=f"{ad}_{i}")
|
517 |
+
st.write(report[tabs_names[i]]["insights"])
|
518 |
+
|
519 |
+
with v_d:
|
520 |
+
if not os.path.exists(f"{adset_id}_video_dropoff.json"):
|
521 |
+
report = video_dropoff_analysis(df_ads)
|
522 |
+
json.dump(report, open(f"{adset_id}_video_dropoff.json", "w"))
|
523 |
+
else:
|
524 |
+
report = json.load(open(f"{adset_id}_video_dropoff.json", "r"))
|
525 |
+
st.write(report)
|
526 |
|
527 |
def create_video_plays_funnel(funnel_data, funnel_title):
|
528 |
fig = go.Figure(go.Funnel(
|
|
|
540 |
st.session_state["initiated"] = True
|
541 |
with st_campaigns.container():
|
542 |
st.title("Campaigns")
|
|
|
543 |
for c in (get_campaigns(ACCOUNT_ID))["data"]:
|
544 |
with st.popover(c["campaign_name"]):
|
545 |
st.markdown("**Impressions**: " + str(c["impressions"]))
|
|
|
551 |
on_click=format_adsets,
|
552 |
kwargs={"campaign_id": c["campaign_id"]},
|
553 |
)
|
554 |
+
|