def __init__(self, requests: List[FacebookRequest], api: FacebookAdsApi = None): self.api = api or FacebookAdsApi.get_default_api() self._requests = requests self._batches = []
def search(cls, params=None, api=None): api = api or FacebookAdsApi.get_default_api() if not api: raise FacebookBadObjectError( "An Api instance must be provided as an argument or set as " "the default Api in FacebookAdsApi.", ) params = {} if not params else params.copy() response = api.call( FacebookAdsApi.HTTP_METHOD_GET, "/".join( (FacebookSession.GRAPH, FacebookAdsApi.API_VERSION, 'search')), params, ).json() ret_val = [] if response: keys = response['data'] # The response object can be either a dictionary of dictionaries # or a dictionary of lists. if isinstance(keys, list): for item in keys: search_obj = TargetingSearch() search_obj.update(item) ret_val.append(search_obj) elif isinstance(keys, dict): for item in keys: search_obj = TargetingSearch() search_obj.update(keys[item]) if keys[item]: ret_val.append(search_obj) return ret_val
async def create_event(self, session): api_version = FacebookAdsApi.get_default_api().API_VERSION url = '/'.join( [FacebookSession.GRAPH, api_version, self.pixel_id, 'events']) facebook_session = FacebookAdsApi.get_default_api()._session access_token = facebook_session.access_token params = self.get_params() params['access_token'] = access_token if facebook_session.app_secret: params['appsecret_proof'] = facebook_session._gen_appsecret_proof() cafile = os.path.join(os.path.dirname(__file__), '..', '..', 'fb_ca_chain_bundle.crt') sslcontext = ssl.create_default_context(cafile=cafile) async with session.post(url=url, data=params, ssl=sslcontext, headers=FacebookAdsApi.get_default_api(). HTTP_DEFAULT_HEADERS) as response: return await response.json()
def _process_single_day_jobs_concurrently(job_list: typing.List[JobQueueItem], n_threads: int) -> None: if n_threads < 1: raise ValueError( '_process_single_day_jobs_concurrently should have n_threads > 0') heapq.heapify(job_list) thread_args: ThreadArgs = ThreadArgs(job_list) # store the default API since the worker threads will change it default_api: FacebookAdsApi = FacebookAdsApi.get_default_api() thread_list: typing.List[threading.Thread] = list() thread: threading.Thread = threading.Thread(target=_retry_thread_func, args=(thread_args, )) thread_list.append(thread) thread.start() for i in range(0, n_threads): thread = threading.Thread(target=_job_thread_func, args=(thread_args, )) thread_list.append(thread) thread.start() thread_args.state_changed_cv.acquire() try: while (not thread_args.error_occured) and (thread_args.jobs_left > 0): thread_args.state_changed_cv.wait() except: thread_args.error_occured = True finally: thread_args.state_changed_cv.release() # notify all waiting threads, so they can see that they are done # release -> aquire ordering matters due to potential deadlocking # use a second variable to identify a done variable # that requires only a single lock rather than all three with thread_args.job_list_cv: thread_args.job_thread_done = True thread_args.job_list_cv.notify_all() with thread_args.retry_queue_cv: thread_args.retry_thread_done = True thread_args.retry_queue_cv.notify() with thread_args.logging_mutex: logging.info('waiting for all threads to exit'.format( threading.get_ident())) for thread in thread_list: thread.join() # restore the default API in case something else needs it after this function FacebookAdsApi.set_default_api(default_api) if thread_args.error_occured: sys.exit(1)
def retrieve_order_level_report_data( self, from_date, to_date, business_id, pixel_id, app_id, limit=150, api=None, ): """ Retrieve order level reporting for a given time range. """ path = (business_id, "order_id_attributions", ) params = { 'since': from_date.strftime('%s'), 'until': to_date.strftime('%s'), 'pixel_id': pixel_id, 'app_id': app_id, 'limit': limit, } if not api: api = FacebookAdsApi.get_default_api() page_index = 0 while path: page_index += 1 logger.info( "Pulling page %s for %s/%s from %s -> %s", page_index, pixel_id, app_id, from_date, to_date, ) response = api.call( FacebookAdsApi.HTTP_METHOD_GET, path, params, ).json() # only emit non-empty pages if response['data']: yield response['data'] if 'paging' in response and 'next' in response['paging']: path = response['paging']['next'] params = {} else: break
def get_by_ids(cls, ids, params=None, fields=None, api=None): api = api or FacebookAdsApi.get_default_api() params = dict(params or {}) cls._assign_fields_to_params(fields, params) params['ids'] = ','.join(map(str, ids)) response = api.call( 'GET', ['/'], params=params, ) result = [] for fbid, data in response.json().items(): obj = cls(fbid, api=api) obj._set_data(data) result.append(obj) return result
def get_by_ids(cls, ids, params=None, fields=None, api=None): api = api or FacebookAdsApi.get_default_api() params = dict(params or {}) cls._assign_fields_to_params(fields, params) params['ids'] = ','.join(map(str, ids)) response = api.call( 'GET', ['/'], params=params, ) result = [] for fbid, data in response.json().items(): obj = cls(fbid, api=api) obj._set_data(data) result.append(obj) return result
def retrieve_order_level_report_data_parallel( self, from_date, to_date, business_id, pixel_id='', app_id='', splits=4, ): """ Parallelise the retrieval of order level reports. """ d = (to_date - from_date) / splits # Convert any dates to datetime before splitting # otherwise we lose intra-day resolution start_time = from_date if isinstance(start_time, date): start_time = datetime.combine(start_time, time()) time_partitions = [ (i * d + start_time, (i + 1) * d + start_time) for i in range(splits) ] # keep a copy of the Ads API session as we're going to be using # it across new threads api = FacebookAdsApi.get_default_api() return itertools.chain.from_iterable( map( lambda (start, end): self.zzz_buffer_iterable_async( # promise fetches time-ranges concurrently self.retrieve_order_level_report_data( api=api, from_date=start, to_date=end, business_id=business_id, pixel_id=pixel_id, app_id=app_id, ), buffer_size=30, ), time_partitions, ) )
def __init__(self, fbid=None, parent_id=None, api=None): """Initializes a CRUD object. Args: fbid (optional): The id of the object ont the Graph. parent_id (optional): The id of the object's parent. api (optional): An api object which all calls will go through. If an api object is not specified, api calls will revert to going through the default api. """ super(AbstractCrudObject, self).__init__() self._api = api or FacebookAdsApi.get_default_api() self._changes = {} if (parent_id is not None): warning_message = "parent_id as a parameter of constructor is " \ "being deprecated." logging.warning(warning_message) self._parent_id = parent_id self._data['id'] = fbid self._include_summary = True
def __init__(self, fbid=None, parent_id=None, api=None): """Initializes a CRUD object. Args: fbid (optional): The id of the object ont the Graph. parent_id (optional): The id of the object's parent. api (optional): An api object which all calls will go through. If an api object is not specified, api calls will revert to going through the default api. """ super(AbstractCrudObject, self).__init__() self._api = api or FacebookAdsApi.get_default_api() self._changes = {} if (parent_id is not None): warning_message = "parent_id as a parameter of constructor is " \ "being deprecated." logging.warning(warning_message) self._parent_id = parent_id self._data['id'] = fbid self._include_summary = True
def remote_create_from_zip(cls, filename, parent_id, api=None): api = api or FacebookAdsApi.get_default_api() open_file = open(filename, 'rb') response = api.call( 'POST', (parent_id, cls.get_endpoint()), files={filename: open_file}, ) open_file.close() data = response.json() objs = [] for image_filename in data['images']: image = cls(parent_id=parent_id) image.update(data['images'][image_filename]) image[cls.Field.id] = '%s:%s' % ( parent_id[4:], data['images'][image_filename][cls.Field.hash], ) objs.append(image) return objs
def remote_create_from_zip(cls, filename, parent_id, api=None): api = api or FacebookAdsApi.get_default_api() open_file = open(filename, 'rb') response = api.call( 'POST', (parent_id, cls.get_endpoint()), files={filename: open_file}, ) open_file.close() data = response.json() objs = [] for image_filename in data['images']: image = cls(parent_id=parent_id) image.update(data['images'][image_filename]) image[cls.Field.id] = '%s:%s' % ( parent_id[4:], data['images'][image_filename][cls.Field.hash], ) objs.append(image) return objs
def search(cls, params=None, api=None): api = api or FacebookAdsApi.get_default_api() if not api: raise FacebookBadObjectError( "An Api instance must be provided as an argument or set as " "the default Api in FacebookAdsApi.", ) params = {} if not params else params.copy() response = api.call( FacebookAdsApi.HTTP_METHOD_GET, "/".join(( FacebookSession.GRAPH, FacebookAdsApi.API_VERSION, 'search' )), params, ).json() ret_val = [] if response: keys = response['data'] # The response object can be either a dictionary of dictionaries # or a dictionary of lists. if isinstance(keys, list): for item in keys: search_obj = TargetingSearch() search_obj.update(item) ret_val.append(search_obj) elif isinstance(keys, dict): for item in keys: search_obj = TargetingSearch() search_obj.update(keys[item]) if keys[item]: ret_val.append(search_obj) return ret_val
def enable(cls): if cls.reporter_instance == None: api = FacebookAdsApi.get_default_api() cls.reporter_instance = cls(api._session.app_id, sys.excepthook) sys.excepthook = cls.reporter_instance.__exception_handler print('CrashReporter: Enabled')
class Authentication(): """ DON'T USE THIS CLASS DIRECTLY. USE `auth()` function from this module Helper class to authenticate using config.json config file from this repository. This is useful in two cases: - Testing environment - Interactive exploration in REPL This class shouldn't be used in production. It's intended for development. Use FacebookAdsApi.init in production """ _api = FacebookAdsApi.get_default_api() if _api: _is_authenticated = True else: _is_authenticated = False @property def is_authenticated(cls): return cls._is_authenticated @classmethod def load_config(cls): config_file = open(os.path.join(repo_dir, 'config.json')) config = json.load(config_file) config_file.close() return config @classmethod def auth(cls): """ Prepare for Ads API calls and return a tuple with act_id and page_id. page_id can be None but act_id is always set. """ config = cls.load_config() if cls._is_authenticated: return config['act_id'], config.get('page_id', None) if config['app_id'] and config['app_secret'] \ and config['act_id'] and config['access_token']: FacebookAdsApi.init( config['app_id'], config['app_secret'], config['access_token'], config['act_id'], ) cls._is_authenticated = True return config['act_id'], config.get('page_id', None) else: required_fields = set( ('app_id', 'app_secret', 'act_id', 'access_token')) missing_fields = required_fields - set(config.keys()) raise FacebookError( '\n\tFile config.json needs to have the following fields: {}\n' '\tMissing fields: {}\n'.format( ', '.join(required_fields), ', '.join(missing_fields), ))