Skip to content

Commit db51c16

Browse files
Merge pull request #3 from contentstack/feature/gql-srte
Feature/gql srte
2 parents 28807be + 6c62588 commit db51c16

File tree

16 files changed

+369
-189
lines changed

16 files changed

+369
-189
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ target/
8484
# pyenv
8585
.python-version
8686

87-
# celery beat schedule file
87+
# celery beat schedule self
8888
celerybeat-schedule
8989

9090
# SageMath parsed files

README.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ If you are using Contentstack Python SDK in your project by running the followin
3131
## For the specific version
3232

3333
```python
34-
pip install Contentstack==1.4.0
34+
pip install Contentstack==1.5.1
3535
```
3636

3737
## Usage
@@ -46,11 +46,9 @@ To render embedded items on the front-end, use the renderContents function, and
4646
from contentstack_utils.utils import Utils
4747
from contentstack_utils.render.options import Options
4848

49-
json_array # should be type of dictionary or list
50-
rte_content = "html_string"
51-
52-
callback = Options()
53-
response = Utils.render_content(rte_content, json_array, callback)
49+
json_array = {} # should be type of dictionary or list
50+
option = Options()
51+
response = Utils.render_content('html_string', json_array, option)
5452
print(response)
5553

5654
```
@@ -106,7 +104,24 @@ query = stack.content_type("content_type_uid").query()
106104
result = query.find()
107105
if result is not None and 'entries' in result:
108106
entry = result['entries']
109-
for item in range:
107+
for item in entry:
110108
option = Option()
111109
Utils.json_to_html(item, ['paragraph_text'], option)
112110
```
111+
112+
## GraphQL SRTE
113+
114+
To get supercharged items from multiple entries, you need to provide the stack API key, delivery token, environment name, and content type’s UID.
115+
116+
```python
117+
import contentstack
118+
119+
stack = contentstack.Stack('api_key','delivery_token','environment')
120+
query = stack.content_type("content_type_uid").query()
121+
result = query.find()
122+
if result is not None and 'entries' in result:
123+
entry = result['entries']
124+
for item in entry:
125+
option = Option()
126+
GQL.json_to_html(item, ['paragraph_text'], option)
127+
```

changelog.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,20 @@
22
**CHANGELOG**
33
================
44

5+
*v1.2.0*
6+
============
7+
8+
NEW FEATURE: GraphQl supercharged RTE
9+
10+
- GQL.jsonToHtml function support added
11+
12+
13+
*v1.1.0*
14+
============
15+
516
NEW FEATURE: Supercharged RTE
617

7-
- jsonToHtml function support added
18+
- Utils.jsonToHtml function support added
819

920
*v0.2.0*
1021
============

contentstack_utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
__title__ = 'contentstack_utils'
1818
__author__ = 'contentstack'
1919
__status__ = 'debug'
20-
__version__ = '0.0.1'
20+
__version__ = '1.1.0'
2121
__endpoint__ = 'cdn.contentstack.io'
2222
__contact__ = 'support@contentstack.com'

contentstack_utils/automate.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import json
2+
3+
from contentstack_utils.helper.converter import convert_style
4+
from contentstack_utils.helper.metadata import Metadata
5+
from contentstack_utils.helper.node_to_html import NodeToHtml
6+
from contentstack_utils.render.options import Options
7+
8+
9+
class Automate:
10+
11+
@staticmethod
12+
def _str_from_embed_items(metadata, entry, option):
13+
if isinstance(entry, list):
14+
for node in entry:
15+
uid = node['node']['uid']
16+
if uid == metadata.get_item_uid:
17+
return option.render_options(node['node'], metadata)
18+
elif isinstance(entry, dict) and '_embedded_items' in entry:
19+
items = entry['_embedded_items'].keys()
20+
for item in items:
21+
items_array = entry['_embedded_items'][item]
22+
content = Automate._find_embedded_entry(items_array, metadata)
23+
if content is not None:
24+
return option.render_options(content, metadata)
25+
return ''
26+
27+
@staticmethod
28+
def _get_embedded_keys(entry, key_path, option: Options, render_callback):
29+
if '_embedded_items' in entry:
30+
if key_path is not None:
31+
for path in key_path:
32+
Automate._find_embed_keys(entry, path, option, render_callback)
33+
else:
34+
_embedded_items = entry['_embedded_items']
35+
available_keys: list = _embedded_items.keys()
36+
for path in available_keys:
37+
Automate._find_embed_keys(entry, path, option, render_callback)
38+
39+
@staticmethod
40+
def _find_embed_keys(entry, path, option: Options, render_callback):
41+
keys = path.split('.')
42+
Automate._get_content(keys, entry, option, render_callback)
43+
44+
@staticmethod
45+
def _get_content(keys_array, entry, option: Options, render_callback):
46+
if keys_array is not None and len(keys_array) > 0:
47+
key = keys_array[0]
48+
if len(keys_array) == 1 and keys_array[0] in entry:
49+
var_content = entry[key]
50+
if isinstance(var_content, (list, str, dict)):
51+
entry[key] = render_callback(var_content, entry, option)
52+
else:
53+
keys_array.remove(key)
54+
if key in entry and isinstance(entry[key], dict):
55+
Automate._get_content(keys_array, entry[key], option, render_callback)
56+
elif key in entry and isinstance(entry[key], list):
57+
list_json = entry[key]
58+
for node in list_json:
59+
Automate._get_content(keys_array, node, option, render_callback)
60+
61+
@staticmethod
62+
def is_json(self: object) -> bool:
63+
try:
64+
json.dumps(self)
65+
return True
66+
except ValueError:
67+
return False
68+
69+
@staticmethod
70+
def find_embed_keys(entry, path, option: Options, render_callback):
71+
keys = path.split('.')
72+
Automate.get_content(keys, entry, option, render_callback)
73+
74+
@staticmethod
75+
def get_content(keys_array, entry, option: Options, render_callback):
76+
if keys_array is not None and len(keys_array) > 0:
77+
key = keys_array[0]
78+
if len(keys_array) == 1 and keys_array[0] in entry:
79+
var_content = entry[key]
80+
if isinstance(var_content, (list, str, dict)):
81+
entry[key] = render_callback(var_content, entry, option)
82+
else:
83+
keys_array.remove(key)
84+
if key in entry and isinstance(entry[key], dict):
85+
Automate.get_content(keys_array, entry[key], option, render_callback)
86+
elif key in entry and isinstance(entry[key], list):
87+
list_json = entry[key]
88+
for node in list_json:
89+
Automate.get_content(keys_array, node, option, render_callback)
90+
91+
@staticmethod
92+
def _enumerate_content(content, entry, option):
93+
if len(content) > 0:
94+
if isinstance(content, list):
95+
array_content = []
96+
for item in content:
97+
result = Automate._enumerate_content(item, entry, option)
98+
array_content.append(result)
99+
return array_content
100+
if isinstance(content, dict):
101+
if 'type' and 'children' in content:
102+
if content['type'] == 'doc':
103+
return Automate._raw_processing(content['children'], entry, option)
104+
return ''
105+
106+
@staticmethod
107+
def _raw_processing(children, entry, option):
108+
array_container = []
109+
for item in children:
110+
if isinstance(item, dict):
111+
array_container.append(Automate._extract_keys(item, entry, option))
112+
temp = ''.join(array_container)
113+
return temp
114+
115+
@staticmethod
116+
def _extract_keys(item, entry, option: Options):
117+
if 'type' not in item.keys() and 'text' in item.keys():
118+
return NodeToHtml.text_node_to_html(item, option)
119+
120+
elif 'type' in item.keys():
121+
node_style = item['type']
122+
if node_style == 'reference':
123+
metadata = Automate._return_metadata(item, node_style)
124+
return Automate._str_from_embed_items(metadata=metadata, entry=entry, option=option)
125+
else:
126+
def call(children):
127+
return Automate._raw_processing(children, entry, option)
128+
129+
return option.render_node(node_style, item, callback=call)
130+
return ''
131+
132+
@staticmethod
133+
def _find_embedded_entry(list_json: list, metadata: Metadata):
134+
for obj in list_json:
135+
if obj['uid'] == metadata.get_item_uid:
136+
return obj
137+
return None
138+
139+
@staticmethod
140+
def _return_metadata(item, node_style):
141+
attr = item['attrs']
142+
text = Automate._get_child_text(item)
143+
style = convert_style(attr['display-type'])
144+
if attr['type'] == 'asset':
145+
return Metadata(text, node_style,
146+
attr['asset-uid'],
147+
'sys-asset',
148+
style, '', '')
149+
else:
150+
return Metadata(text, node_style,
151+
attr['entry-uid'],
152+
attr['content-type-uid'],
153+
style, '', '')
154+
155+
@staticmethod
156+
def _get_child_text(item):
157+
text = ''
158+
if 'children' in item.keys() and len(item['children']) > 0:
159+
children = item['children']
160+
for child in children:
161+
if text in child.keys():
162+
text = child['text']
163+
break
164+
return text

