contentstack-utils 1.2.3__py3-none-any.whl → 1.3.0__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.
- contentstack_utils/__init__.py +36 -0
- contentstack_utils/automate.py +176 -0
- contentstack_utils/embedded/__init__.py +1 -0
- contentstack_utils/embedded/item_type.py +15 -0
- contentstack_utils/embedded/styletype.py +20 -0
- contentstack_utils/gql.py +37 -0
- contentstack_utils/helper/__init__.py +1 -0
- contentstack_utils/helper/converter.py +14 -0
- contentstack_utils/helper/metadata.py +65 -0
- contentstack_utils/helper/node_to_html.py +28 -0
- contentstack_utils/render/__init__.py +1 -0
- contentstack_utils/render/options.py +126 -0
- contentstack_utils/utils.py +94 -0
- {contentstack_utils-1.2.3.dist-info → contentstack_utils-1.3.0.dist-info}/METADATA +1 -1
- contentstack_utils-1.3.0.dist-info/RECORD +31 -0
- {contentstack_utils-1.2.3.dist-info → contentstack_utils-1.3.0.dist-info}/WHEEL +1 -1
- contentstack_utils-1.3.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +52 -0
- tests/convert_style.py +29 -0
- tests/test_default_opt_others.py +44 -0
- tests/test_gql_to_html_func.py +37 -0
- tests/test_helper_node_to_html.py +61 -0
- tests/test_item_types.py +13 -0
- tests/test_metadata.py +53 -0
- tests/test_option_render_mark.py +52 -0
- tests/test_render_default_options.py +82 -0
- tests/test_render_options.py +36 -0
- tests/test_style_type.py +26 -0
- tests/test_util_srte.py +135 -0
- tests/test_utils.py +80 -0
- contentstack_utils-1.2.3.dist-info/RECORD +0 -5
- contentstack_utils-1.2.3.dist-info/top_level.txt +0 -1
- {contentstack_utils-1.2.3.dist-info → contentstack_utils-1.3.0.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# pip install -r requirements.txt
|
|
2
|
+
# pytest --html=tests/report/test-report.html
|
|
3
|
+
# coverage report -m
|
|
4
|
+
# coverage html -d coveragereport
|
|
5
|
+
"""
|
|
6
|
+
__author__, __status__, __version__, __endpoint__ and __email__
|
|
7
|
+
|
|
8
|
+
`Your code has been rated at 10.00/10`
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from contentstack_utils.embedded.item_type import ItemType
|
|
12
|
+
from contentstack_utils.embedded.styletype import StyleType
|
|
13
|
+
from contentstack_utils.helper.metadata import Metadata
|
|
14
|
+
from contentstack_utils.helper.node_to_html import NodeToHtml
|
|
15
|
+
from contentstack_utils.render.options import Options
|
|
16
|
+
from contentstack_utils.utils import Utils
|
|
17
|
+
from contentstack_utils.gql import GQL
|
|
18
|
+
from contentstack_utils.automate import Automate
|
|
19
|
+
|
|
20
|
+
__all__ = (
|
|
21
|
+
"Utils",
|
|
22
|
+
"Options",
|
|
23
|
+
"Metadata",
|
|
24
|
+
"GQL",
|
|
25
|
+
"Automate",
|
|
26
|
+
"StyleType",
|
|
27
|
+
"ItemType",
|
|
28
|
+
"NodeToHtml"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__title__ = 'contentstack_utils'
|
|
32
|
+
__author__ = 'contentstack'
|
|
33
|
+
__status__ = 'debug'
|
|
34
|
+
__version__ = '1.3.0'
|
|
35
|
+
__endpoint__ = 'cdn.contentstack.io'
|
|
36
|
+
__contact__ = 'support@contentstack.com'
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from contentstack_utils.helper.converter import convert_style
|
|
3
|
+
from contentstack_utils.helper.metadata import Metadata
|
|
4
|
+
from contentstack_utils.helper.node_to_html import NodeToHtml
|
|
5
|
+
from contentstack_utils.render.options import Options
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Automate:
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def _str_from_embed_items(metadata, entry, option):
|
|
12
|
+
if isinstance(entry, list):
|
|
13
|
+
for node in entry:
|
|
14
|
+
uid = node['node']['uid']
|
|
15
|
+
if uid == metadata.get_item_uid:
|
|
16
|
+
return option.render_options(node['node'], metadata)
|
|
17
|
+
elif isinstance(entry, dict) and '_embedded_items' in entry:
|
|
18
|
+
items = entry['_embedded_items'].keys()
|
|
19
|
+
for item in items:
|
|
20
|
+
items_array = entry['_embedded_items'][item]
|
|
21
|
+
content = Automate._find_embedded_entry(items_array, metadata)
|
|
22
|
+
if content is not None:
|
|
23
|
+
return option.render_options(content, metadata)
|
|
24
|
+
else:
|
|
25
|
+
node_style = entry['type']
|
|
26
|
+
def call(children):
|
|
27
|
+
return Automate._raw_processing(children, entry, option)
|
|
28
|
+
|
|
29
|
+
return option.render_node(node_style, entry, callback=call)
|
|
30
|
+
return ''
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def _get_embedded_keys(entry, key_path, option: Options, render_callback):
|
|
34
|
+
if '_embedded_items' in entry:
|
|
35
|
+
if key_path is not None:
|
|
36
|
+
for path in key_path:
|
|
37
|
+
Automate._find_embed_keys(entry, path, option, render_callback)
|
|
38
|
+
else:
|
|
39
|
+
_embedded_items = entry['_embedded_items']
|
|
40
|
+
available_keys: list = _embedded_items.keys()
|
|
41
|
+
for path in available_keys:
|
|
42
|
+
Automate._find_embed_keys(entry, path, option, render_callback)
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def _find_embed_keys(entry, path, option: Options, render_callback):
|
|
46
|
+
keys = path.split('.')
|
|
47
|
+
Automate._get_content(keys, entry, option, render_callback)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def _get_content(keys_array, entry, option: Options, render_callback):
|
|
51
|
+
if keys_array is not None and len(keys_array) > 0:
|
|
52
|
+
key = keys_array[0]
|
|
53
|
+
if len(keys_array) == 1 and keys_array[0] in entry:
|
|
54
|
+
var_content = entry[key]
|
|
55
|
+
if isinstance(var_content, (list, str, dict)):
|
|
56
|
+
entry[key] = render_callback(var_content, entry, option)
|
|
57
|
+
else:
|
|
58
|
+
keys_array.remove(key)
|
|
59
|
+
if key in entry and isinstance(entry[key], dict):
|
|
60
|
+
Automate._get_content(keys_array, entry[key], option, render_callback)
|
|
61
|
+
elif key in entry and isinstance(entry[key], list):
|
|
62
|
+
list_json = entry[key]
|
|
63
|
+
for node in list_json:
|
|
64
|
+
Automate._get_content(keys_array, node, option, render_callback)
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def is_json(self: object) -> bool:
|
|
68
|
+
try:
|
|
69
|
+
json.dumps(self)
|
|
70
|
+
return True
|
|
71
|
+
except ValueError:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def find_embed_keys(entry, paths, option: Options, render_callback):
|
|
76
|
+
Automate.get_content(paths, entry, option, render_callback)
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def get_content(keys_array, entry, option: Options, render_callback):
|
|
80
|
+
if keys_array is not None and len(keys_array) > 0:
|
|
81
|
+
key = keys_array[0]
|
|
82
|
+
if len(keys_array) == 1 and keys_array[0] in entry:
|
|
83
|
+
var_content = entry[key]
|
|
84
|
+
if isinstance(var_content, (list, str, dict)):
|
|
85
|
+
entry[key] = render_callback(var_content, entry, option)
|
|
86
|
+
else:
|
|
87
|
+
keys_array.remove(key)
|
|
88
|
+
if key in entry and isinstance(entry[key], dict):
|
|
89
|
+
Automate.get_content(keys_array, entry[key], option, render_callback)
|
|
90
|
+
elif key in entry and isinstance(entry[key], list):
|
|
91
|
+
list_json = entry[key]
|
|
92
|
+
for node in list_json:
|
|
93
|
+
Automate.get_content(keys_array, node, option, render_callback)
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def _enumerate_content(content, entry, option):
|
|
97
|
+
if len(content) > 0:
|
|
98
|
+
if isinstance(content, list):
|
|
99
|
+
array_content = []
|
|
100
|
+
for item in content:
|
|
101
|
+
result = Automate._enumerate_content(item, entry, option)
|
|
102
|
+
array_content.append(result)
|
|
103
|
+
return array_content
|
|
104
|
+
if isinstance(content, dict):
|
|
105
|
+
content_entry = content[entry]
|
|
106
|
+
if isinstance(content_entry, dict):
|
|
107
|
+
if 'type' and 'children' in content_entry:
|
|
108
|
+
if content_entry['type']:
|
|
109
|
+
return Automate._raw_processing(content_entry['children'], entry, option)
|
|
110
|
+
elif isinstance(content_entry, list):
|
|
111
|
+
for entry_item in content_entry:
|
|
112
|
+
if 'type' and 'children' in entry_item:
|
|
113
|
+
if entry_item['type']:
|
|
114
|
+
return Automate._raw_processing(entry_item['children'], entry, option)
|
|
115
|
+
|
|
116
|
+
return ''
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def _raw_processing(children, entry, option):
|
|
120
|
+
array_container = []
|
|
121
|
+
for item in children:
|
|
122
|
+
if isinstance(item, dict):
|
|
123
|
+
array_container.append(Automate._extract_keys(item, entry, option))
|
|
124
|
+
temp = ''.join(array_container)
|
|
125
|
+
return temp
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def _extract_keys(item, entry, option: Options):
|
|
129
|
+
if 'type' not in item.keys() and 'text' in item.keys():
|
|
130
|
+
return NodeToHtml.text_node_to_html(item, option)
|
|
131
|
+
|
|
132
|
+
elif 'type' in item.keys():
|
|
133
|
+
node_style = item['type']
|
|
134
|
+
if node_style == 'reference':
|
|
135
|
+
metadata = Automate._return_metadata(item, node_style)
|
|
136
|
+
return Automate._str_from_embed_items(metadata=metadata, entry=item, option=option)
|
|
137
|
+
else:
|
|
138
|
+
def call(children):
|
|
139
|
+
return Automate._raw_processing(children, entry, option)
|
|
140
|
+
|
|
141
|
+
return option.render_node(node_style, item, callback=call)
|
|
142
|
+
return ''
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def _find_embedded_entry(list_json: list, metadata: Metadata):
|
|
146
|
+
for obj in list_json:
|
|
147
|
+
if obj['uid'] == metadata.get_item_uid:
|
|
148
|
+
return obj
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def _return_metadata(item, node_style):
|
|
153
|
+
attr = item['attrs']
|
|
154
|
+
text = Automate._get_child_text(item)
|
|
155
|
+
style = convert_style(attr['display-type'])
|
|
156
|
+
if attr['type'] == 'asset':
|
|
157
|
+
return Metadata(text, node_style,
|
|
158
|
+
attr['asset-uid'],
|
|
159
|
+
'sys-asset',
|
|
160
|
+
style, '', '')
|
|
161
|
+
else:
|
|
162
|
+
return Metadata(text, node_style,
|
|
163
|
+
attr['entry-uid'],
|
|
164
|
+
attr['content-type-uid'],
|
|
165
|
+
style, '', '')
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def _get_child_text(item):
|
|
169
|
+
text = ''
|
|
170
|
+
if 'children' in item.keys() and len(item['children']) > 0:
|
|
171
|
+
children = item['children']
|
|
172
|
+
for child in children:
|
|
173
|
+
if text in child.keys():
|
|
174
|
+
text = child['text']
|
|
175
|
+
break
|
|
176
|
+
return text
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import contentstack_utils
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ItemType is Enumeration class that conatains two options for ItemType:
|
|
3
|
+
|
|
4
|
+
ASSET
|
|
5
|
+
|
|
6
|
+
ENTRY
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import enum
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ItemType(enum.Enum):
|
|
13
|
+
"""Contains Two option for ItemsType => ENTRY and ASSET """
|
|
14
|
+
ENTRY = 'entry'
|
|
15
|
+
ASSET = 'asset'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
""""
|
|
2
|
+
There are two types StyleType ENTRY and ASSETS
|
|
3
|
+
For `Entry`: StyleType.BLOCK, StyleType.INLINE, StyleType.LINKED,
|
|
4
|
+
For `Assets`: StyleType.DISPLAY, StyleType.DOWNLOADABLE
|
|
5
|
+
"""
|
|
6
|
+
import enum
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StyleType(enum.Enum):
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
This StyleType contains four options like below.
|
|
13
|
+
BLOCK ,INLINE ,LINK,DISPLAY,DOWNLOAD
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
BLOCK = 'block'
|
|
17
|
+
INLINE = 'inline'
|
|
18
|
+
LINK = 'link'
|
|
19
|
+
DISPLAY = 'display'
|
|
20
|
+
DOWNLOAD = 'download'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from contentstack_utils import Utils
|
|
2
|
+
from contentstack_utils.automate import Automate
|
|
3
|
+
from contentstack_utils.render.options import Options
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GQL(Automate):
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def json_to_html(gql_entry: dict, paths: list, option: Options):
|
|
10
|
+
if not Automate.is_json(gql_entry):
|
|
11
|
+
raise FileNotFoundError("Can't process invalid object")
|
|
12
|
+
if len(paths) > 0:
|
|
13
|
+
for path in paths:
|
|
14
|
+
Automate.find_embed_keys(gql_entry, path, option, render_callback=GQL._json_matcher)
|
|
15
|
+
return Automate._enumerate_content(gql_entry, path, option)
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def __filter_content(content_dict):
|
|
19
|
+
embedded_items = None
|
|
20
|
+
if content_dict is not None and 'embedded_itemsConnection' in content_dict:
|
|
21
|
+
embedded_connection = content_dict['embedded_itemsConnection']
|
|
22
|
+
if 'edges' in embedded_connection:
|
|
23
|
+
embedded_items = embedded_connection['edges']
|
|
24
|
+
return embedded_items
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def _json_matcher(content_dict, entry, option):
|
|
28
|
+
embedded_items = GQL.__filter_content(content_dict)
|
|
29
|
+
if 'json' in content_dict:
|
|
30
|
+
json = content_dict['json']
|
|
31
|
+
if isinstance(json, dict):
|
|
32
|
+
return Automate._enumerate_content(json, entry=embedded_items, option=option)
|
|
33
|
+
elif isinstance(json, list):
|
|
34
|
+
json_container = []
|
|
35
|
+
for item in json:
|
|
36
|
+
json_container.append(Automate._enumerate_content(item, entry=embedded_items, option=option))
|
|
37
|
+
return json_container
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import contentstack_utils
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from contentstack_utils.embedded.styletype import StyleType
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def convert_style(style) -> StyleType:
|
|
5
|
+
if style == 'block':
|
|
6
|
+
return StyleType.BLOCK
|
|
7
|
+
if style == 'inline':
|
|
8
|
+
return StyleType.INLINE
|
|
9
|
+
if style == 'link':
|
|
10
|
+
return StyleType.LINK
|
|
11
|
+
if style == 'display':
|
|
12
|
+
return StyleType.DISPLAY
|
|
13
|
+
if style == 'download':
|
|
14
|
+
return StyleType.DOWNLOAD
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Metadata is the model class for embedded objects
|
|
3
|
+
|
|
4
|
+
Returns:
|
|
5
|
+
str: text, item_type, item_uid, type_uid, style_type, outer_html and attributes
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from contentstack_utils.embedded.styletype import StyleType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Metadata:
|
|
12
|
+
"""
|
|
13
|
+
model helper class to set and get value
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, text: str, item_type: str, item_uid: str,
|
|
17
|
+
content_type_uid: str, style_type: StyleType,
|
|
18
|
+
outer_html: str, attributes: str):
|
|
19
|
+
"""
|
|
20
|
+
Used to set the value to the variables
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
text (str): text for embedded objects
|
|
24
|
+
item_type (str): item_type for embedded objects
|
|
25
|
+
item_uid (str): item_uid for embedded objects
|
|
26
|
+
content_type_uid (str): content_type_uid for embedded objects
|
|
27
|
+
style_type (StyleType): style_type for embedded objects
|
|
28
|
+
outer_html (str): outer_html for embedded objects
|
|
29
|
+
attributes (str): attributes for embedded objects
|
|
30
|
+
"""
|
|
31
|
+
self.text = text
|
|
32
|
+
self.item_type = item_type
|
|
33
|
+
self.item_uid = item_uid
|
|
34
|
+
self.content_type_uid = content_type_uid
|
|
35
|
+
self.style_type = style_type
|
|
36
|
+
self.outer_html = outer_html
|
|
37
|
+
self.attributes = attributes
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def get_text(self):
|
|
41
|
+
return self.text
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def get_item_type(self):
|
|
45
|
+
return self.item_type
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def get_item_uid(self):
|
|
49
|
+
return self.item_uid
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def get_content_type_uid(self):
|
|
53
|
+
return self.content_type_uid
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def get_style_type(self) -> StyleType:
|
|
57
|
+
return self.style_type
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def get_outer_html(self):
|
|
61
|
+
return self.outer_html
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def get_attributes(self):
|
|
65
|
+
return self.attributes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from contentstack_utils.render.options import Options
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NodeToHtml:
|
|
5
|
+
|
|
6
|
+
@staticmethod
|
|
7
|
+
def text_node_to_html(node, option: Options):
|
|
8
|
+
"""
|
|
9
|
+
accepts node type,
|
|
10
|
+
on the basis of the node type, generates string
|
|
11
|
+
:rtype: str
|
|
12
|
+
"""
|
|
13
|
+
node_text = node['text']
|
|
14
|
+
if 'superscript' in node:
|
|
15
|
+
node_text = option.render_mark('superscript', node_text)
|
|
16
|
+
if 'subscript' in node:
|
|
17
|
+
node_text = option.render_mark('subscript', node_text)
|
|
18
|
+
if 'inlineCode' in node:
|
|
19
|
+
node_text = option.render_mark('inlineCode', node_text)
|
|
20
|
+
if 'strikethrough' in node:
|
|
21
|
+
node_text = option.render_mark('strikethrough', node_text)
|
|
22
|
+
if 'underline' in node:
|
|
23
|
+
node_text = option.render_mark('underline', node_text)
|
|
24
|
+
if 'italic' in node:
|
|
25
|
+
node_text = option.render_mark('italic', node_text)
|
|
26
|
+
if 'bold' in node:
|
|
27
|
+
node_text = option.render_mark('bold', node_text)
|
|
28
|
+
return node_text
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import contentstack_utils
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from contentstack_utils.helper.metadata import Metadata
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _title_or_uid(_obj: dict) -> str:
|
|
5
|
+
_title = ""
|
|
6
|
+
if _obj is not None:
|
|
7
|
+
if 'title' in _obj and len(_obj['title']) != 0:
|
|
8
|
+
_title = _obj['title']
|
|
9
|
+
elif 'uid' in _obj:
|
|
10
|
+
_title = _obj['uid']
|
|
11
|
+
return _title
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _asset_title_or_uid(_obj: dict) -> str:
|
|
15
|
+
_title = ""
|
|
16
|
+
if _obj is not None:
|
|
17
|
+
if 'title' in _obj and len(_obj['title']) != 0:
|
|
18
|
+
_title = _obj['title']
|
|
19
|
+
elif 'filename' in _obj:
|
|
20
|
+
_title = _obj['filename']
|
|
21
|
+
elif 'uid' in _obj:
|
|
22
|
+
_title = _obj['uid']
|
|
23
|
+
return _title
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Options:
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def render_options(_obj: dict, metadata: Metadata):
|
|
30
|
+
if metadata.style_type.value == 'block':
|
|
31
|
+
_content_type_uid = ''
|
|
32
|
+
if '_content_type_uid' in _obj:
|
|
33
|
+
_content_type_uid = _obj['_content_type_uid']
|
|
34
|
+
return '<div><p>' + _title_or_uid(_obj) \
|
|
35
|
+
+ '</p><div><p>Content type: <span>' + _content_type_uid \
|
|
36
|
+
+ '</span></p></div>'
|
|
37
|
+
if metadata.style_type.value == 'inline':
|
|
38
|
+
return '<span>' + _title_or_uid(_obj) + '</span>'
|
|
39
|
+
if metadata.style_type.value == 'link':
|
|
40
|
+
return '<a href=' + _obj['url'] + '>' + _title_or_uid(_obj) + '</a>'
|
|
41
|
+
if metadata.style_type.value == 'display':
|
|
42
|
+
return '<img src=' + _obj['url'] + ' alt=' \
|
|
43
|
+
+ _asset_title_or_uid(_obj) + '/>'
|
|
44
|
+
if metadata.style_type.value == 'download':
|
|
45
|
+
return '<a href=' + _obj['url'] + '>' + _asset_title_or_uid(_obj) + '</a>'
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def render_mark(mark_type: str, render_text: str):
|
|
49
|
+
if mark_type == 'superscript':
|
|
50
|
+
return "<sup>" + render_text + "</sup>"
|
|
51
|
+
if mark_type == 'subscript':
|
|
52
|
+
return "<sub>" + render_text + "</sub>"
|
|
53
|
+
if mark_type == 'inlineCode':
|
|
54
|
+
return "<span>" + render_text + "</span>"
|
|
55
|
+
if mark_type == 'strikethrough':
|
|
56
|
+
return "<strike>" + render_text + "</strike>"
|
|
57
|
+
if mark_type == 'underline':
|
|
58
|
+
return "<u>" + render_text + "</u>"
|
|
59
|
+
if mark_type == 'italic':
|
|
60
|
+
return "<em>" + render_text + "</em>"
|
|
61
|
+
if mark_type == 'bold':
|
|
62
|
+
return "<strong>" + render_text + "</strong>"
|
|
63
|
+
else:
|
|
64
|
+
return render_text
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def render_node(node_type, node_obj: dict, callback):
|
|
69
|
+
inner_html = callback(node_obj['children'])
|
|
70
|
+
if node_type == 'p':
|
|
71
|
+
return "<p>" + inner_html + "</p>"
|
|
72
|
+
if node_type == 'a':
|
|
73
|
+
return "<a href=\"{}\">{}</a>".format(node_obj["attrs"]["href"], inner_html)
|
|
74
|
+
if node_type == 'img':
|
|
75
|
+
return "<img src=\"{}\" />{}".format(node_obj["attrs"]["src"], inner_html)
|
|
76
|
+
if node_type == 'embed':
|
|
77
|
+
return "<iframe src={}>{}</iframe>".format(node_obj["attrs"]["src"], inner_html)
|
|
78
|
+
if node_type == 'h1':
|
|
79
|
+
return "<h1>" + inner_html + "</h1>"
|
|
80
|
+
if node_type == 'h2':
|
|
81
|
+
return "<h2>" + inner_html + "</h2>"
|
|
82
|
+
if node_type == 'h3':
|
|
83
|
+
return "<h3>" + inner_html + "</h3>"
|
|
84
|
+
if node_type == 'h4':
|
|
85
|
+
return "<h4>" + inner_html + "</h4>"
|
|
86
|
+
if node_type == 'h5':
|
|
87
|
+
return "<h5>" + inner_html + "</h5>"
|
|
88
|
+
if node_type == 'h6':
|
|
89
|
+
return "<h6>" + inner_html + "</h6>"
|
|
90
|
+
if node_type == 'ol':
|
|
91
|
+
return "<ol>" + inner_html + "</ol>"
|
|
92
|
+
if node_type == 'ul':
|
|
93
|
+
return "<ul>" + inner_html + "</ul>"
|
|
94
|
+
if node_type == 'li':
|
|
95
|
+
return "<li>" + inner_html + "</li>"
|
|
96
|
+
if node_type == 'hr':
|
|
97
|
+
return "<hr />"
|
|
98
|
+
if node_type == 'table':
|
|
99
|
+
return "<table>" + inner_html + "</table>"
|
|
100
|
+
if node_type == 'thead':
|
|
101
|
+
return "<thead>" + inner_html + "</thead>"
|
|
102
|
+
if node_type == 'tbody':
|
|
103
|
+
return "<tbody>" + inner_html + "</tbody>"
|
|
104
|
+
if node_type == 'tfoot':
|
|
105
|
+
return "<tfoot>" + inner_html + "</tfoot>"
|
|
106
|
+
if node_type == 'tr':
|
|
107
|
+
return "<tr>" + inner_html + "</tr>"
|
|
108
|
+
if node_type == 'th':
|
|
109
|
+
return "<th>" + inner_html + "</th>"
|
|
110
|
+
if node_type == 'td':
|
|
111
|
+
return "<td>" + inner_html + "</td>"
|
|
112
|
+
if node_type == 'blockquote':
|
|
113
|
+
return "<blockquote>" + inner_html + "</blockquote>"
|
|
114
|
+
if node_type == 'code':
|
|
115
|
+
return "<code>" + inner_html + "</code>"
|
|
116
|
+
if node_type == 'fragment':
|
|
117
|
+
return "<fragment>" + inner_html + "</fragment>"
|
|
118
|
+
if node_type == 'reference':
|
|
119
|
+
if node_obj['attrs']['type'] == 'asset':
|
|
120
|
+
return "<img src=\"{}\" alt=\"{}\" class=\"{}\" />{}".format(node_obj["attrs"]["asset-link"], node_obj["attrs"]["alt"], node_obj["attrs"]["class-name"], inner_html)
|
|
121
|
+
else:
|
|
122
|
+
return inner_html
|
|
123
|
+
if node_type == 'doc':
|
|
124
|
+
return inner_html
|
|
125
|
+
else:
|
|
126
|
+
return inner_html
|
|
@@ -0,0 +1,94 @@
|
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: contentstack_utils
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.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
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
contentstack_utils/__init__.py,sha256=CfbGkFGBS_v7mfYxOo1QU35LLUUhX8muQ650EjzDisU,957
|
|
2
|
+
contentstack_utils/automate.py,sha256=3tm43Y12dWZrJy6DR9tkAl4Dmph9-hNMXK94UWSKYso,7389
|
|
3
|
+
contentstack_utils/gql.py,sha256=_r1K8pBduin_STrIo9IqdD3ftH0_3Ii6RSnTcrqwJlY,1587
|
|
4
|
+
contentstack_utils/utils.py,sha256=4oIy_JBhpLW0CkxyZJ39ubj9u7Lw4sKRhOyg3cCStdM,4045
|
|
5
|
+
contentstack_utils/embedded/__init__.py,sha256=0Ni3R_ia-HuTDFYQvoLURdeRkRD8HiyteiEEJ3O4V14,25
|
|
6
|
+
contentstack_utils/embedded/item_type.py,sha256=yoB3ZTJ0IdwhL5E61zrxLXZSvcLMnnUiI6vi6jhR3Ww,239
|
|
7
|
+
contentstack_utils/embedded/styletype.py,sha256=lwRV-tn1be3DPqeW3YnZPsWCXns9Xd0ml_hC5M87zow,443
|
|
8
|
+
contentstack_utils/helper/__init__.py,sha256=0Ni3R_ia-HuTDFYQvoLURdeRkRD8HiyteiEEJ3O4V14,25
|
|
9
|
+
contentstack_utils/helper/converter.py,sha256=2tZ6paPnWCCW0aoaQiMFGAhAdx4Bh2LsCnlCm3Q7yVw,391
|
|
10
|
+
contentstack_utils/helper/metadata.py,sha256=YrNVhvkv4n_nR0nrJR2x2eqYE3-P1jT7ctVSklvnSgc,1795
|
|
11
|
+
contentstack_utils/helper/node_to_html.py,sha256=himweM9ID4tQb7GNQP49XYTkN0KWWPy_LzBi-f6eS2E,1019
|
|
12
|
+
contentstack_utils/render/__init__.py,sha256=0Ni3R_ia-HuTDFYQvoLURdeRkRD8HiyteiEEJ3O4V14,25
|
|
13
|
+
contentstack_utils/render/options.py,sha256=hbqgPxCksgQlV5hK_6Flb2HEHaXk5JpMNc7z5yMxgCI,5033
|
|
14
|
+
tests/__init__.py,sha256=stlQRdpO_kQoRExPLd-q80RCzHd-F4t1Rs6tZ0ObP2o,2336
|
|
15
|
+
tests/convert_style.py,sha256=gXHQjSnxCZ7kQBG6TjKIYk-tvKhzdpeOBCA88YTcDC8,983
|
|
16
|
+
tests/test_default_opt_others.py,sha256=H-xhmyGMJNET0kkhXGcyepWUTE0Vqd1DpzPCI6hKaro,2138
|
|
17
|
+
tests/test_gql_to_html_func.py,sha256=oh7YpNay9GBBnVXbJhbh5e0r5eMYcrXhrzaiwuAQTOg,1180
|
|
18
|
+
tests/test_helper_node_to_html.py,sha256=S3JSRv5FXyBnBXM23J7v9rtrIvODii0J9AEBMO4gve4,2497
|
|
19
|
+
tests/test_item_types.py,sha256=lHKquAgitHEPquAR9Qyvcl2aWhXYaHJe9F1anCloYh0,350
|
|
20
|
+
tests/test_metadata.py,sha256=B5SY50S7-bdzgSs5UDupcRWThzm3weyTNu4WqUBr3OY,1837
|
|
21
|
+
tests/test_option_render_mark.py,sha256=NU7TOTJgljZyO5cnrphX-XyhJXUYqaZIbTzMJX0HOvY,2582
|
|
22
|
+
tests/test_render_default_options.py,sha256=JNx_gA11Af-hPRoSd5MG59Cy5xjyUrpr8WXpVzqk6FY,3993
|
|
23
|
+
tests/test_render_options.py,sha256=BFuf9nkh1c9BJ-UTFVBkmjBTf31pSeWJ2jTZY7XgVaY,1701
|
|
24
|
+
tests/test_style_type.py,sha256=6Wz_UR5ZQcaUlB-Tfeqp6Dcu85AE7pHbtg6V1Sh7A9k,723
|
|
25
|
+
tests/test_util_srte.py,sha256=vNb2xsrmGmGSonW_EzpItcqoQmnSWUmOlzybIo5fzik,5951
|
|
26
|
+
tests/test_utils.py,sha256=2rvZZcT6m3QJZeyqzyp9ydsTX34w5tAkQOCKqXso3xw,3614
|
|
27
|
+
contentstack_utils-1.3.0.dist-info/LICENSE,sha256=lNqz95ic6VWhDILAlbV_KgjGfl7GM1QQd6LKZq__zx4,1051
|
|
28
|
+
contentstack_utils-1.3.0.dist-info/METADATA,sha256=3J3TPvQlfE0wmBcz1EweJYGRt6wzYdIH5jpWRI3UhgY,4906
|
|
29
|
+
contentstack_utils-1.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
30
|
+
contentstack_utils-1.3.0.dist-info/top_level.txt,sha256=4b5I6taLW2KGOy1931_UZDIcML7HJEfjpzIDpz5MmTU,25
|
|
31
|
+
contentstack_utils-1.3.0.dist-info/RECORD,,
|