Contentstack 2.7.0__tar.gz → 2.8.0__tar.gz
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.
- {contentstack-2.7.0 → contentstack-2.8.0}/Contentstack.egg-info/PKG-INFO +31 -1
- {contentstack-2.7.0 → contentstack-2.8.0}/Contentstack.egg-info/SOURCES.txt +2 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/PKG-INFO +31 -1
- {contentstack-2.7.0 → contentstack-2.8.0}/README.md +30 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/__init__.py +1 -1
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/stack.py +28 -6
- contentstack-2.8.0/contentstack/taxonomy.py +233 -0
- contentstack-2.8.0/contentstack/term.py +194 -0
- contentstack-2.8.0/tests/test_taxonomies.py +733 -0
- contentstack-2.8.0/tests/test_taxonomies_unit.py +283 -0
- contentstack-2.7.0/contentstack/taxonomy.py +0 -64
- contentstack-2.7.0/tests/test_taxonomies.py +0 -246
- {contentstack-2.7.0 → contentstack-2.8.0}/Contentstack.egg-info/dependency_links.txt +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/Contentstack.egg-info/not-zip-safe +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/Contentstack.egg-info/requires.txt +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/Contentstack.egg-info/top_level.txt +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/LICENSE +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/asset.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/assetquery.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/basequery.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/contenttype.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/controller.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/deep_merge_lp.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/endpoint.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/entry.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/entryqueryable.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/error_messages.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/globalfields.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/https_connection.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/image_transform.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/query.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/region_refresh.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/utility.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/contentstack/variants.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/setup.cfg +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/setup.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_assets.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_basequery.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_early_fetch.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_early_find.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_endpoint.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_entry.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_global_fields.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_live_preview.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_query.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_stack.py +0 -0
- {contentstack-2.7.0 → contentstack-2.8.0}/tests/test_variants.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Contentstack
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: Contentstack is a headless CMS with an API-first approach.
|
|
5
5
|
Home-page: https://github.com/contentstack/contentstack-python
|
|
6
6
|
Author: Contentstack
|
|
@@ -162,6 +162,36 @@ image = stack.image_transform(url, {'width': 100, 'height': 100}).get_url()
|
|
|
162
162
|
image = stack.image_transform(url, {'auto': 'webp'}).get_url()
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
### Working with Taxonomies
|
|
166
|
+
|
|
167
|
+
Taxonomies published to an environment/locale can be fetched directly via the Content Delivery API.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# List all published taxonomies
|
|
171
|
+
result = stack.taxonomy().limit(10).include_count().find()
|
|
172
|
+
taxonomies = result['taxonomies']
|
|
173
|
+
|
|
174
|
+
# Fetch a single taxonomy, with locale fallback
|
|
175
|
+
taxonomy = stack.taxonomy('regions').locale('fr-fr').include_fallback().fetch()
|
|
176
|
+
|
|
177
|
+
# List all terms in a taxonomy
|
|
178
|
+
terms_result = stack.taxonomy('regions').term().locale('en-us').depth(3).find()
|
|
179
|
+
|
|
180
|
+
# Fetch a single term
|
|
181
|
+
term = stack.taxonomy('regions').term('california').fetch()
|
|
182
|
+
|
|
183
|
+
# Term hierarchy traversal
|
|
184
|
+
ancestors = stack.taxonomy('regions').term('san-francisco').depth(5).ancestors()
|
|
185
|
+
descendants = stack.taxonomy('electronics').term('laptops').depth(2).descendants()
|
|
186
|
+
locales = stack.taxonomy('regions').term('california').locales()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`stack.taxonomy()` (no uid) with a filter chained still filters entries by taxonomy, unchanged:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
result = stack.taxonomy().in_('taxonomies.category', ['category1', 'category2']).find()
|
|
193
|
+
```
|
|
194
|
+
|
|
165
195
|
### Using the Sync API with Python SDK
|
|
166
196
|
|
|
167
197
|
The Sync API takes care of syncing your Contentstack data with your application and ensures that the data is always
|
|
@@ -25,6 +25,7 @@ contentstack/query.py
|
|
|
25
25
|
contentstack/region_refresh.py
|
|
26
26
|
contentstack/stack.py
|
|
27
27
|
contentstack/taxonomy.py
|
|
28
|
+
contentstack/term.py
|
|
28
29
|
contentstack/utility.py
|
|
29
30
|
contentstack/variants.py
|
|
30
31
|
tests/test_assets.py
|
|
@@ -38,4 +39,5 @@ tests/test_live_preview.py
|
|
|
38
39
|
tests/test_query.py
|
|
39
40
|
tests/test_stack.py
|
|
40
41
|
tests/test_taxonomies.py
|
|
42
|
+
tests/test_taxonomies_unit.py
|
|
41
43
|
tests/test_variants.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Contentstack
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: Contentstack is a headless CMS with an API-first approach.
|
|
5
5
|
Home-page: https://github.com/contentstack/contentstack-python
|
|
6
6
|
Author: Contentstack
|
|
@@ -162,6 +162,36 @@ image = stack.image_transform(url, {'width': 100, 'height': 100}).get_url()
|
|
|
162
162
|
image = stack.image_transform(url, {'auto': 'webp'}).get_url()
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
### Working with Taxonomies
|
|
166
|
+
|
|
167
|
+
Taxonomies published to an environment/locale can be fetched directly via the Content Delivery API.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# List all published taxonomies
|
|
171
|
+
result = stack.taxonomy().limit(10).include_count().find()
|
|
172
|
+
taxonomies = result['taxonomies']
|
|
173
|
+
|
|
174
|
+
# Fetch a single taxonomy, with locale fallback
|
|
175
|
+
taxonomy = stack.taxonomy('regions').locale('fr-fr').include_fallback().fetch()
|
|
176
|
+
|
|
177
|
+
# List all terms in a taxonomy
|
|
178
|
+
terms_result = stack.taxonomy('regions').term().locale('en-us').depth(3).find()
|
|
179
|
+
|
|
180
|
+
# Fetch a single term
|
|
181
|
+
term = stack.taxonomy('regions').term('california').fetch()
|
|
182
|
+
|
|
183
|
+
# Term hierarchy traversal
|
|
184
|
+
ancestors = stack.taxonomy('regions').term('san-francisco').depth(5).ancestors()
|
|
185
|
+
descendants = stack.taxonomy('electronics').term('laptops').depth(2).descendants()
|
|
186
|
+
locales = stack.taxonomy('regions').term('california').locales()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`stack.taxonomy()` (no uid) with a filter chained still filters entries by taxonomy, unchanged:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
result = stack.taxonomy().in_('taxonomies.category', ['category1', 'category2']).find()
|
|
193
|
+
```
|
|
194
|
+
|
|
165
195
|
### Using the Sync API with Python SDK
|
|
166
196
|
|
|
167
197
|
The Sync API takes care of syncing your Contentstack data with your application and ensures that the data is always
|
|
@@ -126,6 +126,36 @@ image = stack.image_transform(url, {'width': 100, 'height': 100}).get_url()
|
|
|
126
126
|
image = stack.image_transform(url, {'auto': 'webp'}).get_url()
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
### Working with Taxonomies
|
|
130
|
+
|
|
131
|
+
Taxonomies published to an environment/locale can be fetched directly via the Content Delivery API.
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
# List all published taxonomies
|
|
135
|
+
result = stack.taxonomy().limit(10).include_count().find()
|
|
136
|
+
taxonomies = result['taxonomies']
|
|
137
|
+
|
|
138
|
+
# Fetch a single taxonomy, with locale fallback
|
|
139
|
+
taxonomy = stack.taxonomy('regions').locale('fr-fr').include_fallback().fetch()
|
|
140
|
+
|
|
141
|
+
# List all terms in a taxonomy
|
|
142
|
+
terms_result = stack.taxonomy('regions').term().locale('en-us').depth(3).find()
|
|
143
|
+
|
|
144
|
+
# Fetch a single term
|
|
145
|
+
term = stack.taxonomy('regions').term('california').fetch()
|
|
146
|
+
|
|
147
|
+
# Term hierarchy traversal
|
|
148
|
+
ancestors = stack.taxonomy('regions').term('san-francisco').depth(5).ancestors()
|
|
149
|
+
descendants = stack.taxonomy('electronics').term('laptops').depth(2).descendants()
|
|
150
|
+
locales = stack.taxonomy('regions').term('california').locales()
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`stack.taxonomy()` (no uid) with a filter chained still filters entries by taxonomy, unchanged:
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
result = stack.taxonomy().in_('taxonomies.category', ['category1', 'category2']).find()
|
|
157
|
+
```
|
|
158
|
+
|
|
129
159
|
### Using the Sync API with Python SDK
|
|
130
160
|
|
|
131
161
|
The Sync API takes care of syncing your Contentstack data with your application and ensures that the data is always
|
|
@@ -42,7 +42,7 @@ def get_contentstack_endpoint(region='us', service='', omit_https=False):
|
|
|
42
42
|
__title__ = 'contentstack-delivery-python'
|
|
43
43
|
__author__ = 'contentstack'
|
|
44
44
|
__status__ = 'debug'
|
|
45
|
-
__version__ = 'v2.
|
|
45
|
+
__version__ = 'v2.8.0'
|
|
46
46
|
__endpoint__ = 'cdn.contentstack.io'
|
|
47
47
|
__email__ = 'support@contentstack.com'
|
|
48
48
|
__developer_email__ = 'mobile@contentstack.com'
|
|
@@ -8,7 +8,7 @@ from contentstack.asset import Asset
|
|
|
8
8
|
from contentstack.assetquery import AssetQuery
|
|
9
9
|
from contentstack.contenttype import ContentType
|
|
10
10
|
from contentstack.endpoint import Endpoint
|
|
11
|
-
from contentstack.taxonomy import Taxonomy
|
|
11
|
+
from contentstack.taxonomy import Taxonomy, TaxonomyQuery
|
|
12
12
|
from contentstack.globalfields import GlobalField
|
|
13
13
|
from contentstack.https_connection import HTTPSConnection
|
|
14
14
|
from contentstack.image_transform import ImageTransform
|
|
@@ -202,13 +202,35 @@ class Stack:
|
|
|
202
202
|
"""
|
|
203
203
|
return ContentType(self.http_instance, content_type_uid)
|
|
204
204
|
|
|
205
|
-
def taxonomy(self):
|
|
205
|
+
def taxonomy(self, taxonomy_uid: str = None):
|
|
206
206
|
"""
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
Without taxonomy_uid: returns a TaxonomyQuery. Chain a filter
|
|
208
|
+
(in_, or_, and_, exists, above, below, equal_and_above, equal_and_below)
|
|
209
|
+
then find() to fetch entries filtered by taxonomy (legacy behavior,
|
|
210
|
+
unchanged). Or call find() directly with nothing chained to list all
|
|
211
|
+
published taxonomies from the Content Delivery API.
|
|
212
|
+
|
|
213
|
+
With taxonomy_uid: returns a Taxonomy for CDA access to that specific
|
|
214
|
+
published taxonomy (fetch(), term()).
|
|
215
|
+
|
|
216
|
+
:param taxonomy_uid: {str} -- (optional) unique identifier of the taxonomy.
|
|
217
|
+
:return: TaxonomyQuery or Taxonomy
|
|
218
|
+
-----------------------------
|
|
219
|
+
Example::
|
|
220
|
+
|
|
221
|
+
>>> import contentstack
|
|
222
|
+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
|
|
223
|
+
>>> # Legacy: filter entries by taxonomy
|
|
224
|
+
>>> result = stack.taxonomy().in_('taxonomies.color', ['red']).find()
|
|
225
|
+
>>> # New: list all published taxonomies
|
|
226
|
+
>>> result = stack.taxonomy().limit(10).find()
|
|
227
|
+
>>> # New: fetch a single published taxonomy
|
|
228
|
+
>>> result = stack.taxonomy('regions').fetch()
|
|
229
|
+
-----------------------------
|
|
210
230
|
"""
|
|
211
|
-
|
|
231
|
+
if taxonomy_uid:
|
|
232
|
+
return Taxonomy(self.http_instance, taxonomy_uid)
|
|
233
|
+
return TaxonomyQuery(self.http_instance)
|
|
212
234
|
|
|
213
235
|
def global_field(self, global_field_uid=None):
|
|
214
236
|
"""
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from urllib.parse import quote
|
|
3
|
+
|
|
4
|
+
from contentstack.error_messages import ErrorMessages
|
|
5
|
+
from contentstack.utility import Utils
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TaxonomyFilter:
|
|
9
|
+
"""
|
|
10
|
+
Taxonomy entry-filter queries ($above, $below, $in, $exists, ...).
|
|
11
|
+
Use stack.taxonomy() to instantiate.
|
|
12
|
+
|
|
13
|
+
API Reference: https://www.contentstack.com/docs/developers/apis/content-delivery-api/#taxonomies
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, http_instance):
|
|
17
|
+
self.http_instance = http_instance
|
|
18
|
+
self._filters: dict = {}
|
|
19
|
+
|
|
20
|
+
def _add(self, field: str, condition: dict) -> "TaxonomyFilter":
|
|
21
|
+
self._filters[field] = condition
|
|
22
|
+
return self
|
|
23
|
+
|
|
24
|
+
def in_(self, field: str, terms: list) -> "TaxonomyFilter":
|
|
25
|
+
return self._add(field, {"$in": terms})
|
|
26
|
+
|
|
27
|
+
def or_(self, *conds: dict) -> "TaxonomyFilter":
|
|
28
|
+
return self._add("$or", list(conds))
|
|
29
|
+
|
|
30
|
+
def and_(self, *conds: dict) -> "TaxonomyFilter":
|
|
31
|
+
return self._add("$and", list(conds))
|
|
32
|
+
|
|
33
|
+
def exists(self, field: str) -> "TaxonomyFilter":
|
|
34
|
+
return self._add(field, {"$exists": True})
|
|
35
|
+
|
|
36
|
+
def equal_and_below(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyFilter":
|
|
37
|
+
cond = {"$eq_below": term_uid, "levels": levels}
|
|
38
|
+
return self._add(field, cond)
|
|
39
|
+
|
|
40
|
+
def below(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyFilter":
|
|
41
|
+
cond = {"$below": term_uid, "levels": levels}
|
|
42
|
+
return self._add(field, cond)
|
|
43
|
+
|
|
44
|
+
def equal_and_above(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyFilter":
|
|
45
|
+
cond = {"$eq_above": term_uid, "levels": levels}
|
|
46
|
+
return self._add(field, cond)
|
|
47
|
+
|
|
48
|
+
def above(self, field: str, term_uid: str, levels: int = 10) -> "TaxonomyFilter":
|
|
49
|
+
cond = {"$above": term_uid, "levels": levels}
|
|
50
|
+
return self._add(field, cond)
|
|
51
|
+
|
|
52
|
+
def find(self, params=None):
|
|
53
|
+
"""
|
|
54
|
+
This method fetches entries filtered by taxonomy from the stack.
|
|
55
|
+
"""
|
|
56
|
+
self.local_param = {}
|
|
57
|
+
self.local_param['environment'] = self.http_instance.headers['environment']
|
|
58
|
+
|
|
59
|
+
# Ensure query param is always present
|
|
60
|
+
query_string = json.dumps(self._filters or {})
|
|
61
|
+
query_encoded = quote(query_string, safe='{}":,[]') # preserves JSON characters
|
|
62
|
+
|
|
63
|
+
# Build the base URL
|
|
64
|
+
endpoint = self.http_instance.endpoint
|
|
65
|
+
url = f'{endpoint}/taxonomies/entries?environment={self.local_param["environment"]}&query={query_encoded}'
|
|
66
|
+
|
|
67
|
+
# Append any additional params manually
|
|
68
|
+
if params:
|
|
69
|
+
other_params = '&'.join(f'{k}={v}' for k, v in params.items())
|
|
70
|
+
url += f'&{other_params}'
|
|
71
|
+
return self.http_instance.get(url)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TaxonomyQuery(TaxonomyFilter):
|
|
75
|
+
"""
|
|
76
|
+
stack.taxonomy() — no uid.
|
|
77
|
+
|
|
78
|
+
Chain a filter (in_, or_, and_, exists, above, below, equal_and_above,
|
|
79
|
+
equal_and_below) then call find() to fetch entries filtered by taxonomy
|
|
80
|
+
(unchanged legacy behavior, inherited from TaxonomyFilter).
|
|
81
|
+
|
|
82
|
+
Call find() directly with nothing chained to list all published
|
|
83
|
+
taxonomies from the Content Delivery API instead.
|
|
84
|
+
|
|
85
|
+
Example::
|
|
86
|
+
|
|
87
|
+
>>> import contentstack
|
|
88
|
+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
|
|
89
|
+
>>> # Legacy: filter entries by taxonomy
|
|
90
|
+
>>> result = stack.taxonomy().in_('taxonomies.color', ['red']).find()
|
|
91
|
+
>>> # New: list all published taxonomies
|
|
92
|
+
>>> result = stack.taxonomy().limit(10).include_count().find()
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, http_instance):
|
|
96
|
+
super().__init__(http_instance)
|
|
97
|
+
self._query_params = {}
|
|
98
|
+
|
|
99
|
+
def skip(self, skip: int) -> "TaxonomyQuery":
|
|
100
|
+
"""Sets pagination offset for listing taxonomies.
|
|
101
|
+
:param skip: {int} -- number of taxonomies to skip
|
|
102
|
+
:return: TaxonomyQuery, so you can chain this call.
|
|
103
|
+
"""
|
|
104
|
+
self._query_params['skip'] = skip
|
|
105
|
+
return self
|
|
106
|
+
|
|
107
|
+
def limit(self, limit: int) -> "TaxonomyQuery":
|
|
108
|
+
"""Sets the maximum number of taxonomies to return.
|
|
109
|
+
:param limit: {int} -- max number of taxonomies to return
|
|
110
|
+
:return: TaxonomyQuery, so you can chain this call.
|
|
111
|
+
"""
|
|
112
|
+
self._query_params['limit'] = limit
|
|
113
|
+
return self
|
|
114
|
+
|
|
115
|
+
def include_count(self) -> "TaxonomyQuery":
|
|
116
|
+
"""Includes the total count of taxonomies in the response.
|
|
117
|
+
:return: TaxonomyQuery, so you can chain this call.
|
|
118
|
+
"""
|
|
119
|
+
self._query_params['include_count'] = 'true'
|
|
120
|
+
return self
|
|
121
|
+
|
|
122
|
+
def param(self, key: str, value) -> "TaxonomyQuery":
|
|
123
|
+
"""Adds an arbitrary query parameter to the list-taxonomies request
|
|
124
|
+
(e.g. 'locale').
|
|
125
|
+
:param key: {str} -- query parameter key
|
|
126
|
+
:param value: value for the query parameter
|
|
127
|
+
:return: TaxonomyQuery, so you can chain this call.
|
|
128
|
+
"""
|
|
129
|
+
if None in (key, value):
|
|
130
|
+
raise KeyError(ErrorMessages.INVALID_KEY_VALUE_ARGS)
|
|
131
|
+
self._query_params[key] = value
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
def find(self, params=None):
|
|
135
|
+
"""
|
|
136
|
+
If a filter was chained (in_/or_/and_/exists/above/below/...), fetches
|
|
137
|
+
entries filtered by taxonomy — unchanged legacy behavior.
|
|
138
|
+
GET /taxonomies/entries?environment=...&query=...
|
|
139
|
+
|
|
140
|
+
Otherwise, fetches all published taxonomies from the CDA.
|
|
141
|
+
GET /taxonomies
|
|
142
|
+
|
|
143
|
+
:return: dict -- filtered entries, or {'taxonomies': [...], 'count'?: n}
|
|
144
|
+
"""
|
|
145
|
+
if self._filters:
|
|
146
|
+
return super().find(params)
|
|
147
|
+
if params:
|
|
148
|
+
self._query_params.update(params)
|
|
149
|
+
url = f'{self.http_instance.endpoint}/taxonomies'
|
|
150
|
+
query_str = Utils.do_url_encode(self._query_params)
|
|
151
|
+
return self.http_instance.get(f'{url}?{query_str}' if query_str else url)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Taxonomy:
|
|
155
|
+
"""
|
|
156
|
+
Represents a single published taxonomy from the Content Delivery API.
|
|
157
|
+
Use stack.taxonomy(uid) to instantiate.
|
|
158
|
+
|
|
159
|
+
Example::
|
|
160
|
+
|
|
161
|
+
>>> import contentstack
|
|
162
|
+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
|
|
163
|
+
>>> taxonomy = stack.taxonomy('regions').locale('fr-fr').include_fallback()
|
|
164
|
+
>>> result = taxonomy.fetch()
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(self, http_instance, taxonomy_uid: str):
|
|
168
|
+
if not taxonomy_uid:
|
|
169
|
+
raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE)
|
|
170
|
+
self.http_instance = http_instance
|
|
171
|
+
self._taxonomy_uid = taxonomy_uid
|
|
172
|
+
self._url = f'{http_instance.endpoint}/taxonomies/{taxonomy_uid}'
|
|
173
|
+
self._query_params = {}
|
|
174
|
+
|
|
175
|
+
def term(self, term_uid: str = None):
|
|
176
|
+
"""
|
|
177
|
+
Without term_uid: returns TermQuery for listing all terms in this taxonomy.
|
|
178
|
+
With term_uid: returns Term for a specific term.
|
|
179
|
+
|
|
180
|
+
:param term_uid: {str} -- (optional) unique identifier of the term.
|
|
181
|
+
:return: Term or TermQuery
|
|
182
|
+
"""
|
|
183
|
+
from contentstack.term import Term, TermQuery
|
|
184
|
+
if term_uid:
|
|
185
|
+
return Term(self.http_instance, self._taxonomy_uid, term_uid)
|
|
186
|
+
return TermQuery(self.http_instance, self._taxonomy_uid)
|
|
187
|
+
|
|
188
|
+
def locale(self, locale: str) -> "Taxonomy":
|
|
189
|
+
"""Sets the locale for this taxonomy fetch (e.g. 'en-us', 'fr-fr').
|
|
190
|
+
:param locale: {str} -- locale code
|
|
191
|
+
:return: Taxonomy, so you can chain this call.
|
|
192
|
+
"""
|
|
193
|
+
self._query_params['locale'] = locale
|
|
194
|
+
return self
|
|
195
|
+
|
|
196
|
+
def include_fallback(self) -> "Taxonomy":
|
|
197
|
+
"""Enables locale fallback through the branch hierarchy.
|
|
198
|
+
If the taxonomy is not published in the requested locale, falls back
|
|
199
|
+
to the parent locale in the branch hierarchy.
|
|
200
|
+
:return: Taxonomy, so you can chain this call.
|
|
201
|
+
"""
|
|
202
|
+
self._query_params['include_fallback'] = 'true'
|
|
203
|
+
return self
|
|
204
|
+
|
|
205
|
+
def include_branch(self) -> "Taxonomy":
|
|
206
|
+
"""Includes the _branch field in the response.
|
|
207
|
+
:return: Taxonomy, so you can chain this call.
|
|
208
|
+
"""
|
|
209
|
+
self._query_params['include_branch'] = 'true'
|
|
210
|
+
return self
|
|
211
|
+
|
|
212
|
+
def param(self, key: str, value) -> "Taxonomy":
|
|
213
|
+
"""Adds an arbitrary query parameter.
|
|
214
|
+
:param key: {str} -- query parameter key
|
|
215
|
+
:param value: value for the query parameter
|
|
216
|
+
:return: Taxonomy, so you can chain this call.
|
|
217
|
+
"""
|
|
218
|
+
if None in (key, value):
|
|
219
|
+
raise KeyError(ErrorMessages.INVALID_KEY_VALUE_ARGS)
|
|
220
|
+
self._query_params[key] = value
|
|
221
|
+
return self
|
|
222
|
+
|
|
223
|
+
def fetch(self) -> dict:
|
|
224
|
+
"""
|
|
225
|
+
Fetches this taxonomy from the CDA.
|
|
226
|
+
GET /taxonomies/{taxonomy_uid}
|
|
227
|
+
|
|
228
|
+
:return: dict -- the taxonomy object.
|
|
229
|
+
"""
|
|
230
|
+
query_str = Utils.do_url_encode(self._query_params)
|
|
231
|
+
url = f'{self._url}?{query_str}' if query_str else self._url
|
|
232
|
+
response = self.http_instance.get(url)
|
|
233
|
+
return response.get('taxonomy', response)
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
from contentstack.error_messages import ErrorMessages
|
|
2
|
+
from contentstack.utility import Utils
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Term:
|
|
6
|
+
"""
|
|
7
|
+
Represents a single published taxonomy term.
|
|
8
|
+
Supports hierarchy traversal: ancestors(), descendants(), locales().
|
|
9
|
+
Use stack.taxonomy(uid).term(term_uid) to instantiate.
|
|
10
|
+
|
|
11
|
+
Example::
|
|
12
|
+
|
|
13
|
+
>>> import contentstack
|
|
14
|
+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
|
|
15
|
+
>>> term = stack.taxonomy('regions').term('california')
|
|
16
|
+
>>> result = term.fetch()
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, http_instance, taxonomy_uid: str, term_uid: str):
|
|
20
|
+
if not term_uid:
|
|
21
|
+
raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE)
|
|
22
|
+
self.http_instance = http_instance
|
|
23
|
+
self._url = f'{http_instance.endpoint}/taxonomies/{taxonomy_uid}/terms/{term_uid}'
|
|
24
|
+
self._query_params = {}
|
|
25
|
+
|
|
26
|
+
def depth(self, depth: int) -> "Term":
|
|
27
|
+
"""Limits the depth of hierarchy traversal for ancestors()/descendants().
|
|
28
|
+
:param depth: {int} -- maximum hierarchy depth
|
|
29
|
+
:return: Term, so you can chain this call.
|
|
30
|
+
"""
|
|
31
|
+
self._query_params['depth'] = depth
|
|
32
|
+
return self
|
|
33
|
+
|
|
34
|
+
def locale(self, locale: str) -> "Term":
|
|
35
|
+
"""Sets the locale for this term fetch (e.g. 'en-us', 'fr-fr').
|
|
36
|
+
:param locale: {str} -- locale code
|
|
37
|
+
:return: Term, so you can chain this call.
|
|
38
|
+
"""
|
|
39
|
+
self._query_params['locale'] = locale
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def include_fallback(self) -> "Term":
|
|
43
|
+
"""Enables locale fallback through the branch hierarchy.
|
|
44
|
+
:return: Term, so you can chain this call.
|
|
45
|
+
"""
|
|
46
|
+
self._query_params['include_fallback'] = 'true'
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def include_branch(self) -> "Term":
|
|
50
|
+
"""Includes the _branch field in the response.
|
|
51
|
+
:return: Term, so you can chain this call.
|
|
52
|
+
"""
|
|
53
|
+
self._query_params['include_branch'] = 'true'
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def param(self, key: str, value) -> "Term":
|
|
57
|
+
"""Adds an arbitrary query parameter.
|
|
58
|
+
:param key: {str} -- query parameter key
|
|
59
|
+
:param value: value for the query parameter
|
|
60
|
+
:return: Term, so you can chain this call.
|
|
61
|
+
"""
|
|
62
|
+
if None in (key, value):
|
|
63
|
+
raise KeyError(ErrorMessages.INVALID_KEY_VALUE_ARGS)
|
|
64
|
+
self._query_params[key] = value
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
def _get(self, suffix: str = '', key: str = None):
|
|
68
|
+
url = f'{self._url}{suffix}'
|
|
69
|
+
query_str = Utils.do_url_encode(self._query_params)
|
|
70
|
+
response = self.http_instance.get(f'{url}?{query_str}' if query_str else url)
|
|
71
|
+
return response.get(key, response) if key else response
|
|
72
|
+
|
|
73
|
+
def fetch(self) -> dict:
|
|
74
|
+
"""
|
|
75
|
+
Fetches this term.
|
|
76
|
+
GET /taxonomies/{taxonomy_uid}/terms/{term_uid}
|
|
77
|
+
:return: dict -- the term object.
|
|
78
|
+
"""
|
|
79
|
+
return self._get(key='term')
|
|
80
|
+
|
|
81
|
+
def locales(self) -> dict:
|
|
82
|
+
"""
|
|
83
|
+
Fetches all published localized versions of this term.
|
|
84
|
+
GET /taxonomies/{taxonomy_uid}/terms/{term_uid}/locales
|
|
85
|
+
:return: dict -- the localized terms.
|
|
86
|
+
"""
|
|
87
|
+
return self._get('/locales', key='terms')
|
|
88
|
+
|
|
89
|
+
def ancestors(self) -> list:
|
|
90
|
+
"""
|
|
91
|
+
Fetches all ancestor terms up to the root.
|
|
92
|
+
GET /taxonomies/{taxonomy_uid}/terms/{term_uid}/ancestors
|
|
93
|
+
:return: list -- the ancestor terms.
|
|
94
|
+
"""
|
|
95
|
+
return self._get('/ancestors', key='terms')
|
|
96
|
+
|
|
97
|
+
def descendants(self) -> list:
|
|
98
|
+
"""
|
|
99
|
+
Fetches all descendant terms.
|
|
100
|
+
GET /taxonomies/{taxonomy_uid}/terms/{term_uid}/descendants
|
|
101
|
+
:return: list -- the descendant terms.
|
|
102
|
+
"""
|
|
103
|
+
return self._get('/descendants', key='terms')
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class TermQuery:
|
|
107
|
+
"""
|
|
108
|
+
Query builder for fetching all terms in a taxonomy.
|
|
109
|
+
Use stack.taxonomy(uid).term() to instantiate.
|
|
110
|
+
|
|
111
|
+
Example::
|
|
112
|
+
|
|
113
|
+
>>> import contentstack
|
|
114
|
+
>>> stack = contentstack.Stack('api_key', 'delivery_token', 'environment')
|
|
115
|
+
>>> result = stack.taxonomy('regions').term().locale('en-us').depth(3).find()
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def __init__(self, http_instance, taxonomy_uid: str):
|
|
119
|
+
self.http_instance = http_instance
|
|
120
|
+
self._url = f'{http_instance.endpoint}/taxonomies/{taxonomy_uid}/terms'
|
|
121
|
+
self._query_params = {}
|
|
122
|
+
|
|
123
|
+
def depth(self, depth: int) -> "TermQuery":
|
|
124
|
+
"""Limits term hierarchy traversal depth.
|
|
125
|
+
:param depth: {int} -- maximum hierarchy depth
|
|
126
|
+
:return: TermQuery, so you can chain this call.
|
|
127
|
+
"""
|
|
128
|
+
self._query_params['depth'] = depth
|
|
129
|
+
return self
|
|
130
|
+
|
|
131
|
+
def skip(self, skip: int) -> "TermQuery":
|
|
132
|
+
"""Sets pagination offset.
|
|
133
|
+
:param skip: {int} -- number of terms to skip
|
|
134
|
+
:return: TermQuery, so you can chain this call.
|
|
135
|
+
"""
|
|
136
|
+
self._query_params['skip'] = skip
|
|
137
|
+
return self
|
|
138
|
+
|
|
139
|
+
def limit(self, limit: int) -> "TermQuery":
|
|
140
|
+
"""Sets the maximum number of terms to return.
|
|
141
|
+
:param limit: {int} -- max number of terms to return
|
|
142
|
+
:return: TermQuery, so you can chain this call.
|
|
143
|
+
"""
|
|
144
|
+
self._query_params['limit'] = limit
|
|
145
|
+
return self
|
|
146
|
+
|
|
147
|
+
def locale(self, locale: str) -> "TermQuery":
|
|
148
|
+
"""Filters terms by locale (e.g. 'en-us', 'fr-fr').
|
|
149
|
+
:param locale: {str} -- locale code
|
|
150
|
+
:return: TermQuery, so you can chain this call.
|
|
151
|
+
"""
|
|
152
|
+
self._query_params['locale'] = locale
|
|
153
|
+
return self
|
|
154
|
+
|
|
155
|
+
def include_fallback(self) -> "TermQuery":
|
|
156
|
+
"""Enables locale fallback through the branch hierarchy.
|
|
157
|
+
:return: TermQuery, so you can chain this call.
|
|
158
|
+
"""
|
|
159
|
+
self._query_params['include_fallback'] = 'true'
|
|
160
|
+
return self
|
|
161
|
+
|
|
162
|
+
def include_branch(self) -> "TermQuery":
|
|
163
|
+
"""Includes the _branch field in the response.
|
|
164
|
+
:return: TermQuery, so you can chain this call.
|
|
165
|
+
"""
|
|
166
|
+
self._query_params['include_branch'] = 'true'
|
|
167
|
+
return self
|
|
168
|
+
|
|
169
|
+
def include_count(self) -> "TermQuery":
|
|
170
|
+
"""Includes the total count of terms in the response.
|
|
171
|
+
:return: TermQuery, so you can chain this call.
|
|
172
|
+
"""
|
|
173
|
+
self._query_params['include_count'] = 'true'
|
|
174
|
+
return self
|
|
175
|
+
|
|
176
|
+
def param(self, key: str, value) -> "TermQuery":
|
|
177
|
+
"""Adds an arbitrary query parameter.
|
|
178
|
+
:param key: {str} -- query parameter key
|
|
179
|
+
:param value: value for the query parameter
|
|
180
|
+
:return: TermQuery, so you can chain this call.
|
|
181
|
+
"""
|
|
182
|
+
if None in (key, value):
|
|
183
|
+
raise KeyError(ErrorMessages.INVALID_KEY_VALUE_ARGS)
|
|
184
|
+
self._query_params[key] = value
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
def find(self) -> dict:
|
|
188
|
+
"""
|
|
189
|
+
Fetches all terms in the taxonomy.
|
|
190
|
+
GET /taxonomies/{taxonomy_uid}/terms
|
|
191
|
+
:return: dict -- {'terms': [...], 'count'?: n}
|
|
192
|
+
"""
|
|
193
|
+
query_str = Utils.do_url_encode(self._query_params)
|
|
194
|
+
return self.http_instance.get(f'{self._url}?{query_str}' if query_str else self._url)
|