예제 #1
0
def upload_image_asset(client, googleads_client, account, asset_name, path,
                       adgroups):
    """Upload image asset and assign to ad groups if given."""
    asset_service = Service_Class.get_asset_service(client)

    with open(path, 'rb') as image_handle:
        image_data = image_handle.read()

    # Construct media and upload image asset.
    image_asset = {
        'xsi_type': 'ImageAsset',
        'assetName': asset_name,
        'imageData': image_data,
    }

    operation = {'operator': 'ADD', 'operand': image_asset}

    asset = asset_service.mutate([operation])['value'][0]

    new_asset = {
        'id': asset['assetId'],
        'name': asset['assetName'],
        'type': 'IMAGE',
        'image_url': asset['fullSizeInfo']['imageUrl']
    }

    return _assign_new_asset_to_adgroups(client, googleads_client, account,
                                         new_asset, adgroups)
예제 #2
0
def upload_html5_asset(client, googleads_client, account, asset_name, path,
                       adgroups):
    """Upload html5 asset and assign to ad groups if given."""
    asset_service = Service_Class.get_asset_service(client)

    with open(path, 'rb') as html_handle:
        html_data = html_handle.read()

    media_bundle_asset = {
        'xsi_type': 'MediaBundleAsset',
        'assetName': asset_name,
        'mediaBundleData': html_data
    }
    operation = {'operator': 'ADD', 'operand': media_bundle_asset}

    asset = asset_service.mutate([operation])['value'][0]

    new_asset = {
        'id': asset['assetId'],
        'name': asset['assetName'],
        'type': 'MEDIA_BUNDLE',
    }

    return _assign_new_asset_to_adgroups(client, googleads_client, account,
                                         new_asset, adgroups)
예제 #3
0
def upload_yt_video_asset(client, googleads_client, account, asset_name, url,
                          adgroups):
    """Upload YT video asset and assign to ad groups if given."""
    asset_service = Service_Class.get_asset_service(client)

    url_data = urllib.parse.urlparse(url)
    if url_data.netloc == 'youtu.be':
        video_id = url_data.path.lstrip('/')
    else:
        query = urllib.parse.parse_qs(url_data.query)
        video_id = query['v'][0]

    vid_asset = {
        'xsi_type': 'YouTubeVideoAsset',
        'assetName': asset_name,
        'youTubeVideoId': video_id
    }

    operation = {'operator': 'ADD', 'operand': vid_asset}

    asset = asset_service.mutate([operation])['value'][0]

    new_asset = {
        'id': asset['assetId'],
        'name': asset['assetName'],
        'type': 'YOUTUBE_VIDEO',
        'video_id': video_id,
        'link': url,
        'image_url': yt_thumbnail_url % (video_id)
    }

    return _assign_new_asset_to_adgroups(client, googleads_client, account,
                                         new_asset, adgroups)
예제 #4
0
def _get_ad_id(client, adgroup):
    """gets the ad id from the adgroup id."""
    adgroupad_service = Service_Class.get_ad_group_ad_service(client)
    selector = {
        'fields': ['Id'],
        'predicates': [{
            'field': 'AdGroupId',
            'operator': 'EQUALS',
            'values': [adgroup]
        }],
    }

    page = adgroupad_service.get(selector)
    return page['entries'][0]['ad']['id']
