Example #1
0
    def __init__(self,
                 requests: List[FacebookRequest],
                 api: FacebookAdsApi = None):
        self.api = api or FacebookAdsApi.get_default_api()

        self._requests = requests
        self._batches = []
Example #2
0
    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()
Example #4
0
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
Example #6
0
 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
Example #7
0
 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,
            )
        )
Example #9
0
    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
Example #10
0
    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
Example #11
0
    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')
Example #15
0
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),
                ))