Exemplo n.º 1
0
 def remote_create(
     self,
     batch=None,
     failure=None,
     files=None,
     params=None,
     success=None,
     api_version=None,
 ):
     """Uploads filename and creates the AdImage object from it.
     It has same arguments as AbstractCrudObject.remote_create except it
     does not have the files argument but requires the 'filename' property
     to be defined.
     """
     if not self[self.Field.filename]:
         raise FacebookBadObjectError(
             "AdImage required a filename to be defined.", )
     filename = self[self.Field.filename]
     with open(filename, 'rb') as open_file:
         return_val = AbstractCrudObject.remote_create(
             self,
             files={filename: open_file},
             batch=batch,
             failure=failure,
             params=params,
             success=success,
             api_version=api_version,
         )
     return return_val
Exemplo n.º 2
0
    def parse_single(self, response):
        if self._custom_parse_method is not None:
            return self._custom_parse_method(response, self._api)

        data = response
        if 'data' in response and isinstance(response['data'], dict):
            data = response['data']
        elif 'images' in response and not isinstance(data['images'], list):
            _, data = data['images'].popitem()
        if 'campaigns' in data:
            _, data = data['campaigns'].popitem()
        elif 'adsets' in data:
            _, data = data['adsets'].popitem()
        elif 'ads' in data:
            _, data = data['ads'].popitem()
        if 'success' in data:
            del data['success']

        if self._reuse_object is not None:
            self._reuse_object._set_data(data)
            return self._reuse_object
        elif self._target_class is not None:
            return AbstractObject.create_object(self._api, data,
                                                self._target_class)
        else:
            raise FacebookBadObjectError(
                'Must specify either target class calling object' +
                'or custom parse method for parser')
Exemplo n.º 3
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
Exemplo n.º 4
0
    def prepare_request_params(self, path, params, headers, files,
                               url_override, api_version):
        if not params:
            params = {}
        if not headers:
            headers = {}
        if not files:
            files = {}
        api_version = api_version or self._api_version
        if api_version and not re.search('v[0-9]+\.[0-9]+', api_version):
            raise FacebookBadObjectError(
                    'Please provide the API version in the following format: %s'
                    % self.API_VERSION)
        if not isinstance(path, six.string_types):
            # Path is not a full path
            path = "/".join((
                self._session.GRAPH or url_override,
                api_version or self.API_VERSION,
                '/'.join(map(str, path)),
            ))

        # Include api headers in http request
        headers = headers.copy()
        headers.update(FacebookAdsApi.HTTP_DEFAULT_HEADERS)
        if params:
            params = _top_level_param_json_encode(params)
        return path, params, headers, files
Exemplo n.º 5
0
 def remote_create(
     self,
     batch=None,
     failure=None,
     files=None,
     params=None,
     success=None,
     api_version=None
 ):
     """
     Uploads filepath and creates the AdVideo object from it.
     It has same arguments as AbstractCrudObject.remote_create except it
     does not have the files argument but requires the 'filepath' property
     to be defined.
     """
     if (self.Field.slideshow_spec in self and
     self[self.Field.slideshow_spec] is not None):
         request = VideoUploadRequest(self.get_api_assured())
         request.setParams(params={'slideshow_spec': {
             'images_urls': self[self.Field.slideshow_spec]['images_urls'],
             'duration_ms': self[self.Field.slideshow_spec]['duration_ms'],
             'transition_ms': self[self.Field.slideshow_spec]['transition_ms'],
         }})
         response = request.send((self.get_parent_id_assured(), 'advideos')).json()
     elif not (self.Field.filepath in self):
         raise FacebookBadObjectError(
             "AdVideo requires a filepath or slideshow_spec to be defined.",
         )
     else:
         video_uploader = VideoUploader()
         response = video_uploader.upload(self)
     self._set_data(response)
     return response
Exemplo n.º 6
0
 def __init__(
     self,
     api=None,
     target_class=None,
     reuse_object=None,
     custom_parse_method=None,
 ):
     """ Initialize an ObjectParser.
     To Initialize, you need to provide either a resuse_object, target_class,
     or an custom_parse_method.
     Args:
         api: FacebookAdsApi object.
         target_class (optional): The expected return object type.
         reuse_object (optional): Reuse existing object to populate response.
         custom_parse_method (optional): Custom parsing method.
     """
     if not any(
         [target_class, reuse_object is not None, custom_parse_method]):
         raise FacebookBadObjectError(
             'Must specify either target class calling object' +
             'or custom parse method for parser')
     self._reuse_object = reuse_object
     self._target_class = target_class
     self._custom_parse_method = custom_parse_method
     self._api = api
Exemplo n.º 7
0
 def _set_data(self, data):
     if hasattr(data, 'items'):
         for key, value in data.items():
             self[key] = value
     else:
         raise FacebookBadObjectError("Bad data to set object data")
     self._json = data
 def __setitem__(self, key, value):
     if key not in self.Field.__dict__:
         raise FacebookBadObjectError(
             "\"%s\" is not a valid field of %s"
             % (key, self.__class__.__name__)
         )
     else:
         super(ValidatesFields, self).__setitem__(key, value)
Exemplo n.º 9
0
    def get_parent_id_assured(self):
        """Returns the object's parent's fbid.
        Raises:
            FacebookBadObjectError if the object does not have a parent id.
        """
        if not self.get_parent_id():
            raise FacebookBadObjectError(
                "%s object needs a parent_id for this operation." %
                self.__class__.__name__, )

        return self.get_parent_id()
Exemplo n.º 10
0
    def get_id_assured(self):
        """Returns the fbid of the object.
        Raises:
            FacebookBadObjectError if the object does not have an id.
        """
        if not self.get(self.Field.id):
            raise FacebookBadObjectError(
                "%s object needs an id for this operation." %
                self.__class__.__name__, )

        return self.get_id()
Exemplo n.º 11
0
def get_parent_id_assured(self):
    """Returns the object's parent's fbid.
    Raises:
        FacebookBadObjectError if the object does not have a parent id.
    """
    #warning_message = "parent_id is being deprecated."
    #logging.warning(warning_message)
    if not self.get_parent_id():
        raise FacebookBadObjectError(
            "%s object needs a parent_id for this operation." %
            self.__class__.__name__, )

    return self.get_parent_id()
Exemplo n.º 12
0
    def get_api_assured(self):
        """Returns the fbid of the object.
        Raises:
            FacebookBadObjectError if get_api returns None.
        """
        api = self.get_api()
        if not api:
            raise FacebookBadObjectError(
                "%s does not yet have an associated api object.\n"
                "Did you forget to instantiate an API session with: "
                "FacebookAdsApi.init(app_id, app_secret, access_token)" %
                self.__class__.__name__, )

        return api
Exemplo n.º 13
0
    def test_retries_on_bad_data(self):
        """`AdInsights.run_job()` calls a `facebook_business` method,
        `get_insights()`, to make a request to the API. We mock this
        method to raise a `FacebookBadObjectError`

        We expect the tap to retry this request up to 5 times, which is
        the current hard coded `max_tries` value.
        """

        # Create the mock and force the function to throw an error
        mocked_account = Mock()
        mocked_account.get_insights = Mock()
        mocked_account.get_insights.side_effect = FacebookBadObjectError("Bad data to set object data")

        # Initialize the object and call `sync()`
        ad_creative_object = AdsInsights('', mocked_account, '', '', {}, {})
        with self.assertRaises(FacebookBadObjectError):
            ad_creative_object.run_job({})
        # 5 is the max tries specified in the tap
        self.assertEquals(5, mocked_account.get_insights.call_count )
Exemplo n.º 14
0
    def format_params(cls,
                      schema,
                      users,
                      is_raw=False,
                      app_ids=None,
                      pre_hashed=None,
                      session=None):
        hashed_users = []
        if schema in (cls.Schema.phone_hash, cls.Schema.email_hash,
                      cls.Schema.mobile_advertiser_id):
            for user in users:
                if schema == cls.Schema.email_hash:
                    user = user.strip(" \t\r\n\0\x0B.").lower()
                if isinstance(user, six.text_type) and not (
                        pre_hashed
                ) and schema != cls.Schema.mobile_advertiser_id:
                    user = user.encode('utf8')  # required for hashlib
                # for mobile_advertiser_id, don't hash it
                if pre_hashed or schema == cls.Schema.mobile_advertiser_id:
                    hashed_users.append(user)
                else:
                    hashed_users.append(hashlib.sha256(user).hexdigest())
        elif isinstance(schema, list):
            # SDK will support only single PII
            if not is_raw:
                raise FacebookBadObjectError(
                    "Please send single PIIs i.e. is_raw should be true. " +
                    "The combining of the keys will be done internally.", )
            # users is array of array
            for user in users:
                if len(schema) != len(user):
                    raise FacebookBadObjectError(
                        "Number of keys in each list in the data should " +
                        "match the number of keys specified in scheme", )
                    break

                # If the keys are already hashed then send as it is
                if pre_hashed:
                    hashed_users.append(user)
                else:
                    counter = 0
                    hashed_user = []
                    for key in user:
                        key = key.strip(" \t\r\n\0\x0B.").lower()
                        key = cls.normalize_key(schema[counter], str(key))
                        if schema[counter] not in (
                                cls.Schema.MultiKeySchema.extern_id,
                                cls.Schema.MultiKeySchema.appuid):
                            if isinstance(key, six.text_type):
                                key = key.encode('utf8')
                            key = hashlib.sha256(key).hexdigest()
                        counter = counter + 1
                        hashed_user.append(key)
                    hashed_users.append(hashed_user)

        payload = {
            'schema': schema,
            'is_raw': is_raw,
            'data': hashed_users or users,
        }

        if schema == cls.Schema.uid:
            if not app_ids:
                raise FacebookBadObjectError(
                    "Custom Audiences with type " + cls.Schema.uid +
                    "require at least one app_id", )

        if app_ids:
            payload['app_ids'] = app_ids

        params = {
            'payload': payload,
        }
        if session:
            params['session'] = session
        return params