예제 #5
0
def mutate_ad(client,
              account,
              adgroup,
              asset,
              action,
              text_type_to_assign='descriptions'):
    """Add or remove asset to a given adgroup's ad."""
    if action not in actions:
        raise ValueError('action not supported')

    client.SetClientCustomerId(account)

    asset_type_map = {
        'TEXT': 'TextAsset',
        'IMAGE': 'ImageAsset',
        'YOUTUBE_VIDEO': 'YouTubeVideoAsset',
        'MEDIA_BUNDLE': 'MediaBundleAsset'
    }
    property_map = {
        'TEXT': text_type_to_assign,
        'IMAGE': 'images',
        'YOUTUBE_VIDEO': 'videos',
        'MEDIA_BUNDLE': 'html5MediaBundles'
    }

    asset_type = asset_type_map[asset['type']]
    ad_property = property_map[asset['type']]

    # ad id is required to add/remove assets from the adgroup
    ad_id = _get_ad_id(client, adgroup)

    selector = {
        'fields': [
            'Id',
            'UniversalAppAdDescriptions',
            'UniversalAppAdHeadlines',
            'UniversalAppAdImages',
            'UniversalAppAdYouTubeVideos',
            'UniversalAppAdHtml5MediaBundles',
        ],
        'predicates': [{
            'field': 'Id',
            'operator': 'EQUALS',
            'values': [ad_id],
        }],
    }

    ad_service = Service_Class.get_ad_service(client)

    ad = ad_service.get(selector)['entries'][0]

    # Case 1: adding an asset to the ad
    if action == 'ADD':
        attached_asset = [{
            'asset': {
                'xsi_type': asset_type,
                'assetId': asset['id'],
            }
        }]

        # YT video assets require the video ID to assign to ad
        if asset_type == 'YouTubeVideoAsset':
            attached_asset[0]['asset']['youTubeVideoId'] = asset['video_id']

        # Text assets require the text itself to assign to ad
        if asset_type == 'TextAsset':
            attached_asset[0]['asset']['assetText'] = asset['asset_text']

        ad[ad_property] += attached_asset

    # Case 2: Removing an asset from the ad
    elif action == 'REMOVE':
        for item in ad[ad_property]:
            if item['asset']['assetId'] == asset['id']:
                ad[ad_property].remove(item)

    operations = [{
        'operator': 'SET',
        'operand': ad,
    }]

    ad_service.mutate(operations)
    Service_Class.reset_cid(client)
예제 #6
0
def upload_asset():
    """upload new asset to account and assign to specified adgroups.

  asset_type needs to be IMAGE,YOUTUBE_VIDEO,MEDIA_BUNDLE, descriptions,
  headlines
  """
    data = request.get_json(force=True)

    if data.get('account') is None or data.get('asset_type') is None:
        return _build_response(msg='invalid arguments', status=400)

    # uniform file names
    asset_name = data.get('asset_name')
    if asset_name and data.get('asset_type') == 'IMAGE':
        asset_name = asset_name.replace(' ', '_')
        for char in string.punctuation:
            if char not in ['_', '-', '.']:
                asset_name = asset_name.replace(char, '')

    try:
        result = upload(client,
                        googleads_client,
                        data.get('account'),
                        data.get('asset_type'),
                        asset_name,
                        asset_text=data.get('asset_text'),
                        path=UPLOAD_FOLDER / asset_name,
                        url=data.get('url'),
                        adgroups=data.get('adgroups'))
    except Exception as e:
        logging.exception(e)
        Service_Class.reset_cid(client)
        # Asset not uploaded
        print(str(e))
        return _build_response(msg=json.dumps({
            'msg':
            'Could not upload asset',
            'error_message':
            error_mapping(str(e)),
            'err':
            str(e)
        }),
                               status=400)

    Service_Class.reset_cid(client)

    # No adgroup assignment was requested, asset uploaded successfully
    if result['status'] == -1:
        return _build_response(msg=json.dumps({
            'msg': 'Asset Uploaded',
            'asset': result['asset']
        }),
                               status=200)

    # successfully uploaded and assigend to all ad groups
    if result['status'] == 0:
        return _build_response(msg=json.dumps(result), status=200)

    # successfully assigend only to some ad groups
    if result['status'] == 1:
        return _build_response(msg=json.dumps(result), status=206)

    # text asset couldn't assign to any ad group - therefore also not uploaded
    if result['status'] == 3:
        return _build_response(msg=json.dumps({
            'msg':
            'Text asset could not be assigned to any adgroup',
            'failures':
            result['unsuccessfull']
        }),
                               status=500)

    # asset uploaded but didn't assign to any ad groups
    elif result['status'] == 2:
        return _build_response(msg=json.dumps({
            'msg': 'could not assign asset',
            'asset': result['asset']
        }),
                               status=500)
