#!/usr/bin/python # # Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """Methods to access AdService service.""" __author__ = 'api.sgrinberg@gmail.com (Stan Grinberg)' from aw_api import SanityCheck as glob_sanity_check from aw_api import SOAPPY from aw_api import ZSI from aw_api.Errors import ValidationError from aw_api.WebService import WebService class AdService(object): """Wrapper for AdService. The Ad Service provides operations for accessing, creating and modifying Ads in an AdGroup. """ def __init__(self, headers, config, op_config, lock, logger): """Inits AdService. Args: headers: dict dictionary object with populated authentication credentials. config: dict dictionary object with populated configuration values. op_config: dict dictionary object with additional configuration values for this operation. lock: thread.lock the thread lock. logger: Logger the instance of Logger """ url = [op_config['server'], 'api/adwords', op_config['version'], self.__class__.__name__] if config['access']: url.insert(len(url) - 1, config['access']) self.__service = WebService(headers, config, op_config, '/'.join(url), lock, logger) self.__config = config if self.__config['soap_lib'] == SOAPPY: from aw_api.soappy_toolkit import SanityCheck elif self.__config['soap_lib'] == ZSI: from aw_api import API_VERSIONS from aw_api.zsi_toolkit import SanityCheck if op_config['version'] in API_VERSIONS: module = '%s_services' % self.__class__.__name__ try: web_services = __import__('aw_api.zsi_toolkit.%s.%s' % (op_config['version'], module), globals(), locals(), ['']) except ImportError, e: # If one of library's required modules is missing, re raise exception. if str(e).find(module) < 0: raise ImportError(e) msg = ('The version \'%s\' is not compatible with \'%s\'.' % (op_config['version'], self.__class__.__name__)) raise ValidationError(msg) else: msg = 'Invalid API version, not one of %s.' % str(list(API_VERSIONS)) raise ValidationError(msg) self.__web_services = web_services self.__loc = eval('web_services.%sLocator()' % self.__class__.__name__) self.__sanity_check = SanityCheck def AddAds(self, ads): """Make a batch of new Ads. Args: ads: list new data for the Ads. Ex: ads = [ { 'adGroupId': '1234567890', 'adType': 'CommerceAd' or 'ImageAd' or 'LocalBusinessAd' or 'MobileAd' or 'TextAd' or 'VideoAd' or 'MobileImageAd', 'destinationUrl': 'http://www.example.com', 'displayUrl': 'example.com', 'exemptionRequest': 'Exemption request goes here.', 'status': 'Enabled', # CommerceAd only 'description1': 'Choose from our delicious range now', 'description2': 'Pre-order or delivered to your door', 'headline': 'Pizza Place', 'postPriceAnnotation': '', 'prePriceAnnotation': '', 'priceString': '$2-$8', 'productImage': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' } # ImageAd only 'image': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' } # LocalBusinessAd only 'address': 'A, 81, Old Church Road', 'businessImage': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'businessKey': '...', 'businessName': 'Pizza Place', 'city': 'London', 'countryCode': 'GB', 'customIcon': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'customIconId': '1234567890', 'description1': 'Choose from our delicious range now', 'description2': 'Pre-order or delivered to your door', 'fullBusinessName': 'Pizza Place', 'latitude': '...', 'longitude': '...', 'phoneNumber': '...', 'postalCode': 'E4 6ST', 'region': 'GB-ENG', 'stockIcon': 'Standard_5', 'targetRadiusInKm': '16.09344' # MobileAd only 'businessName': 'Pizza Place', 'countryCode': 'GB', 'description': 'Get it now', 'headline': 'Great Pizza', 'markupLanguages': ['HTML'], 'mobileCarriers': ['O2@GB'], 'phoneNumber': '...' # TextAd only 'description1': 'Visit the Red Planet in style.', 'description2': 'Low-gravity fun for everyone!', 'headline': 'Luxury Cruise to Mars' # VideoAd only 'image': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'name': 'Test Video', 'video': { 'duration': '10000000', 'filename': 'test_video', 'preview': 'http://www.example.com/path_to_video_preview', 'title': 'Test Video', 'videoId': '1234567890' } # MobileImageAd only 'image': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'markupLanguages': ['HTML'], 'mobileCarriers': ['O2@GB'] } ] Returns: tuple response from the API method. """ glob_sanity_check.ValidateTypes(((ads, list),)) method_name = 'addAds' if self.__config['soap_lib'] == SOAPPY: for item in ads: self.__sanity_check.ValidateAdV13(item) return self.__service.CallMethod(method_name, (ads)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services new_ads = [] for item in ads: new_ads.append(self.__sanity_check.ValidateAd('', item, web_services)) request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'ads': new_ads},)), 'Ad', self.__loc, request) def CheckAds(self, ads, language_target, geo_target): """Check a batch of Ads for policy errors. Args: ads: list Ads to be checked. language_target: list overriding language context. geo_target: dict overriding geographical context. Ex: ads = [ { 'adGroupId': '1234567890', 'adType': 'CommerceAd' or 'ImageAd' or 'LocalBusinessAd' or 'MobileAd' or 'TextAd' or 'VideoAd' or 'MobileImageAd', 'destinationUrl': 'http://www.example.com', 'displayUrl': 'example.com', 'exemptionRequest': 'Exemption request goes here.', 'status': 'Enabled', # CommerceAd only 'description1': 'Choose from our delicious range now', 'description2': 'Pre-order or delivered to your door', 'headline': 'Pizza Place', 'postPriceAnnotation': '', 'prePriceAnnotation': '', 'priceString': '$2-$8', 'productImage': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' } # ImageAd only 'image': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' } # LocalBusinessAd only 'address': 'A, 81, Old Church Road', 'businessImage': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'businessKey': '...', 'businessName': 'Pizza Place', 'city': 'London', 'countryCode': 'GB', 'customIcon': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'customIconId': '1234567890', 'description1': 'Choose from our delicious range now', 'description2': 'Pre-order or delivered to your door', 'fullBusinessName': 'Pizza Place', 'latitude': '...', 'longitude': '...', 'phoneNumber': '...', 'postalCode': 'E4 6ST', 'region': 'GB-ENG', 'stockIcon': 'Standard_5', 'targetRadiusInKm': '16.09344' # MobileAd only 'businessName': 'Pizza Place', 'countryCode': 'GB', 'description': 'Get it now', 'headline': 'Great Pizza', 'markupLanguages': ['HTML'], 'mobileCarriers': ['O2@GB'], 'phoneNumber': '...' # TextAd only 'description1': 'Visit the Red Planet in style.', 'description2': 'Low-gravity fun for everyone!', 'headline': 'Luxury Cruise to Mars' # VideoAd only 'image': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'name': 'Test Video', 'video': { 'duration': '10000000', 'filename': 'test_video', 'preview': 'http://www.example.com/path_to_video_preview', 'title': 'Test Video', 'videoId': '1234567890' } # MobileImageAd only 'image': { 'data': '...', 'height': '250', 'imageUrl': 'http://www.example.com/path_to_image', 'mimeType': 'image/jpeg', 'name': 'Test Image', 'shrunkenUrl': 'http://www.example.com/path_to_shrunken_image', 'thumbnailUrl': 'http://www.example.com/path_to_thumb_image', 'type': 'image', 'width': '300' }, 'markupLanguages': ['HTML'], 'mobileCarriers': ['O2@GB'] } ] language_target = ['en'] 'geoTargeting': { 'cityTargets': { 'cities': ['New York, NY US'], 'excludedCities': ['New York, NY US'] }, 'countryTargets': { 'countries': ['US'], 'excludedCountries': ['US'] }, 'metroTargets': { 'excludedMetros': ['501'], 'metros': ['501'] }, 'regionTargets': { 'excludedRegions': ['US-NY'], 'regions': ['US-NY'] } 'proximityTargets': { 'circles': [ { 'latitudeMicroDegrees': '12345678', 'longitudeMicroDegrees': '-12345678', 'radiusMeters': '1000' } ] }, 'targetAll': 'False' }, Returns: tuple response from the API method. """ glob_sanity_check.ValidateTypes(((ads, list), (language_target, list), (geo_target, dict))) method_name = 'checkAds' if self.__config['soap_lib'] == SOAPPY: for item in ads: self.__sanity_check.ValidateAdV13(item) language_target = self.__sanity_check.UnType( self.__sanity_check.ValidateLanguageTargetV13(language_target)) geo_target = self.__sanity_check.UnType( self.__sanity_check.ValidateGeoTargetV13(geo_target)) return self.__service.CallMethod(method_name, (ads, language_target, geo_target)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services new_ads = [] for item in ads: new_ads.append(self.__sanity_check.ValidateAd('', item, web_services)) language_target = self.__sanity_check.ValidateLanguageTargetV13( language_target) geo_target = self.__sanity_check.ValidateGeoTargetV13(geo_target) request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'ads': new_ads}, {'languageTarget': language_target}, {'geoTarget': geo_target})), 'Ad', self.__loc, request) def FindBusinesses(self, name, address, country_code): """Searches for businesses with similar attributes. Args: name: str name of the business. address: str location of the business. country_code: str two letter country code of the business address. Ex: name = 'Pizza Place' address = 'A, 81, Old Church Road' country_code = 'GB' Returns: tuple response from the API method. """ glob_sanity_check.ValidateTypes(((name, (str, unicode)), (address, (str, unicode)), (country_code, (str, unicode)))) method_name = 'findBusinesses' if self.__config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, (name, address, country_code)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'name': name}, {'address': address}, {'countryCode': country_code})), 'Ad', self.__loc, request) def GetActiveAds(self, ad_group_ids): """Return all active Ads associated with the list of AdGroup ids specified. Args: ad_group_ids: list array of AdGroup Ids that own the Ads. Ex: ad_group_ids = ['1234567890'] Returns: tuple response from the API method. """ glob_sanity_check.ValidateTypes(((ad_group_ids, list),)) for item in ad_group_ids: glob_sanity_check.ValidateTypes(((item, (str, unicode)),)) method_name = 'getActiveAds' if self.__config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, (ad_group_ids)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'adGroupIds': ad_group_ids},)), 'Ad', self.__loc, request) def GetAd(self, ad_group_id, ad_id): """Return information about one Ad. Args: ad_group_id: str ID of the AdGroup owning the Ad. ad_id: str ID of the Ad. Ex: ad_group_id = '1234567890' ad_id = '1234567890' Returns: tuple response from the API method. """ glob_sanity_check.ValidateTypes(((ad_group_id, (str, unicode)), (ad_id, (str, unicode)))) method_name = 'getAd' if self.__config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, (ad_group_id, ad_id)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'adGroupId': ad_group_id}, {'adId': ad_id})), 'Ad', self.__loc, request) def GetAdStats(self, ad_group_id, ad_ids, start_day, end_day): """Get statistics for a list of ads in an ad group. Args: ad_group_id: str ad group that contains the ads to be queried. ad_ids: list ads to query. start_day: str starting day of the period for which statistics are to be collected. end_dat: str ending day of the period for which statistics are to be collected, inclusive. Ex: ad_group_id = '1234567890' ad_ids = ['1234567890'] start_day = '2008-01-01' end_day = '2008-01-31' Returns: tuple response from the API method. """ glob_sanity_check.ValidateTypes(((ad_group_id, (str, unicode)), (ad_ids, list), (start_day, (str, unicode)), (end_day, (str, unicode)))) for item in ad_ids: glob_sanity_check.ValidateTypes(((item, (str, unicode)),)) method_name = 'getAdStats' if self.__config['soap_lib'] == SOAPPY: return self.__service.CallMethod( method_name, (ad_group_id, ad_ids, start_day, end_day)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'adGroupId': ad_group_id}, {'adIds': ad_ids}, {'startDay': start_day}, {'endDay': end_day})), 'Ad', self.__loc, request) def GetAllAds(self, ad_group_ids): """Return all Ads associated with the list of AdGroup ids specified. Args: ad_group_ids: list array of AdGroup Ids that own the Ads. Ex: ad_group_ids = ['1234567890'] Returns: tuple response from the API method. """ glob_sanity_check.ValidateTypes(((ad_group_ids, list),)) for item in ad_group_ids: glob_sanity_check.ValidateTypes(((item, (str, unicode)),)) method_name = 'getAllAds' if self.__config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, (ad_group_ids)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (({'adGroupIds': ad_group_ids},)), 'Ad', self.__loc, request) def GetMyBusinesses(self): """Returns the list of businesses registered to the user. Businesses are registered in the Local Business Center. Returns: tuple response from the API method. """ method_name = 'getMyBusinesses' if self.__config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Ad', self.__loc, request) def GetMyVideos(self): """Returns a list of all videos stored under the users account. Returns: tuple response from the API method. """ method_name = 'getMyVideos' if self.__config['soap_lib'] == SOAPPY: return self.__service.CallMethod(method_name, ()) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services request = eval('web_services.%sRequest()' % method_name) return self.__service.CallMethod(method_name, (), 'Ad', self.__loc, request) def UpdateAds(self, ads): """Update a batch of ads. Args: ads: list ads to be updated. Ex: ads = [ { 'adGroupId': '1234567890', 'adType': 'CommerceAd' or 'ImageAd' or 'LocalBusinessAd' or 'MobileAd' or 'TextAd' or 'VideoAd' or 'MobileImageAd', 'destinationUrl': 'http://www.example.com', 'displayUrl': 'example.com', 'exemptionRequest': 'Exemption request goes here.', 'id': '1234567890', 'status': 'Enabled', } ] """ glob_sanity_check.ValidateTypes(((ads, list),)) method_name = 'updateAds' if self.__config['soap_lib'] == SOAPPY: for item in ads: self.__sanity_check.ValidateAdV13(item) self.__service.CallMethod(method_name, (ads)) elif self.__config['soap_lib'] == ZSI: web_services = self.__web_services new_ads = [] for item in ads: new_ads.append(self.__sanity_check.ValidateAd('', item, web_services)) request = eval('web_services.%sRequest()' % method_name) self.__service.CallMethod(method_name, (({'ads': new_ads},)), 'Ad', self.__loc, request)