Exemplo n.º 15
0
def warning(message):
    if apiconfig.ads_api_config['STRICT_MODE']:
        raise FacebookBadObjectError(message)
    else:
        warnings.warn(message)
Exemplo n.º 16
0
    def call(
        self,
        method,
        path,
        params=None,
        headers=None,
        files=None,
        url_override=None,
        api_version=None,
    ):
        """Makes an API call.
        Args:
            method: The HTTP method name (e.g. 'GET').
            path: A tuple of path tokens or a full URL string. A tuple will
                be translated to a url as follows:
                graph_url/tuple[0]/tuple[1]...
                It will be assumed that if the path is not a string, it will be
                iterable.
            params (optional): A mapping of request parameters where a key
                is the parameter name and its value is a string or an object
                which can be JSON-encoded.
            headers (optional): A mapping of request headers where a key is the
                header name and its value is the header value.
            files (optional): An optional mapping of file names to binary open
                file objects. These files will be attached to the request.
        Returns:
            A FacebookResponse object containing the response body, headers,
            http status, and summary of the call that was made.
        Raises:
            FacebookResponse.error() if the request failed.
        """
        if not params:
            params = {}
        if not headers:
            headers = {}
        if not files:
            files = {}

        api_version = api_version or self._api_version

        if api_version and not re.search('v[0-9]+\.[0-9]+', api_version):
            raise FacebookBadObjectError(
                'Please provide the API version in the following format: %s'
                % self.API_VERSION,
            )

        self._num_requests_attempted += 1

        if not isinstance(path, six.string_types):
            # Path is not a full path
            path = "/".join((
                url_override or self._session.GRAPH,
                api_version,
                '/'.join(map(str, path)),
            ))

        # Include api headers in http request
        headers = headers.copy()
        headers.update(FacebookAdsApi.HTTP_DEFAULT_HEADERS)

        if params:
            params = _top_level_param_json_encode(params)

        # Get request response and encapsulate it in a FacebookResponse
        if method in ('GET', 'DELETE'):
            response = self._session.requests.request(
                method,
                path,
                params=params,
                headers=headers,
                files=files,
                timeout=self._session.timeout
            )

        else:
            response = self._session.requests.request(
                method,
                path,
                data=params,
                headers=headers,
                files=files,
                timeout=self._session.timeout
            )
        if self._enable_debug_logger:
            import curlify
            print(curlify.to_curl(response.request))
        fb_response = FacebookResponse(
            body=response.text,
            headers=response.headers,
            http_status=response.status_code,
            call={
                'method': method,
                'path': path,
                'params': params,
                'headers': headers,
                'files': files,
            },
        )

        if fb_response.is_failure():
            raise fb_response.error()

        self._num_requests_succeeded += 1
        return fb_response
Exemplo n.º 17
0
 def assure_call(self):
     if not self._api:
         raise FacebookBadObjectError(
             'Api call cannot be made if api is not set')
Exemplo n.º 18
0
    def remote_create(
        self,
        batch=None,
        failure=None,
        files=None,
        params=None,
        success=None,
        api_version=None,
    ):
        """Creates the object by calling the API.
        Args:
            batch (optional): A FacebookAdsApiBatch object. If specified,
                the call will be added to the batch.
            params (optional): A mapping of request parameters where a key
                is the parameter name and its value is a string or an object
                which can be JSON-encoded.
            files (optional): An optional mapping of file names to binary open
                file objects. These files will be attached to the request.
            success (optional): A callback function which will be called with
                the FacebookResponse of this call if the call succeeded.
            failure (optional): A callback function which will be called with
                the FacebookResponse of this call if the call failed.
        Returns:
            self if not a batch call.
            the return value of batch.add if a batch call.
        """
        warning_message = "`remote_create` is being deprecated, please update your code with new function."
        logging.warning(warning_message)
        if self.get_id():
            raise FacebookBadObjectError(
                "This %s object was already created."
                % self.__class__.__name__,
            )
        if not 'get_endpoint' in dir(self):
            raise TypeError('Cannot create object of type %s.'
                            % self.__class__.__name__)

        params = {} if not params else params.copy()
        params.update(self.export_all_data())
        request = None
        if hasattr(self, 'api_create'):
            request = self.api_create(self.get_parent_id_assured(), pending=True)
        else:
            request = FacebookRequest(
                node_id=self.get_parent_id_assured(),
                method='POST',
                endpoint=self.get_endpoint(),
                api=self._api,
                target_class=self.__class__,
                response_parser=ObjectParser(
                    reuse_object=self
                ),
            )
        request.add_params(params)
        request.add_files(files)

        if batch is not None:

            def callback_success(response):
                self._set_data(response.json())
                self._clear_history()

                if success:
                    success(response)

            def callback_failure(response):
                if failure:
                    failure(response)

            return batch.add_request(
                request=request,
                success=callback_success,
                failure=callback_failure,
            )
        else:
            response = request.execute()
            self._set_data(response._json)
            self._clear_history()

            return self