Skip to content
This repository was archived by the owner on Jul 29, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 49 additions & 24 deletions parsons/facebook_ads/facebook_ads.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,50 @@ def _add_batch_to_custom_audience(app_id, app_secret, access_token, audience_id,
CustomAudience(audience_id).add_users(schema, batch, is_raw=True)
logger.info(f"Added {added_so_far+len(batch)}/{total_rows} users to custom audience...")

def _remove_batch_from_custom_audience(app_id, app_secret, access_token, audience_id, schema,
batch, added_so_far, total_rows):
FacebookAdsApi.init(app_id, app_secret, access_token)

CustomAudience(audience_id).remove_users(schema, batch, is_raw=True)
logger.info(f"Removed {added_so_far+len(batch)}/{total_rows} users from custom audience...")

def _change_users_in_custom_audience(self, audience_id, users_table, adding=True):
match_table = FacebookAds.get_match_table_for_users_table(users_table)
if not match_table.columns:
raise KeyError("No valid columns found for audience matching. "
"See FacebookAds.KeyMatchMap for supported columns")

num_rows = match_table.num_rows
logger.info(f"Found {num_rows} rows with valid FB matching keys")
logger.info(f"Using FB matching keys: {match_table.columns}")

(schema, data) = FacebookAds._get_match_schema_and_data(match_table)

# Use the FB API to add users, respecting the limit per API call.
# Process and upload batches in parallel, to improve performance.

batch_size = MAX_FB_AUDIENCE_API_USERS

if adding:
parallel_jobs = (
delayed(FacebookAds._add_batch_to_custom_audience)(
self.app_id, self.app_secret, self.access_token, audience_id, schema,
data[i:i+batch_size], i, num_rows
)
for i in range(0, len(data), batch_size)
)
else:
parallel_jobs = (
delayed(FacebookAds._remove_batch_from_custom_audience)(
self.app_id, self.app_secret, self.access_token, audience_id, schema,
data[i:i+batch_size], i, num_rows
)
for i in range(0, len(data), batch_size)
)

n_jobs = os.environ.get('PARSONS_NUM_PARALLEL_JOBS', 4)
Parallel(n_jobs=n_jobs)(parallel_jobs)

def add_users_to_custom_audience(self, audience_id, users_table):
"""
Adds user data to a custom audience.
Expand Down Expand Up @@ -352,29 +396,10 @@ def add_users_to_custom_audience(self, audience_id, users_table):
logger.info(f"Adding custom audience users from provided table with "
f"{users_table.num_rows} rows")

match_table = FacebookAds.get_match_table_for_users_table(users_table)
if not match_table.columns:
raise KeyError("No valid columns found for audience matching. "
"See FacebookAds.KeyMatchMap for supported columns")

num_rows = match_table.num_rows
logger.info(f"Found {num_rows} rows with valid FB matching keys")
logger.info(f"Using FB matching keys: {match_table.columns}")
FacebookAds._change_users_in_custom_audience(self, audience_id, users_table)

(schema, data) = FacebookAds._get_match_schema_and_data(match_table)

# Use the FB API to add users, respecting the limit per API call.
# Process and upload batches in parallel, to improve performance.

batch_size = MAX_FB_AUDIENCE_API_USERS

parallel_jobs = (
delayed(FacebookAds._add_batch_to_custom_audience)(
self.app_id, self.app_secret, self.access_token, audience_id, schema,
data[i:i+batch_size], i, num_rows
)
for i in range(0, len(data), batch_size)
)
def remove_users_from_custom_audience(self, audience_id, users_table):
logger.info(f"Removing custom audience users from provided table with "
f"{users_table.num_rows} rows")

n_jobs = os.environ.get('PARSONS_NUM_PARALLEL_JOBS', 4)
Parallel(n_jobs=n_jobs)(parallel_jobs)
FacebookAds._change_users_in_custom_audience(self, audience_id, users_table, adding=False)
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ airtable-python-wrapper==0.13.0
google-cloud-storage==1.17.0
google-cloud-bigquery>=1.21.0
docutils<0.15,>=0.10 # Botocore and Sphinx have conflicting needs.
urllib3==1.26.5
urllib3==1.26.8
simplejson==3.16.0
twilio==6.30.0
simple-salesforce==0.74.3
Expand All @@ -45,5 +45,5 @@ pytest-datadir==1.3.0

# Stuff for TMC scripts
# TODO Remove when we have a TMC-specific Docker image
selenium==3.141.0
selenium==4.1.0
jinja2==3.0.2