예제 #7
0
def _text_asset_mutate(data, asset_id, asset_struct):
    """Handles text asset mutations"""

    asset_handlers = []
    index = 0  # to re-write back to location
    for entry in asset_struct:
        if entry['id'] == asset_id:
            asset_handlers.append({'asset': entry, 'index': index})
        index += 1

    # if only one of headlines/descriptions entries
    # exists in asset_struct, create the second one.
    # if the asset isn't assigned to any adgroup, create both entries
    # create headline entry only if text's len <= 30
    if len(asset_handlers) < 2:
        new_asset = {
            'id': data[0]['asset']['id'],
            'type': 'TEXT',
            'asset_text': data[0]['asset']['asset_text'],
            'adgroups': []
        }
        append = False
        if len(data[0]['asset']['asset_text']) <= 30:
            headline_len = True
        else:
            headline_len = False

        if len(asset_handlers) == 1:
            existing_type = asset_handlers[0]['asset']['text_type']
            if existing_type == 'headlines':
                new_asset['text_type'] = 'descriptions'
                append = True
            elif headline_len:
                new_asset['text_type'] = 'headlines'
                append = True
            if append:
                asset_handlers.append({'asset': new_asset, 'index': None})

        elif len(asset_handlers) == 0:
            new_asset['text_type'] = 'descriptions'
            asset_handlers.append({'asset': new_asset, 'index': None})
            if headline_len:
                new_asset_second = copy.copy(new_asset)
                new_asset_second['adgroups'] = []
                new_asset_second['text_type'] = 'headlines'
                asset_handlers.append({
                    'asset': new_asset_second,
                    'index': None
                })

    successeful_assign = []
    failed_assign = []
    for item in data:
        account = item['account']
        adgroup = item['adgroup']
        action = item['action']
        asset = item['asset']
        text_type_to_assign = item['asset']['text_type_to_assign']

        try:
            mutation = mutate_ad(client, account, adgroup, asset, action,
                                 text_type_to_assign)

        except Exception as e:
            failed_assign.append({
                'adgroup': adgroup,
                'error_message': error_mapping(str(e)),
                'err': str(e)
            })
            mutation = 'failed'
            logging.error('could not execute mutation on adgroup: ' +
                          str(adgroup) + str(e))

        if mutation is None:
            for obj in asset_handlers:
                if obj['asset']['text_type'] == text_type_to_assign:
                    obj['asset'] = _asset_ag_update(obj['asset'], adgroup,
                                                    action)
                    successeful_assign.append(adgroup)

    Service_Class.reset_cid(client)

    for obj in asset_handlers:
        if obj['index']:
            asset_struct[obj['index']] = obj['asset']
        else:
            asset_struct.append(obj['asset'])

    with open(asset_to_ag_json_path, 'w') as f:
        json.dump(asset_struct, f, indent=2)

    if failed_assign and successeful_assign:
        status = 206
    elif successeful_assign:
        status = 200
    else:
        status = 500

    logging.info('mutate response: msg={} , status={}'.format(
        str(asset_handlers), index))
    # switch to this return and tell Mariam the changed return type.
    return _build_response(msg=json.dumps([{
        'asset': asset_handlers,
        'failures': failed_assign
    }]),
                           status=status)
