python-amazon-ad-api 1.1.4__py3-none-any.whl → 1.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ad_api/api/sb/__init__.py CHANGED
@@ -22,6 +22,8 @@ from .benchmarks import Benchmarks
22
22
  from .themes import Themes
23
23
  from .budget_rules import BudgetRules
24
24
  from .optimization_rules import OptimizationRules
25
+ from .targeting_categories import TargetingCategories
26
+ from .migration import Migration
25
27
 
26
28
  __all__ = [
27
29
  "Brands",
@@ -48,4 +50,6 @@ __all__ = [
48
50
  "Themes",
49
51
  "BudgetRules",
50
52
  "OptimizationRules",
53
+ "TargetingCategories",
54
+ "Migration",
51
55
  ]
@@ -0,0 +1,154 @@
1
+ from ad_api.base import Client, sp_endpoint, ApiResponse, Utils
2
+
3
+
4
+ class Migration(Client):
5
+ """Sponsored Brands legacy campaign migration from 3.0 to 4.0.
6
+
7
+ Documentation: https://advertising.amazon.com/API/docs/en-us/sponsored-brands/4-0/openapi
8
+
9
+ Two media types live in this family, both versioned and both required:
10
+
11
+ ======================================== ==========================================================
12
+ Route Media type
13
+ ======================================== ==========================================================
14
+ ``/sb/v4/legacyCampaigns/*`` ``vnd.SponsoredBrands.SponsoredBrandsMigrationApi.v4``
15
+ ``/sb/v4/migrations/list`` ``vnd.sbAdMigration.v4``
16
+ ======================================== ==========================================================
17
+
18
+ .. warning::
19
+ ``list_migrations`` answers ``404 Could not find resource`` when sent
20
+ ``application/json``, the same behaviour already recorded for forecasts,
21
+ budget recommendations and optimization rules. Amazon routes on media
22
+ type; a wrong one reads exactly like a missing route.
23
+
24
+ .. note::
25
+ ``migration_job_status`` and ``migration_job_results`` answer ``500
26
+ INTERNAL_ERROR`` for a job identifier that does not exist, instead of a
27
+ 400 or a 404. The routes are live; the service trips over an unknown id.
28
+ """
29
+
30
+ _MEDIA_LEGACY = ('application/vnd.SponsoredBrands.'
31
+ 'SponsoredBrandsMigrationApi.v4+json')
32
+ _MEDIA_ADMIG = 'application/vnd.sbAdMigration.v4+json'
33
+
34
+ @sp_endpoint('/sb/v4/legacyCampaigns/migrationJob', method='POST')
35
+ def start_migration_job(self, campaignIds, enableThemeTargeting, **kwargs) -> ApiResponse:
36
+ """
37
+ Starts a migration job for 3.0 campaigns.
38
+
39
+ Request Body [Required]
40
+ | **campaignIds** (array of string): [required] Campaigns to migrate.
41
+ | **enableThemeTargeting** (boolean): [required] Carry theme targets
42
+ into the new campaigns.
43
+ | **isStagedMigration** (boolean): [optional]
44
+ | **newCampaignState** (string): [optional] State for the migrated
45
+ campaigns.
46
+ | **brandEntityId** (string): [optional]
47
+
48
+ Returns:
49
+ | ApiResponse, ``200``.
50
+
51
+ .. warning::
52
+ **This changes the account.** It starts a real migration of 3.0
53
+ campaigns into 4.0, which is not something a probe or a retry should
54
+ trigger by accident. Because of that it was never exercised here: the
55
+ shape comes from the contract only.
56
+
57
+ .. note::
58
+ Do not treat a timeout as a failed migration. The job may be running;
59
+ check ``migration_job_status`` before trying again.
60
+ """
61
+ body = {'campaignIds': campaignIds, 'enableThemeTargeting': enableThemeTargeting}
62
+ for key in ('isStagedMigration', 'newCampaignState', 'brandEntityId'):
63
+ if key in kwargs:
64
+ body[key] = kwargs.pop(key)
65
+ return self._request(kwargs.pop('path'), data=Utils.convert_body(body, False),
66
+ params=kwargs, headers={'Accept': self._MEDIA_LEGACY,
67
+ 'Content-Type': self._MEDIA_LEGACY})
68
+
69
+ @sp_endpoint('/sb/v4/legacyCampaigns/migrationJob/status', method='POST')
70
+ def migration_job_status(self, jobId, **kwargs) -> ApiResponse:
71
+ """
72
+ Gets the status of a migration job.
73
+
74
+ Request Body [Required]
75
+ | **jobId** (string): [required]
76
+
77
+ Returns:
78
+ | ApiResponse, ``200``.
79
+
80
+ .. note::
81
+ An unknown ``jobId`` answers ``500 INTERNAL_ERROR`` rather than a
82
+ business error. Not a sign that the route is missing.
83
+ """
84
+ return self._request(kwargs.pop('path'),
85
+ data=Utils.convert_body({'jobId': jobId}, False),
86
+ params=kwargs, headers={'Accept': self._MEDIA_LEGACY,
87
+ 'Content-Type': self._MEDIA_LEGACY})
88
+
89
+ @sp_endpoint('/sb/v4/legacyCampaigns/migrationJob/results', method='POST')
90
+ def migration_job_results(self, jobId, **kwargs) -> ApiResponse:
91
+ """
92
+ Lists the per-campaign results of a migration job.
93
+
94
+ Request Body [Required]
95
+ | **jobId** (string): [required]
96
+ | **nextToken** (string): [optional]
97
+
98
+ Returns:
99
+ | ApiResponse, ``200``.
100
+
101
+ .. note::
102
+ An unknown ``jobId`` answers ``500 INTERNAL_ERROR``, as with the
103
+ status route.
104
+ """
105
+ return self._request(kwargs.pop('path'),
106
+ data=Utils.convert_body({'jobId': jobId}, False),
107
+ params=kwargs, headers={'Accept': self._MEDIA_LEGACY,
108
+ 'Content-Type': self._MEDIA_LEGACY})
109
+
110
+ @sp_endpoint('/sb/v4/legacyCampaigns/overallMigrationResults', method='POST')
111
+ def overall_migration_results(self, **kwargs) -> ApiResponse:
112
+ """
113
+ Lists every campaign migration result for the advertiser.
114
+
115
+ Request Body [Optional]
116
+ | **nextToken** (string): [optional]
117
+
118
+ Returns:
119
+ | ApiResponse, ``200``. With nothing migrated yet the service answers
120
+ an empty object rather than a list, so do not assume a ``migrations``
121
+ key is always present.
122
+ """
123
+ return self._request(kwargs.pop('path'),
124
+ data=Utils.convert_body(kwargs.pop('body', {}), False),
125
+ params=kwargs, headers={'Accept': self._MEDIA_LEGACY,
126
+ 'Content-Type': self._MEDIA_LEGACY})
127
+
128
+ @sp_endpoint('/sb/v4/migrations/list', method='POST')
129
+ def list_migrations(self, **kwargs) -> ApiResponse:
130
+ """
131
+ Lists ad migrations.
132
+
133
+ Request Body [Optional]
134
+ | **adGroupIdFilter** ({}): ``include``, a list of ad group ids.
135
+ | **adIdFilter** ({}): ``include``, a list of ad ids.
136
+ | **campaignIdFilter** ({}): ``include``, a list of campaign ids.
137
+ | **adStateFilter** ({}): ``include`` with ``ENABLED``, ``PAUSED``
138
+ or ``ARCHIVED``.
139
+ | **migrationStatusFilter** ({}): ``include`` with ``ACCEPTED``,
140
+ ``ELIGIBLE``, ``MIGRATED`` or ``MIGRATION_FAILED``.
141
+ | **maxResults** (number): Page size.
142
+ | **nextToken** (string): Pagination token.
143
+
144
+ Returns:
145
+ | ApiResponse, ``200``, with ``migrations`` and ``totalCount``.
146
+
147
+ Example::
148
+
149
+ {"migrations": [], "totalCount": 0}
150
+ """
151
+ return self._request(kwargs.pop('path'),
152
+ data=Utils.convert_body(kwargs.pop('body', {}), False),
153
+ params=kwargs, headers={'Accept': self._MEDIA_ADMIG,
154
+ 'Content-Type': self._MEDIA_ADMIG})
@@ -0,0 +1,98 @@
1
+ from ad_api.base import Client, sp_endpoint, fill_query_params, ApiResponse, Utils
2
+
3
+
4
+ class TargetingCategories(Client):
5
+ """Sponsored Brands targetable categories.
6
+
7
+ Documentation: https://advertising.amazon.com/API/docs/en-us/sponsored-brands/4-0/openapi
8
+
9
+ All three routes are reads, and all three were probed against a live account.
10
+
11
+ .. note::
12
+ ``list_categories`` returns the tree under ``categoryTree``, which the
13
+ contract does not name. Each node carries ``categoryRefinementId``,
14
+ ``name``, ``parentCategoryRefinementId``, ``isTargetable`` and, where the
15
+ service knows it, ``asinCountRange`` with a ``min`` and ``max``.
16
+
17
+ .. note::
18
+ Large root categories come back with ``isTargetable`` set to ``false``:
19
+ they have to be narrowed down to their children before they can be used
20
+ as a target.
21
+ """
22
+
23
+ _MEDIA = 'application/vnd.sbtargeting.v4+json'
24
+
25
+ @sp_endpoint('/sb/targets/categories', method='GET')
26
+ def list_categories(self, supplySource, **kwargs) -> ApiResponse:
27
+ """
28
+ Gets the targetable category tree.
29
+
30
+ Keyword Args
31
+ | query **supplySource** (string): [required] ``AMAZON`` or
32
+ ``STREAMING_VIDEO``.
33
+ | query **locale** (string): [optional] Market locale, for instance
34
+ ``es_ES``, ``en_US`` or ``de_DE``.
35
+ | query **includeOnlyRootCategories** (boolean): [optional] Return
36
+ root categories only.
37
+ | query **parentCategoryRefinementId** (string): [optional] Return
38
+ the children of that category.
39
+ | query **nextToken** (string): [optional] Pagination token.
40
+
41
+ Returns:
42
+ | ApiResponse, ``200``, with the tree under ``categoryTree``.
43
+
44
+ Example node::
45
+
46
+ {"categoryRefinementId": "1909624031", "name": "Chupetes",
47
+ "asinCountRange": {"min": 1247, "max": 2078},
48
+ "isTargetable": true, "parentCategoryRefinementId": "1862267031"}
49
+ """
50
+ return self._request(kwargs.pop('path'), params=kwargs,
51
+ headers={'Accept': self._MEDIA, 'Content-Type': self._MEDIA})
52
+
53
+ @sp_endpoint('/sb/targets/categories/{}/refinements', method='GET')
54
+ def list_refinements(self, categoryRefinementId, **kwargs) -> ApiResponse:
55
+ """
56
+ Gets the refinements of a category.
57
+
58
+ Keyword Args
59
+ | path **categoryRefinementId** (string): [required]
60
+ | query **locale** (string): [optional]
61
+ | query **nextToken** (string): [optional]
62
+
63
+ Returns:
64
+ | ApiResponse, ``200``.
65
+ """
66
+ return self._request(fill_query_params(kwargs.pop('path'), categoryRefinementId),
67
+ params=kwargs,
68
+ headers={'Accept': self._MEDIA, 'Content-Type': self._MEDIA})
69
+
70
+ @sp_endpoint('/sb/targets/products/count', method='POST')
71
+ def get_product_count(self, category, **kwargs) -> ApiResponse:
72
+ """
73
+ Gets the number of products in a category.
74
+
75
+ Request Body [Required]
76
+ | **category** (string): [required] Category refinement identifier.
77
+ | **ageRanges** (array of string): [optional]
78
+ | **brands** (array of string): [optional]
79
+ | **genres** (array of string): [optional]
80
+ | **isPrimeShipping** (boolean): [optional]
81
+ | **ratingRange** ({}): [optional] ``min`` and ``max`` integers.
82
+ | **priceRange** ({}): [optional] ``min`` and ``max`` numbers.
83
+
84
+ Returns:
85
+ | ApiResponse, ``200``.
86
+
87
+ Raises:
88
+ | ``400`` when ``category`` is missing, naming that member.
89
+ """
90
+ body = {'category': category}
91
+ for key in ('ageRanges', 'brands', 'genres', 'isPrimeShipping',
92
+ 'ratingRange', 'priceRange'):
93
+ if key in kwargs:
94
+ body[key] = kwargs.pop(key)
95
+ return self._request(kwargs.pop('path'),
96
+ data=Utils.convert_body(body, False),
97
+ params=kwargs, headers={'Accept': self._MEDIA,
98
+ 'Content-Type': self._MEDIA})
ad_api/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.1.4"
1
+ __version__ = "1.1.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-amazon-ad-api
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: Python wrapper for the Amazon Advertising API
5
5
  Home-page: https://github.com/denisneuf/python-amazon-ad-api
6
6
  Author: Daniel Alvaro
@@ -1,5 +1,5 @@
1
1
  ad_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ad_api/version.py,sha256=XxXhu8-QnuD9hA8Ah0WX5rgpt_DwOQmAwcK-FtpngyQ,22
2
+ ad_api/version.py,sha256=fe1i7ytQ67cmOi4fKSpfB74e36dkRgec1zdg71uuRFM,22
3
3
  ad_api/api/__init__.py,sha256=E9c0ZyK4vplMlKKEBF8CV5u7P4uSkTMSQsAuSQu5bfY,1514
4
4
  ad_api/api/account.py,sha256=CmOvCfHoPWaeTUJvtB0pNoNulZVSz4oMMCKIpVTpHos,2931
5
5
  ad_api/api/advertising_test_account.py,sha256=btOVbdk2tBDmyKSJaQbcIFyzNG8KFJVUmTe4p9xMhpU,1507
@@ -24,7 +24,7 @@ ad_api/api/reports.py,sha256=gNmDHLULD2uaJgsMpIbCX9kabwhMXRr9WfZBtHUdsOY,6043
24
24
  ad_api/api/stores.py,sha256=7W3RJKZyZOQ3nHzInvK9FDhWTXCH6Xe9a6dCBatEZ-o,2484
25
25
  ad_api/api/stream.py,sha256=JpnF8SKniOAHiEnEq1RPrh6tNy-Q2-8GPBTmnlxEXqQ,2989
26
26
  ad_api/api/validation_configurations.py,sha256=V3o06uWGwvA_Ly1x2lAiCk35Wo7DC_k8AtHgMdpm6wU,3830
27
- ad_api/api/sb/__init__.py,sha256=-rseg1GpT0FaUL_s4c0HTU1OvriGccqVybrNV2oPqws,1407
27
+ ad_api/api/sb/__init__.py,sha256=APbCAkWM1gzYuhpEUU98XDIKJKJx1NXALjLBXA9zdBU,1538
28
28
  ad_api/api/sb/ad_creatives_v4.py,sha256=86sF2lhSc3AC6d4P5TXyEnJMBjn9HvsJe4bbCQbSWK0,10508
29
29
  ad_api/api/sb/ad_groups.py,sha256=W06Fk0CCsuEYen1Bd8obR6aHpOMhk6tGWNWApecw59c,2882
30
30
  ad_api/api/sb/ad_groups_v4.py,sha256=f6KpOrJ2RsCa4aznP8vdGbXp0HGsCmLOvC9yXX2pESc,3669
@@ -40,6 +40,7 @@ ad_api/api/sb/keywords.py,sha256=P1dGqcLia349O0CwpBRURlIzgRc0cbFotkAGTSrErt4,763
40
40
  ad_api/api/sb/keywords_recommendations.py,sha256=oA4FRBwDtk52SZK0RgWvjNQ7K2GX0G2zSoisJGiEYDw,629
41
41
  ad_api/api/sb/landing_page_asins.py,sha256=j3qi2-KccuEzGLZrl6EfZBSikW2R4B4l7TixpkAnTq8,1259
42
42
  ad_api/api/sb/media.py,sha256=5LTeM4AHSDKw_58aTB2nEZhIYdWkztH0syBXaovS1rs,2529
43
+ ad_api/api/sb/migration.py,sha256=2Pse2ixv73ZzqKhoCq6hAHzilAXwP8iphuIzTIOWIUE,7091
43
44
  ad_api/api/sb/negative_keywords.py,sha256=ww3IRbawidDHAaYIkX5SsH2OZKJj4UnCylqqFeq3OoY,6918
44
45
  ad_api/api/sb/negative_product_targeting.py,sha256=QbK1EhhzmnNOzGtbkrAZ4HS5OqiLU3nZBmsl1scd2D0,5085
45
46
  ad_api/api/sb/optimization_rules.py,sha256=aj9tZwKdaOzjnAN-8FHb49aiejNZKCCCcR8fjffH4k0,6802
@@ -47,6 +48,7 @@ ad_api/api/sb/product_targeting.py,sha256=o6aB3hIh9jKvi_uTXFi--1hpnPFuXzQ-U9N7VU
47
48
  ad_api/api/sb/recommendations.py,sha256=_21L8tZPuArmJ30XyR1SKQHQH_yswvF0IMqwD9sLhgY,347
48
49
  ad_api/api/sb/reports.py,sha256=icw5DpRrqfj0idEo0NN1GPDpIWQun2h_c2Uwc7CpNUA,3801
49
50
  ad_api/api/sb/stores.py,sha256=sLdA6Lp37VR-ldu_sTUZIqc8MbMzysPhNOh05VaKBcA,4371
51
+ ad_api/api/sb/targeting_categories.py,sha256=mTU3hMEW_orG_D2mlTqUX14DEDPn9A11vH-wntECklk,4212
50
52
  ad_api/api/sb/targeting_recommendations.py,sha256=_7NxD5kC-Od11QOuLT03qpLVxEOIZgy-BtzniWK4CI8,2862
51
53
  ad_api/api/sb/themes.py,sha256=rjxdGcllaqLOUMoTKKgBVSH5iKuxMdF0w-dHq0X3-yU,4618
52
54
  ad_api/api/sd/__init__.py,sha256=RulShFVpowt2L_ZMcifFVgVrQEg5Uf9_SlVRydUnTF4,911
@@ -106,8 +108,8 @@ ad_api/base/exceptions.py,sha256=9D3M40ZIlLS0fA-aQh1BTWPOW-Nvf0tRhbA85jFnDJ4,438
106
108
  ad_api/base/helpers.py,sha256=naUkDLK466x_OBCPXxMlzRc2nPC6iQ6H_vyUji0MJJ0,2282
107
109
  ad_api/base/marketplaces.py,sha256=44lvCcluXNpqTprWQ80e3weSQmQbJoWL-WDSOWVbCyQ,4657
108
110
  ad_api/base/utils.py,sha256=_qnCBOvyJTLgaZjJzUWTi38GGLHkQszXELs79133Rb4,5618
109
- python_amazon_ad_api-1.1.4.dist-info/licenses/LICENSE,sha256=h2rpqlmmLy-4l1tN6KpWAFxx1JEMYeEFrMrZUbx_B_o,1105
110
- python_amazon_ad_api-1.1.4.dist-info/METADATA,sha256=Eu-MK8yO9WBcDpXS-oU_GW5N2lz2Ye8XEl_P1kA1npk,20486
111
- python_amazon_ad_api-1.1.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
112
- python_amazon_ad_api-1.1.4.dist-info/top_level.txt,sha256=nN9twUos-8pkP514ZwpvRU9YL9L2XyQyHsMQFfudGTU,7
113
- python_amazon_ad_api-1.1.4.dist-info/RECORD,,
111
+ python_amazon_ad_api-1.1.6.dist-info/licenses/LICENSE,sha256=h2rpqlmmLy-4l1tN6KpWAFxx1JEMYeEFrMrZUbx_B_o,1105
112
+ python_amazon_ad_api-1.1.6.dist-info/METADATA,sha256=z5F7aaY5WflWIew3RRAup1tAxtWYyE7sTzzSd2lwIfw,20486
113
+ python_amazon_ad_api-1.1.6.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
114
+ python_amazon_ad_api-1.1.6.dist-info/top_level.txt,sha256=nN9twUos-8pkP514ZwpvRU9YL9L2XyQyHsMQFfudGTU,7
115
+ python_amazon_ad_api-1.1.6.dist-info/RECORD,,