contentstack_utils/gql.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
16+
@staticmethod
17+
def __filter_content(content_dict):
18+
embedded_items = None
19+
if content_dict is not None and 'embedded_itemsConnection' in content_dict:
20+
embedded_connection = content_dict['embedded_itemsConnection']
21+
if 'edges' in embedded_connection:
22+
embedded_items = embedded_connection['edges']
23+
return embedded_items
24+
25+
@staticmethod
26+
def _json_matcher(content_dict, entry, option):
27+
embedded_items = GQL.__filter_content(content_dict)
28+
if 'json' in content_dict:
29+
json = content_dict['json']
30+
if isinstance(json, dict):
31+
return Automate._enumerate_content(json, entry=embedded_items, option=option)
32+
elif isinstance(json, list):
33+
json_container = []
34+
for item in json:
35+
json_container.append(Automate._enumerate_content(item, entry=embedded_items, option=option))
36+
return json_container

contentstack_utils/helper/converter.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
def convert_style(style) -> StyleType:
55
if style == 'block':
66
return StyleType.BLOCK
7-
elif style == 'inline':
7+
if style == 'inline':
88
return StyleType.INLINE
9-
elif style == 'link':
9+
if style == 'link':
1010
return StyleType.LINK
11-
elif style == 'display':
11+
if style == 'display':
1212
return StyleType.DISPLAY
13-
elif style == 'download':
13+
if style == 'download':
1414
return StyleType.DOWNLOAD

contentstack_utils/render/options.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ class Options:
2828
@staticmethod
2929
def render_options(_obj: dict, metadata: Metadata):
3030
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']
3134
return '<div><p>' + _title_or_uid(_obj) \
32-
+ '</p><div><p>Content type: <span>' + _obj['_content_type_uid'] \
35+
+ '</p><div><p>Content type: <span>' + _content_type_uid \
3336
+ '</span></p></div>'
3437
if metadata.style_type.value == 'inline':
3538
return '<span>' + _title_or_uid(_obj) + '</span>'
@@ -67,11 +70,11 @@ def render_node(node_type, node_obj: dict, callback):
6770
if node_type == 'p':
6871
return "<p>" + inner_html + "</p>"
6972
if node_type == 'a':
70-
return "<a href=" + node_obj["attrs"]["href"] + ">" + inner_html + "</a>"
73+
return "<a href=\"{}\">{}</a>".format(node_obj["attrs"]["href"], inner_html)
7174
if node_type == 'img':
72-
return "<img src=" + node_obj["attrs"]["src"] + " />" + inner_html + ""
75+
return "<img src=\"{}\" />{}".format(node_obj["attrs"]["src"], inner_html)
7376
if node_type == 'embed':
74-
return "<iframe src=" + node_obj["attrs"]["src"] + ">" + inner_html + "</iframe>"
77+
return "<iframe src={}>{}</iframe>".format(node_obj["attrs"]["src"], inner_html)
7578
if node_type == 'h1':
7679
return "<h1>" + inner_html + "</h1>"
7780
if node_type == 'h2':
@@ -102,7 +105,6 @@ def render_node(node_type, node_obj: dict, callback):
102105
return "<tfoot>" + inner_html + "</tfoot>"
103106
if node_type == 'tr':
104107
return "<tr>" + inner_html + "</tr>"
105-
106108
if node_type == 'th':
107109
return "<th>" + inner_html + "</th>"
108110
if node_type == 'td':

0 commit comments

Comments
 (0)