예제 #8
0
def mutate():
    """Assign or remove an asset from adgroups.

  gets a json file with a list of asset, account, adgourp and action.
  preforms all of the actions one by one.

  returns a list withthe new asset objects with the changed adgroups list.
  if its a text asset, returns a list with
  both 'headlines' and 'descriptions' entries.
  also changes the asset_to_ag.json file.
  """

    data = request.get_json(force=True)
    logging.info('Recived mutate request: ' + str(data))
    asset_id = data[0]['asset']['id']
    asset_type = data[0]['asset']['type']

    with open(asset_to_ag_json_path, 'r') as f:
        asset_struct = json.load(f)

    # special func for text assets, as they have 2 entries in asset_to_ag.json
    if asset_type == 'TEXT':
        return _text_asset_mutate(data, asset_id, asset_struct)

    asset_handler = {}
    index = 0  # to re-write back to location
    for entry in asset_struct:
        if entry['id'] == asset_id:
            asset_handler = entry
            break
        index += 1

    if not asset_handler:
        asset_handler = data[0]['asset']
        asset_handler['adgroups'] = []
        index = None

    failed_assign = []
    successeful_assign = []
    for item in data:
        account = item['account']
        adgroup = item['adgroup']
        action = item['action']
        asset = item['asset']

        try:
            mutation = mutate_ad(client, account, adgroup, asset, action)
        except Exception as e:
            failed_assign.append({
                'adgroup': adgroup,
                'error_message': error_mapping(str(e)),
                'err': str(e)
            })
            mutation = 'failed'
            logging.error('could not execute mutation on adgroup: ' +
                          str(adgroup))

        if mutation is None:
            successeful_assign.append(adgroup)
            asset_handler = _asset_ag_update(asset_handler, adgroup, action)

    Service_Class.reset_cid(client)

    if index:
        asset_struct[index] = asset_handler
    else:
        asset_struct.append(asset_handler)

    with open(asset_to_ag_json_path, 'w') as f:
        json.dump(asset_struct, f, indent=2)

    if failed_assign and successeful_assign:
        status = 206
    elif successeful_assign:
        status = 200
    else:
        status = 500

    logging.info('mutate response: msg={} , status={}'.format(
        asset_handler, index))

    return _build_response(msg=json.dumps([{
        'asset': asset_handler,
        'index': index,
        'failures': failed_assign
    }]),
                           status=status)
예제 #9
0
        config_file = yaml.load(f, Loader=yaml.FullLoader)
except FileNotFoundError:
    config_file = {'config_valid': 0}

if config_file['config_valid']:
    setup.set_api_configs()
    client = adwords.AdWordsClient.LoadFromStorage(CONFIG_PATH /
                                                   'googleads.yaml')
    googleads_client = GoogleAdsClient.load_from_storage(CONFIG_PATH /
                                                         'google-ads.yaml')
    try:
        structure.create_mcc_struct(googleads_client, account_struct_json_path,
                                    asset_to_ag_json_path)
    except Exception as e:
        logging.exception('Error when trying to create struct')
        Service_Class.reset_cid(client)


@server.route('/')
def upload_frontend():
    return render_template('index.html')


@server.route('/config/', methods=['GET'])
def get_configs():
    """return all config parameters"""
    try:
        with open(CONFIG_FILE_PATH, 'r') as fi:
            config = yaml.load(fi, Loader=yaml.FullLoader)
    except FileNotFoundError:
        config = {
예제 #10
0
파일: main.py 프로젝트: google/assetMG
except FileNotFoundError:
    config_file = {'config_valid': 0}


if config_file['config_valid']:
    try:
        setup.download_file_from_gcs('account_struct.json', account_struct_json_path)
        setup.download_file_from_gcs('asset_to_ag.json', asset_to_ag_json_path)
        if CLOUD_VERSION and Path(account_struct_json_path).exists():
            get_global_googleads_client() # to initialize googleads.yaml file
            logging.info('Skipping structure creation on startup, since this is the cloud version')
        else:
            structure.create_mcc_struct(get_global_googleads_client(), account_struct_json_path, asset_to_ag_json_path)
    except Exception as e:
        logging.exception('Error when trying to create struct')
        Service_Class.reset_cid(get_global_adwords_client())


@app.route('/')
def upload_frontend():
    return render_template('index.html')


@app.route('/config/', methods=['GET'])
def get_configs():
    """return all config parameters"""
    try:
        setup.download_file_from_gcs(CONFIG_FILE_PATH_GS, CONFIG_FILE_PATH)
        with open(CONFIG_FILE_PATH, 'r') as fi:
            config = yaml.load(fi, Loader=yaml.FullLoader)
    except FileNotFoundError: