contentstack-utils 1.3.3__tar.gz → 1.5.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_utils-1.3.3 → contentstack_utils-1.5.0}/PKG-INFO +1 -1
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/__init__.py +6 -2
- contentstack_utils-1.5.0/contentstack_utils/entry_editable.py +248 -0
- contentstack_utils-1.5.0/contentstack_utils/utils.py +238 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils.egg-info/PKG-INFO +1 -1
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils.egg-info/SOURCES.txt +4 -1
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/setup.py +1 -1
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/convert_style.py +1 -1
- contentstack_utils-1.5.0/tests/test_editable_tags.py +56 -0
- contentstack_utils-1.5.0/tests/test_variant_aliases.py +164 -0
- contentstack_utils-1.3.3/contentstack_utils/utils.py +0 -94
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/LICENSE +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/README.md +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/automate.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/embedded/__init__.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/embedded/item_type.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/embedded/styletype.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/gql.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/helper/__init__.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/helper/converter.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/helper/metadata.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/helper/node_to_html.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/render/__init__.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/render/options.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils.egg-info/dependency_links.txt +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils.egg-info/top_level.txt +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/setup.cfg +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/__init__.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_default_opt_others.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_gql_to_html_func.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_helper_node_to_html.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_item_types.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_metadata.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_option_render_mark.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_render_default_options.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_render_options.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_style_type.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_util_srte.py +0 -0
- {contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentstack_utils
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: contentstack_utils is a Utility package for Contentstack headless CMS with an API-first approach.
|
|
5
5
|
Home-page: https://github.com/contentstack/contentstack-utils-python
|
|
6
6
|
Author: contentstack
|
|
@@ -16,6 +16,7 @@ from contentstack_utils.render.options import Options
|
|
|
16
16
|
from contentstack_utils.utils import Utils
|
|
17
17
|
from contentstack_utils.gql import GQL
|
|
18
18
|
from contentstack_utils.automate import Automate
|
|
19
|
+
from contentstack_utils.entry_editable import addEditableTags, addTags, getTag
|
|
19
20
|
|
|
20
21
|
__all__ = (
|
|
21
22
|
"Utils",
|
|
@@ -25,12 +26,15 @@ __all__ = (
|
|
|
25
26
|
"Automate",
|
|
26
27
|
"StyleType",
|
|
27
28
|
"ItemType",
|
|
28
|
-
"NodeToHtml"
|
|
29
|
+
"NodeToHtml",
|
|
30
|
+
"addEditableTags",
|
|
31
|
+
"addTags",
|
|
32
|
+
"getTag",
|
|
29
33
|
)
|
|
30
34
|
|
|
31
35
|
__title__ = 'contentstack_utils'
|
|
32
36
|
__author__ = 'contentstack'
|
|
33
37
|
__status__ = 'debug'
|
|
34
|
-
__version__ = '1.
|
|
38
|
+
__version__ = '1.4.0'
|
|
35
39
|
__endpoint__ = 'cdn.contentstack.io'
|
|
36
40
|
__contact__ = 'support@contentstack.com'
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional, Union, cast
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
AppliedVariants = Optional[Dict[str, Any]]
|
|
7
|
+
TagValue = Union[str, Dict[str, str]]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _get_parent_variantised_path(applied_variants: Dict[str, Any], meta_key: str) -> str:
|
|
11
|
+
"""
|
|
12
|
+
Port of JS getParentVariantisedPath().
|
|
13
|
+
Finds the longest variantised field path that is a prefix of meta_key.
|
|
14
|
+
"""
|
|
15
|
+
try:
|
|
16
|
+
if not meta_key:
|
|
17
|
+
return ""
|
|
18
|
+
variantised_field_paths = sorted(applied_variants.keys(), key=len, reverse=True)
|
|
19
|
+
child_fragments = meta_key.split(".")
|
|
20
|
+
if not child_fragments or not variantised_field_paths:
|
|
21
|
+
return ""
|
|
22
|
+
for path in variantised_field_paths:
|
|
23
|
+
parent_fragments = str(path).split(".")
|
|
24
|
+
if len(parent_fragments) > len(child_fragments):
|
|
25
|
+
continue
|
|
26
|
+
if all(child_fragments[i] == parent_fragments[i] for i in range(len(parent_fragments))):
|
|
27
|
+
return str(path)
|
|
28
|
+
return ""
|
|
29
|
+
except Exception:
|
|
30
|
+
return ""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _apply_variant_to_data_value(data_value: str, applied_variants: AppliedVariants, meta_key: str, should_apply_variant: bool) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Port of JS applyVariantToDataValue().
|
|
36
|
+
|
|
37
|
+
If the current field (or its parent field path) is variantised, prefixes with
|
|
38
|
+
'v2:' and appends `_{variant}` to the entry uid segment of the dot-path.
|
|
39
|
+
"""
|
|
40
|
+
if not should_apply_variant or not applied_variants or not meta_key or not isinstance(applied_variants, dict):
|
|
41
|
+
return data_value
|
|
42
|
+
|
|
43
|
+
variant: Optional[str] = None
|
|
44
|
+
if meta_key in applied_variants:
|
|
45
|
+
variant = str(applied_variants[meta_key])
|
|
46
|
+
else:
|
|
47
|
+
parent_path = _get_parent_variantised_path(applied_variants, meta_key)
|
|
48
|
+
if parent_path:
|
|
49
|
+
variant = str(applied_variants.get(parent_path))
|
|
50
|
+
|
|
51
|
+
if not variant:
|
|
52
|
+
return data_value
|
|
53
|
+
|
|
54
|
+
parts = ("v2:" + data_value).split(".")
|
|
55
|
+
if len(parts) >= 2:
|
|
56
|
+
parts[1] = parts[1] + "_" + variant
|
|
57
|
+
return ".".join(parts)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _tags_value(data_value: str, tags_as_object: bool, applied_variants: AppliedVariants, meta_key: str, should_apply_variant: bool) -> TagValue:
|
|
61
|
+
resolved = _apply_variant_to_data_value(data_value, applied_variants, meta_key, should_apply_variant)
|
|
62
|
+
if tags_as_object:
|
|
63
|
+
return {"data-cslp": resolved}
|
|
64
|
+
return f"data-cslp={resolved}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _parent_tags_value(data_value: str, tags_as_object: bool) -> TagValue:
|
|
68
|
+
if tags_as_object:
|
|
69
|
+
return {"data-cslp-parent-field": data_value}
|
|
70
|
+
return f"data-cslp-parent-field={data_value}"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def getTag( # pylint: disable=invalid-name
|
|
74
|
+
content: Any,
|
|
75
|
+
prefix: str,
|
|
76
|
+
tags_as_object: bool,
|
|
77
|
+
locale: str,
|
|
78
|
+
applied_variants: AppliedVariants,
|
|
79
|
+
should_apply_variant: bool,
|
|
80
|
+
meta_key: str = "",
|
|
81
|
+
) -> Dict[str, Any]:
|
|
82
|
+
"""
|
|
83
|
+
Port of JS getTag() from `src/entry-editable.ts`.
|
|
84
|
+
|
|
85
|
+
Returns a dict mapping field keys to CSLP tag values, and mutates nested objects/refs
|
|
86
|
+
by attaching their own `$` tag maps.
|
|
87
|
+
"""
|
|
88
|
+
if content is None or not isinstance(content, dict):
|
|
89
|
+
return {}
|
|
90
|
+
|
|
91
|
+
tags: Dict[str, Any] = {}
|
|
92
|
+
for key, value in content.items():
|
|
93
|
+
if key == "$":
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
meta_uid = ""
|
|
97
|
+
if isinstance(value, dict):
|
|
98
|
+
meta = value.get("_metadata")
|
|
99
|
+
if isinstance(meta, dict) and meta.get("uid"):
|
|
100
|
+
meta_uid = str(meta.get("uid"))
|
|
101
|
+
|
|
102
|
+
meta_key_prefix = (meta_key + ".") if meta_key else ""
|
|
103
|
+
updated_meta_key = f"{meta_key_prefix}{key}" if should_apply_variant else ""
|
|
104
|
+
if meta_uid and updated_meta_key:
|
|
105
|
+
updated_meta_key = updated_meta_key + "." + meta_uid
|
|
106
|
+
|
|
107
|
+
if isinstance(value, list):
|
|
108
|
+
for index, obj in enumerate(value):
|
|
109
|
+
if obj is None:
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
child_key = f"{key}__{index}"
|
|
113
|
+
parent_key = f"{key}__parent"
|
|
114
|
+
|
|
115
|
+
obj_meta_uid = ""
|
|
116
|
+
if isinstance(obj, dict):
|
|
117
|
+
meta = obj.get("_metadata")
|
|
118
|
+
if isinstance(meta, dict) and meta.get("uid"):
|
|
119
|
+
obj_meta_uid = str(meta.get("uid"))
|
|
120
|
+
|
|
121
|
+
array_meta_key = f"{meta_key_prefix}{key}" if should_apply_variant else ""
|
|
122
|
+
if obj_meta_uid and array_meta_key:
|
|
123
|
+
array_meta_key = array_meta_key + "." + obj_meta_uid
|
|
124
|
+
|
|
125
|
+
tags[child_key] = _tags_value(
|
|
126
|
+
f"{prefix}.{key}.{index}",
|
|
127
|
+
tags_as_object,
|
|
128
|
+
applied_variants,
|
|
129
|
+
array_meta_key,
|
|
130
|
+
should_apply_variant,
|
|
131
|
+
)
|
|
132
|
+
tags[parent_key] = _parent_tags_value(f"{prefix}.{key}", tags_as_object)
|
|
133
|
+
|
|
134
|
+
# Reference entries in array
|
|
135
|
+
if isinstance(obj, dict) and obj.get("_content_type_uid") is not None and obj.get("uid") is not None:
|
|
136
|
+
new_applied_variants = obj.get("_applied_variants")
|
|
137
|
+
if new_applied_variants is None and isinstance(obj.get("system"), dict):
|
|
138
|
+
new_applied_variants = cast(dict, obj["system"]).get("applied_variants")
|
|
139
|
+
new_should_apply_variant = bool(new_applied_variants)
|
|
140
|
+
|
|
141
|
+
obj_locale = obj.get("locale") or locale
|
|
142
|
+
obj["$"] = getTag(
|
|
143
|
+
obj,
|
|
144
|
+
f"{obj.get('_content_type_uid')}.{obj.get('uid')}.{obj_locale}",
|
|
145
|
+
tags_as_object,
|
|
146
|
+
locale,
|
|
147
|
+
cast(AppliedVariants, new_applied_variants),
|
|
148
|
+
new_should_apply_variant,
|
|
149
|
+
meta_key="",
|
|
150
|
+
)
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
if isinstance(obj, dict):
|
|
154
|
+
obj["$"] = getTag(
|
|
155
|
+
obj,
|
|
156
|
+
f"{prefix}.{key}.{index}",
|
|
157
|
+
tags_as_object,
|
|
158
|
+
locale,
|
|
159
|
+
applied_variants,
|
|
160
|
+
should_apply_variant,
|
|
161
|
+
meta_key=array_meta_key,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
tags[key] = _tags_value(
|
|
165
|
+
f"{prefix}.{key}",
|
|
166
|
+
tags_as_object,
|
|
167
|
+
applied_variants,
|
|
168
|
+
updated_meta_key,
|
|
169
|
+
should_apply_variant,
|
|
170
|
+
)
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
if isinstance(value, dict):
|
|
174
|
+
value["$"] = getTag(
|
|
175
|
+
value,
|
|
176
|
+
f"{prefix}.{key}",
|
|
177
|
+
tags_as_object,
|
|
178
|
+
locale,
|
|
179
|
+
applied_variants,
|
|
180
|
+
should_apply_variant,
|
|
181
|
+
meta_key=updated_meta_key,
|
|
182
|
+
)
|
|
183
|
+
tags[key] = _tags_value(
|
|
184
|
+
f"{prefix}.{key}",
|
|
185
|
+
tags_as_object,
|
|
186
|
+
applied_variants,
|
|
187
|
+
updated_meta_key,
|
|
188
|
+
should_apply_variant,
|
|
189
|
+
)
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
tags[key] = _tags_value(
|
|
193
|
+
f"{prefix}.{key}",
|
|
194
|
+
tags_as_object,
|
|
195
|
+
applied_variants,
|
|
196
|
+
updated_meta_key,
|
|
197
|
+
should_apply_variant,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
return tags
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def addTags( # pylint: disable=invalid-name
|
|
204
|
+
entry: Optional[dict],
|
|
205
|
+
contentTypeUid: str,
|
|
206
|
+
tagsAsObject: bool,
|
|
207
|
+
locale: str = "en-us",
|
|
208
|
+
options: Optional[dict] = None,
|
|
209
|
+
) -> None:
|
|
210
|
+
"""
|
|
211
|
+
Port of JS addTags() from `src/entry-editable.ts`.
|
|
212
|
+
Mutates `entry` by attaching a `$` dict of CSLP tags.
|
|
213
|
+
"""
|
|
214
|
+
if not entry:
|
|
215
|
+
return
|
|
216
|
+
|
|
217
|
+
use_lower_case_locale = True
|
|
218
|
+
if isinstance(options, dict) and "useLowerCaseLocale" in options:
|
|
219
|
+
use_lower_case_locale = bool(options.get("useLowerCaseLocale"))
|
|
220
|
+
|
|
221
|
+
content_type_uid = (contentTypeUid or "").lower()
|
|
222
|
+
resolved_locale = (locale or "en-us")
|
|
223
|
+
if use_lower_case_locale:
|
|
224
|
+
resolved_locale = resolved_locale.lower()
|
|
225
|
+
|
|
226
|
+
applied_variants = entry.get("_applied_variants")
|
|
227
|
+
if applied_variants is None and isinstance(entry.get("system"), dict):
|
|
228
|
+
applied_variants = cast(dict, entry["system"]).get("applied_variants")
|
|
229
|
+
should_apply_variant = bool(applied_variants)
|
|
230
|
+
|
|
231
|
+
entry["$"] = getTag(
|
|
232
|
+
entry,
|
|
233
|
+
f"{content_type_uid}.{entry.get('uid')}.{resolved_locale}",
|
|
234
|
+
tagsAsObject,
|
|
235
|
+
resolved_locale,
|
|
236
|
+
cast(AppliedVariants, applied_variants),
|
|
237
|
+
should_apply_variant,
|
|
238
|
+
meta_key="",
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# JS parity export name
|
|
243
|
+
addEditableTags = addTags # pylint: disable=invalid-name
|
|
244
|
+
|
|
245
|
+
# Pythonic aliases
|
|
246
|
+
add_tags = addTags
|
|
247
|
+
get_tags = getTag
|
|
248
|
+
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# pylint: disable=missing-function-docstring
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from lxml import etree
|
|
7
|
+
|
|
8
|
+
from contentstack_utils.automate import Automate
|
|
9
|
+
from contentstack_utils.entry_editable import addEditableTags as _addEditableTags
|
|
10
|
+
from contentstack_utils.entry_editable import addTags as _addTags
|
|
11
|
+
from contentstack_utils.entry_editable import getTag as _getTag
|
|
12
|
+
from contentstack_utils.helper.converter import convert_style
|
|
13
|
+
from contentstack_utils.helper.metadata import Metadata
|
|
14
|
+
from contentstack_utils.render.options import Options
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Utils(Automate):
|
|
18
|
+
# JS parity helpers (moved to `contentstack_utils/entry_editable.py`)
|
|
19
|
+
@staticmethod
|
|
20
|
+
def addTags( # pylint: disable=invalid-name
|
|
21
|
+
entry: dict,
|
|
22
|
+
contentTypeUid: str,
|
|
23
|
+
tagsAsObject: Optional[bool] = None,
|
|
24
|
+
locale: str = "en-us",
|
|
25
|
+
options: Optional[dict] = None,
|
|
26
|
+
**kwargs,
|
|
27
|
+
) -> None:
|
|
28
|
+
# Support pythonic kwarg name too (backward compatibility with earlier port).
|
|
29
|
+
if tagsAsObject is None and "tags_as_object" in kwargs:
|
|
30
|
+
tagsAsObject = bool(kwargs["tags_as_object"])
|
|
31
|
+
if tagsAsObject is None:
|
|
32
|
+
tagsAsObject = False
|
|
33
|
+
return _addTags(entry, contentTypeUid, tagsAsObject, locale, options)
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def addEditableTags( # pylint: disable=invalid-name
|
|
37
|
+
entry: dict,
|
|
38
|
+
contentTypeUid: str,
|
|
39
|
+
tagsAsObject: Optional[bool] = None,
|
|
40
|
+
locale: str = "en-us",
|
|
41
|
+
options: Optional[dict] = None,
|
|
42
|
+
**kwargs,
|
|
43
|
+
) -> None:
|
|
44
|
+
if tagsAsObject is None and "tags_as_object" in kwargs:
|
|
45
|
+
tagsAsObject = bool(kwargs["tags_as_object"])
|
|
46
|
+
if tagsAsObject is None:
|
|
47
|
+
tagsAsObject = False
|
|
48
|
+
return _addEditableTags(entry, contentTypeUid, tagsAsObject, locale, options)
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def getTag( # pylint: disable=invalid-name
|
|
52
|
+
content: Any,
|
|
53
|
+
prefix: str,
|
|
54
|
+
tagsAsObject: bool,
|
|
55
|
+
locale: str,
|
|
56
|
+
appliedVariants: Optional[dict],
|
|
57
|
+
shouldApplyVariant: bool,
|
|
58
|
+
metaKey: str = "",
|
|
59
|
+
) -> Dict[str, Any]:
|
|
60
|
+
# Keep JS argument names for parity.
|
|
61
|
+
return _getTag(content, prefix, tagsAsObject, locale, appliedVariants, shouldApplyVariant, metaKey)
|
|
62
|
+
|
|
63
|
+
# Pythonic aliases
|
|
64
|
+
add_tags = addTags
|
|
65
|
+
get_tags = getTag
|
|
66
|
+
get_tag = getTag
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def _variants_map_from_entry(entry: dict) -> dict:
|
|
70
|
+
publish_details = entry.get("publish_details")
|
|
71
|
+
if not isinstance(publish_details, dict):
|
|
72
|
+
return {}
|
|
73
|
+
raw = publish_details.get("variants")
|
|
74
|
+
return raw if isinstance(raw, dict) else {}
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _aliases_from_variants_map(variants_map: dict) -> List[str]:
|
|
78
|
+
aliases: List[str] = []
|
|
79
|
+
for _variant_uid, value in variants_map.items():
|
|
80
|
+
if not isinstance(value, dict):
|
|
81
|
+
continue
|
|
82
|
+
alias = value.get("alias")
|
|
83
|
+
if alias is None:
|
|
84
|
+
continue
|
|
85
|
+
alias_str = str(alias).strip()
|
|
86
|
+
if alias_str:
|
|
87
|
+
aliases.append(alias_str)
|
|
88
|
+
return aliases
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def _variant_aliases_for_entry(entry: dict, content_type_uid: str = "") -> Dict[str, Any]:
|
|
92
|
+
if entry is None:
|
|
93
|
+
raise ValueError("entry cannot be None")
|
|
94
|
+
if not isinstance(entry, dict):
|
|
95
|
+
raise TypeError("entry must be a dict")
|
|
96
|
+
uid = entry.get("uid")
|
|
97
|
+
if uid is None or (isinstance(uid, str) and uid.strip() == ""):
|
|
98
|
+
raise ValueError("entry must contain a non-empty uid")
|
|
99
|
+
entry_uid = str(uid)
|
|
100
|
+
ct = entry.get("_content_type_uid")
|
|
101
|
+
if ct is None or ct == "":
|
|
102
|
+
ct = content_type_uid or ""
|
|
103
|
+
contenttype_uid = "" if ct is None else str(ct)
|
|
104
|
+
variants_map = Utils._variants_map_from_entry(entry)
|
|
105
|
+
aliases = Utils._aliases_from_variants_map(variants_map)
|
|
106
|
+
return {
|
|
107
|
+
"entry_uid": entry_uid,
|
|
108
|
+
"contenttype_uid": contenttype_uid,
|
|
109
|
+
"variants": aliases,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def get_variant_aliases(
|
|
114
|
+
entry_or_entries: Union[dict, List[dict]],
|
|
115
|
+
content_type_uid: str = "",
|
|
116
|
+
) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
|
|
117
|
+
"""
|
|
118
|
+
Extract variant aliases from a CDA entry (or list of entries).
|
|
119
|
+
|
|
120
|
+
The entry must have been fetched with ``x-cs-variant-uid`` set to variant
|
|
121
|
+
aliases (not UIDs) for ``publish_details.variants`` to be present.
|
|
122
|
+
|
|
123
|
+
:param entry_or_entries: A single entry dict, or a list of entry dicts.
|
|
124
|
+
:param content_type_uid: Used when ``entry._content_type_uid`` is absent;
|
|
125
|
+
ignored when ``entry_or_entries`` is a list (each entry supplies its own).
|
|
126
|
+
:raises ValueError: if ``entry_or_entries`` is None, an entry is None, or an
|
|
127
|
+
entry has no non-empty ``uid``.
|
|
128
|
+
:raises TypeError: if a single entry is not a dict, or a list is expected but
|
|
129
|
+
another type was passed for the multi-entry overload.
|
|
130
|
+
"""
|
|
131
|
+
if entry_or_entries is None:
|
|
132
|
+
raise ValueError("entry is required and cannot be None")
|
|
133
|
+
if isinstance(entry_or_entries, list):
|
|
134
|
+
return [Utils._variant_aliases_for_entry(e, "") for e in entry_or_entries]
|
|
135
|
+
if isinstance(entry_or_entries, dict):
|
|
136
|
+
return Utils._variant_aliases_for_entry(entry_or_entries, content_type_uid or "")
|
|
137
|
+
raise TypeError("entry must be a dict or a list of dicts")
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def get_variant_metadata_tags(entries: List[dict]) -> Dict[str, str]:
|
|
141
|
+
"""
|
|
142
|
+
Build a ``data-csvariants`` HTML data-attribute payload from entry objects.
|
|
143
|
+
|
|
144
|
+
:param entries: List of CDA entry dicts (same shape as for multi-entry
|
|
145
|
+
:meth:`get_variant_aliases`).
|
|
146
|
+
:raises ValueError: if ``entries`` is None.
|
|
147
|
+
:raises TypeError: if ``entries`` is not a list.
|
|
148
|
+
"""
|
|
149
|
+
if entries is None:
|
|
150
|
+
raise ValueError("entries is required and cannot be None")
|
|
151
|
+
if not isinstance(entries, list):
|
|
152
|
+
raise TypeError("entries must be a list")
|
|
153
|
+
results = Utils.get_variant_aliases(entries)
|
|
154
|
+
payload = json.dumps(results, separators=(",", ":"))
|
|
155
|
+
return {"data-csvariants": payload}
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def render(entry_obj, key_path: list, option: Options):
|
|
159
|
+
valid = Automate.is_json(entry_obj)
|
|
160
|
+
if not valid:
|
|
161
|
+
raise FileNotFoundError('Invalid file found')
|
|
162
|
+
|
|
163
|
+
if isinstance(entry_obj, list):
|
|
164
|
+
for entry in entry_obj:
|
|
165
|
+
Utils.render(entry, key_path, option)
|
|
166
|
+
|
|
167
|
+
if isinstance(entry_obj, dict):
|
|
168
|
+
Automate._get_embedded_keys(entry_obj, key_path, option, render_callback=Utils.render_content)
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def render_content(rte_content, embed_obj: dict, option: Options) -> object:
|
|
172
|
+
if isinstance(rte_content, str):
|
|
173
|
+
return Utils.__get_embedded_objects(rte_content, embed_obj, option)
|
|
174
|
+
elif isinstance(rte_content, list):
|
|
175
|
+
render_callback = []
|
|
176
|
+
for rte in rte_content:
|
|
177
|
+
render_callback.append(Utils.render_content(rte, embed_obj, option))
|
|
178
|
+
return render_callback
|
|
179
|
+
return rte_content
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def __get_embedded_objects(html_doc, entry, option):
|
|
183
|
+
import re
|
|
184
|
+
document = f"<items>{html_doc}</items>"
|
|
185
|
+
tag = etree.fromstring(document)
|
|
186
|
+
html_doc = etree.tostring(tag).decode('utf-8')
|
|
187
|
+
html_doc = re.sub('(?ms)<%s[^>]*>(.*)</%s>' % (tag.tag, tag.tag), '\\1', html_doc)
|
|
188
|
+
elements = tag.xpath("//*[contains(@class, 'embedded-asset') or contains(@class, 'embedded-entry')]")
|
|
189
|
+
metadata = Utils.__get_metadata(elements)
|
|
190
|
+
string_content = Utils._str_from_embed_items(metadata=metadata, entry=entry, option=option)
|
|
191
|
+
html_doc = html_doc.replace(metadata.outer_html, string_content)
|
|
192
|
+
return html_doc
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def _str_from_embed_items(metadata, entry, option):
|
|
196
|
+
if '_embedded_items' in entry:
|
|
197
|
+
items = entry['_embedded_items'].keys()
|
|
198
|
+
for item in items:
|
|
199
|
+
items_array = entry['_embedded_items'][item]
|
|
200
|
+
content = Automate._find_embedded_entry(items_array, metadata)
|
|
201
|
+
if content is not None:
|
|
202
|
+
return option.render_options(content, metadata)
|
|
203
|
+
return ''
|
|
204
|
+
|
|
205
|
+
@staticmethod
|
|
206
|
+
def __get_metadata(elements):
|
|
207
|
+
for element in elements:
|
|
208
|
+
content_type = None
|
|
209
|
+
typeof = element.attrib['type']
|
|
210
|
+
if typeof == 'asset':
|
|
211
|
+
uid = element.attrib['data-sys-asset-uid']
|
|
212
|
+
else:
|
|
213
|
+
uid = element.attrib['data-sys-entry-uid']
|
|
214
|
+
content_type = element.attrib['data-sys-content-type-uid']
|
|
215
|
+
style = element.attrib['sys-style-type']
|
|
216
|
+
outer_html = etree.tostring(element).decode('utf-8')
|
|
217
|
+
attributes = element.attrib
|
|
218
|
+
style = convert_style(style)
|
|
219
|
+
metadata = Metadata(element.text, typeof, uid, content_type, style, outer_html, attributes)
|
|
220
|
+
return metadata
|
|
221
|
+
|
|
222
|
+
####################################################
|
|
223
|
+
# SUPERCHARGED #
|
|
224
|
+
####################################################
|
|
225
|
+
|
|
226
|
+
@staticmethod
|
|
227
|
+
def json_to_html(entry_obj, key_path: list, option: Options):
|
|
228
|
+
if not Automate.is_json(entry_obj):
|
|
229
|
+
raise FileNotFoundError('Could not process invalid content')
|
|
230
|
+
if isinstance(entry_obj, list):
|
|
231
|
+
for entry in entry_obj:
|
|
232
|
+
return Utils.json_to_html(entry, key_path, option)
|
|
233
|
+
if isinstance(entry_obj, dict):
|
|
234
|
+
if key_path is not None:
|
|
235
|
+
for path in key_path:
|
|
236
|
+
render_callback = Automate._enumerate_content(entry_obj, path, option)
|
|
237
|
+
# Automate._find_embed_keys(entry_obj, path, option, render_callback) This method used in GQL class.
|
|
238
|
+
return render_callback
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentstack_utils
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: contentstack_utils is a Utility package for Contentstack headless CMS with an API-first approach.
|
|
5
5
|
Home-page: https://github.com/contentstack/contentstack-utils-python
|
|
6
6
|
Author: contentstack
|
{contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils.egg-info/SOURCES.txt
RENAMED
|
@@ -3,6 +3,7 @@ README.md
|
|
|
3
3
|
setup.py
|
|
4
4
|
contentstack_utils/__init__.py
|
|
5
5
|
contentstack_utils/automate.py
|
|
6
|
+
contentstack_utils/entry_editable.py
|
|
6
7
|
contentstack_utils/gql.py
|
|
7
8
|
contentstack_utils/utils.py
|
|
8
9
|
contentstack_utils.egg-info/PKG-INFO
|
|
@@ -21,6 +22,7 @@ contentstack_utils/render/options.py
|
|
|
21
22
|
tests/__init__.py
|
|
22
23
|
tests/convert_style.py
|
|
23
24
|
tests/test_default_opt_others.py
|
|
25
|
+
tests/test_editable_tags.py
|
|
24
26
|
tests/test_gql_to_html_func.py
|
|
25
27
|
tests/test_helper_node_to_html.py
|
|
26
28
|
tests/test_item_types.py
|
|
@@ -30,4 +32,5 @@ tests/test_render_default_options.py
|
|
|
30
32
|
tests/test_render_options.py
|
|
31
33
|
tests/test_style_type.py
|
|
32
34
|
tests/test_util_srte.py
|
|
33
|
-
tests/test_utils.py
|
|
35
|
+
tests/test_utils.py
|
|
36
|
+
tests/test_variant_aliases.py
|
|
@@ -10,7 +10,7 @@ class TestConvertStyle(unittest.TestCase):
|
|
|
10
10
|
|
|
11
11
|
def test_converter_style_block(self):
|
|
12
12
|
_returns = converter.convert_style('block')
|
|
13
|
-
self.
|
|
13
|
+
self.assertEqual(StyleType.BLOCK, _returns)
|
|
14
14
|
|
|
15
15
|
def test_converter_style_inline(self):
|
|
16
16
|
_returns = converter.convert_style('inline')
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from contentstack_utils.utils import Utils
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestEditableTags(unittest.TestCase):
|
|
7
|
+
def test_add_tags_mutates_entry_with_dollar_map(self):
|
|
8
|
+
entry = {"uid": "e1", "title": "Hello", "count": 1}
|
|
9
|
+
Utils.addTags(entry, "Blog_Post", tags_as_object=True, locale="EN-us")
|
|
10
|
+
self.assertIn("$", entry)
|
|
11
|
+
self.assertEqual(entry["$"]["title"], {"data-cslp": "blog_post.e1.en-us.title"})
|
|
12
|
+
self.assertEqual(entry["$"]["count"], {"data-cslp": "blog_post.e1.en-us.count"})
|
|
13
|
+
|
|
14
|
+
def test_add_tags_string_mode(self):
|
|
15
|
+
entry = {"uid": "e1", "title": "Hello"}
|
|
16
|
+
Utils.addTags(entry, "blog_post", tags_as_object=False, locale="en-us")
|
|
17
|
+
self.assertEqual(entry["$"]["title"], "data-cslp=blog_post.e1.en-us.title")
|
|
18
|
+
|
|
19
|
+
def test_array_tags_add_index_and_parent_keys(self):
|
|
20
|
+
entry = {"uid": "e1", "array": ["hello", "world"]}
|
|
21
|
+
Utils.addTags(entry, "blog", tags_as_object=True, locale="en-us")
|
|
22
|
+
self.assertEqual(entry["$"]["array"], {"data-cslp": "blog.e1.en-us.array"})
|
|
23
|
+
self.assertEqual(entry["$"]["array__0"], {"data-cslp": "blog.e1.en-us.array.0"})
|
|
24
|
+
self.assertEqual(entry["$"]["array__1"], {"data-cslp": "blog.e1.en-us.array.1"})
|
|
25
|
+
self.assertEqual(entry["$"]["array__parent"], {"data-cslp-parent-field": "blog.e1.en-us.array"})
|
|
26
|
+
|
|
27
|
+
def test_reference_entry_inside_array_gets_own_dollar(self):
|
|
28
|
+
entry = {
|
|
29
|
+
"uid": "e1",
|
|
30
|
+
"refs": [
|
|
31
|
+
{
|
|
32
|
+
"uid": "r1",
|
|
33
|
+
"_content_type_uid": "ref_ct",
|
|
34
|
+
"title": "Ref Title",
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
}
|
|
38
|
+
Utils.addTags(entry, "blog", tags_as_object=True, locale="en-us")
|
|
39
|
+
ref = entry["refs"][0]
|
|
40
|
+
self.assertIn("$", ref)
|
|
41
|
+
self.assertEqual(ref["$"]["title"], {"data-cslp": "ref_ct.r1.en-us.title"})
|
|
42
|
+
|
|
43
|
+
def test_variantised_field_applies_v2_prefix_and_uid_suffix(self):
|
|
44
|
+
entry = {
|
|
45
|
+
"uid": "e1",
|
|
46
|
+
"_applied_variants": {"title": "v123"},
|
|
47
|
+
"title": {"value": "Hello"},
|
|
48
|
+
}
|
|
49
|
+
Utils.addTags(entry, "blog", tags_as_object=True, locale="en-us")
|
|
50
|
+
# title is an object; ensure we tag the object itself (like JS) and apply variant.
|
|
51
|
+
self.assertEqual(entry["$"]["title"], {"data-cslp": "v2:blog.e1_v123.en-us.title"})
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
unittest.main()
|
|
56
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
from contentstack_utils.utils import Utils
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestVariantAliases(unittest.TestCase):
|
|
8
|
+
|
|
9
|
+
def _sample_entry(self):
|
|
10
|
+
return {
|
|
11
|
+
"uid": "blt3e91e3812a44ba90",
|
|
12
|
+
"_content_type_uid": "landing_page",
|
|
13
|
+
"publish_details": {
|
|
14
|
+
"variants": {
|
|
15
|
+
"cs669f1759b774fe1d": {
|
|
16
|
+
"alias": "cs_personalize_0_2",
|
|
17
|
+
"environment": "bltb5963e2163c24eb6",
|
|
18
|
+
"locale": "en",
|
|
19
|
+
},
|
|
20
|
+
"csbf165536748bdee2": {
|
|
21
|
+
"alias": "cs_personalize_0_1",
|
|
22
|
+
"environment": "bltb5963e2163c24eb6",
|
|
23
|
+
"locale": "en",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def test_single_entry_extracts_aliases(self):
|
|
30
|
+
result = Utils.get_variant_aliases(self._sample_entry())
|
|
31
|
+
self.assertEqual(result["entry_uid"], "blt3e91e3812a44ba90")
|
|
32
|
+
self.assertEqual(result["contenttype_uid"], "landing_page")
|
|
33
|
+
self.assertEqual(
|
|
34
|
+
result["variants"],
|
|
35
|
+
["cs_personalize_0_2", "cs_personalize_0_1"],
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def test_content_type_from_parameter_when_missing_on_entry(self):
|
|
39
|
+
entry = {
|
|
40
|
+
"uid": "blt1",
|
|
41
|
+
"publish_details": {"variants": {}},
|
|
42
|
+
}
|
|
43
|
+
result = Utils.get_variant_aliases(entry, "landing_page")
|
|
44
|
+
self.assertEqual(result["contenttype_uid"], "landing_page")
|
|
45
|
+
|
|
46
|
+
def test_empty_contenttype_when_missing(self):
|
|
47
|
+
entry = {"uid": "blt1", "publish_details": {"variants": {}}}
|
|
48
|
+
result = Utils.get_variant_aliases(entry)
|
|
49
|
+
self.assertEqual(result["contenttype_uid"], "")
|
|
50
|
+
|
|
51
|
+
def test_missing_publish_details(self):
|
|
52
|
+
entry = {"uid": "blt1", "_content_type_uid": "page"}
|
|
53
|
+
result = Utils.get_variant_aliases(entry)
|
|
54
|
+
self.assertEqual(result["variants"], [])
|
|
55
|
+
|
|
56
|
+
def test_missing_variants_key(self):
|
|
57
|
+
entry = {"uid": "blt1", "publish_details": {}}
|
|
58
|
+
result = Utils.get_variant_aliases(entry)
|
|
59
|
+
self.assertEqual(result["variants"], [])
|
|
60
|
+
|
|
61
|
+
def test_empty_variants_object(self):
|
|
62
|
+
entry = {"uid": "blt1", "publish_details": {"variants": {}}}
|
|
63
|
+
result = Utils.get_variant_aliases(entry)
|
|
64
|
+
self.assertEqual(result["variants"], [])
|
|
65
|
+
|
|
66
|
+
def test_skips_variant_without_alias(self):
|
|
67
|
+
entry = {
|
|
68
|
+
"uid": "blt1",
|
|
69
|
+
"publish_details": {
|
|
70
|
+
"variants": {
|
|
71
|
+
"a": {"alias": "ok"},
|
|
72
|
+
"b": {},
|
|
73
|
+
"c": {"alias": ""},
|
|
74
|
+
"d": {"alias": " "},
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
result = Utils.get_variant_aliases(entry)
|
|
79
|
+
self.assertEqual(result["variants"], ["ok"])
|
|
80
|
+
|
|
81
|
+
def test_non_dict_variant_value_skipped(self):
|
|
82
|
+
entry = {
|
|
83
|
+
"uid": "blt1",
|
|
84
|
+
"publish_details": {"variants": {"x": "not-a-dict"}},
|
|
85
|
+
}
|
|
86
|
+
result = Utils.get_variant_aliases(entry)
|
|
87
|
+
self.assertEqual(result["variants"], [])
|
|
88
|
+
|
|
89
|
+
def test_none_entry_raises(self):
|
|
90
|
+
with self.assertRaises(ValueError):
|
|
91
|
+
Utils.get_variant_aliases(None)
|
|
92
|
+
|
|
93
|
+
def test_missing_uid_raises(self):
|
|
94
|
+
with self.assertRaises(ValueError):
|
|
95
|
+
Utils.get_variant_aliases({"publish_details": {}})
|
|
96
|
+
|
|
97
|
+
def test_empty_uid_raises(self):
|
|
98
|
+
with self.assertRaises(ValueError):
|
|
99
|
+
Utils.get_variant_aliases({"uid": ""})
|
|
100
|
+
|
|
101
|
+
def test_whitespace_uid_raises(self):
|
|
102
|
+
with self.assertRaises(ValueError):
|
|
103
|
+
Utils.get_variant_aliases({"uid": " "})
|
|
104
|
+
|
|
105
|
+
def test_non_dict_entry_raises(self):
|
|
106
|
+
with self.assertRaises(TypeError):
|
|
107
|
+
Utils.get_variant_aliases("not-a-dict")
|
|
108
|
+
|
|
109
|
+
def test_multiple_entries(self):
|
|
110
|
+
results = Utils.get_variant_aliases(
|
|
111
|
+
[
|
|
112
|
+
{
|
|
113
|
+
"uid": "blt123",
|
|
114
|
+
"_content_type_uid": "page",
|
|
115
|
+
"publish_details": {
|
|
116
|
+
"variants": {
|
|
117
|
+
"v1": {"alias": "cs_personalize_3_1"},
|
|
118
|
+
"v2": {"alias": "cs_personalize_4_0"},
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{"uid": "blt456", "_content_type_uid": "page"},
|
|
123
|
+
]
|
|
124
|
+
)
|
|
125
|
+
self.assertEqual(len(results), 2)
|
|
126
|
+
self.assertEqual(results[0]["entry_uid"], "blt123")
|
|
127
|
+
self.assertEqual(
|
|
128
|
+
results[0]["variants"],
|
|
129
|
+
["cs_personalize_3_1", "cs_personalize_4_0"],
|
|
130
|
+
)
|
|
131
|
+
self.assertEqual(results[1]["variants"], [])
|
|
132
|
+
|
|
133
|
+
def test_list_entry_none_raises(self):
|
|
134
|
+
with self.assertRaises(ValueError):
|
|
135
|
+
Utils.get_variant_aliases([None])
|
|
136
|
+
|
|
137
|
+
def test_get_variant_metadata_tags(self):
|
|
138
|
+
entries = [
|
|
139
|
+
{
|
|
140
|
+
"uid": "blt123",
|
|
141
|
+
"_content_type_uid": "page",
|
|
142
|
+
"publish_details": {
|
|
143
|
+
"variants": {"v1": {"alias": "cs_personalize_3_1"}}
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
tag = Utils.get_variant_metadata_tags(entries)
|
|
148
|
+
self.assertIn("data-csvariants", tag)
|
|
149
|
+
parsed = json.loads(tag["data-csvariants"])
|
|
150
|
+
self.assertEqual(len(parsed), 1)
|
|
151
|
+
self.assertEqual(parsed[0]["entry_uid"], "blt123")
|
|
152
|
+
self.assertEqual(parsed[0]["variants"], ["cs_personalize_3_1"])
|
|
153
|
+
|
|
154
|
+
def test_get_variant_metadata_tags_none_raises(self):
|
|
155
|
+
with self.assertRaises(ValueError):
|
|
156
|
+
Utils.get_variant_metadata_tags(None)
|
|
157
|
+
|
|
158
|
+
def test_get_variant_metadata_tags_not_list_raises(self):
|
|
159
|
+
with self.assertRaises(TypeError):
|
|
160
|
+
Utils.get_variant_metadata_tags({})
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
unittest.main()
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# pylint: disable=missing-function-docstring
|
|
2
|
-
|
|
3
|
-
from lxml import etree
|
|
4
|
-
|
|
5
|
-
from contentstack_utils.automate import Automate
|
|
6
|
-
from contentstack_utils.helper.converter import convert_style
|
|
7
|
-
from contentstack_utils.helper.metadata import Metadata
|
|
8
|
-
from contentstack_utils.render.options import Options
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Utils(Automate):
|
|
12
|
-
|
|
13
|
-
@staticmethod
|
|
14
|
-
def render(entry_obj, key_path: list, option: Options):
|
|
15
|
-
valid = Automate.is_json(entry_obj)
|
|
16
|
-
if not valid:
|
|
17
|
-
raise FileNotFoundError('Invalid file found')
|
|
18
|
-
|
|
19
|
-
if isinstance(entry_obj, list):
|
|
20
|
-
for entry in entry_obj:
|
|
21
|
-
Utils.render(entry, key_path, option)
|
|
22
|
-
|
|
23
|
-
if isinstance(entry_obj, dict):
|
|
24
|
-
Automate._get_embedded_keys(entry_obj, key_path, option, render_callback=Utils.render_content)
|
|
25
|
-
|
|
26
|
-
@staticmethod
|
|
27
|
-
def render_content(rte_content, embed_obj: dict, option: Options) -> object:
|
|
28
|
-
if isinstance(rte_content, str):
|
|
29
|
-
return Utils.__get_embedded_objects(rte_content, embed_obj, option)
|
|
30
|
-
elif isinstance(rte_content, list):
|
|
31
|
-
render_callback = []
|
|
32
|
-
for rte in rte_content:
|
|
33
|
-
render_callback.append(Utils.render_content(rte, embed_obj, option))
|
|
34
|
-
return render_callback
|
|
35
|
-
return rte_content
|
|
36
|
-
|
|
37
|
-
@staticmethod
|
|
38
|
-
def __get_embedded_objects(html_doc, entry, option):
|
|
39
|
-
import re
|
|
40
|
-
document = f"<items>{html_doc}</items>"
|
|
41
|
-
tag = etree.fromstring(document)
|
|
42
|
-
html_doc = etree.tostring(tag).decode('utf-8')
|
|
43
|
-
html_doc = re.sub('(?ms)<%s[^>]*>(.*)</%s>' % (tag.tag, tag.tag), '\\1', html_doc)
|
|
44
|
-
elements = tag.xpath("//*[contains(@class, 'embedded-asset') or contains(@class, 'embedded-entry')]")
|
|
45
|
-
metadata = Utils.__get_metadata(elements)
|
|
46
|
-
string_content = Utils._str_from_embed_items(metadata=metadata, entry=entry, option=option)
|
|
47
|
-
html_doc = html_doc.replace(metadata.outer_html, string_content)
|
|
48
|
-
return html_doc
|
|
49
|
-
|
|
50
|
-
@staticmethod
|
|
51
|
-
def _str_from_embed_items(metadata, entry, option):
|
|
52
|
-
if '_embedded_items' in entry:
|
|
53
|
-
items = entry['_embedded_items'].keys()
|
|
54
|
-
for item in items:
|
|
55
|
-
items_array = entry['_embedded_items'][item]
|
|
56
|
-
content = Automate._find_embedded_entry(items_array, metadata)
|
|
57
|
-
if content is not None:
|
|
58
|
-
return option.render_options(content, metadata)
|
|
59
|
-
return ''
|
|
60
|
-
|
|
61
|
-
@staticmethod
|
|
62
|
-
def __get_metadata(elements):
|
|
63
|
-
for element in elements:
|
|
64
|
-
content_type = None
|
|
65
|
-
typeof = element.attrib['type']
|
|
66
|
-
if typeof == 'asset':
|
|
67
|
-
uid = element.attrib['data-sys-asset-uid']
|
|
68
|
-
else:
|
|
69
|
-
uid = element.attrib['data-sys-entry-uid']
|
|
70
|
-
content_type = element.attrib['data-sys-content-type-uid']
|
|
71
|
-
style = element.attrib['sys-style-type']
|
|
72
|
-
outer_html = etree.tostring(element).decode('utf-8')
|
|
73
|
-
attributes = element.attrib
|
|
74
|
-
style = convert_style(style)
|
|
75
|
-
metadata = Metadata(element.text, typeof, uid, content_type, style, outer_html, attributes)
|
|
76
|
-
return metadata
|
|
77
|
-
|
|
78
|
-
####################################################
|
|
79
|
-
# SUPERCHARGED #
|
|
80
|
-
####################################################
|
|
81
|
-
|
|
82
|
-
@staticmethod
|
|
83
|
-
def json_to_html(entry_obj, key_path: list, option: Options):
|
|
84
|
-
if not Automate.is_json(entry_obj):
|
|
85
|
-
raise FileNotFoundError('Could not process invalid content')
|
|
86
|
-
if isinstance(entry_obj, list):
|
|
87
|
-
for entry in entry_obj:
|
|
88
|
-
return Utils.json_to_html(entry, key_path, option)
|
|
89
|
-
if isinstance(entry_obj, dict):
|
|
90
|
-
if key_path is not None:
|
|
91
|
-
for path in key_path:
|
|
92
|
-
render_callback = Automate._enumerate_content(entry_obj, path, option)
|
|
93
|
-
# Automate._find_embed_keys(entry_obj, path, option, render_callback) This method used in GQL class.
|
|
94
|
-
return render_callback
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/embedded/__init__.py
RENAMED
|
File without changes
|
{contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/embedded/item_type.py
RENAMED
|
File without changes
|
{contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/embedded/styletype.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/helper/converter.py
RENAMED
|
File without changes
|
|
File without changes
|
{contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils/helper/node_to_html.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentstack_utils-1.3.3 → contentstack_utils-1.5.0}/contentstack_utils.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|