Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ target/
# pyenv
.python-version

# celery beat schedule file
# celery beat schedule self
celerybeat-schedule

# SageMath parsed files
Expand Down
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ If you are using Contentstack Python SDK in your project by running the followin
## For the specific version

```python
pip install Contentstack==1.4.0
pip install Contentstack==1.5.1
```

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

json_array # should be type of dictionary or list
rte_content = "html_string"

callback = Options()
response = Utils.render_content(rte_content, json_array, callback)
json_array = {} # should be type of dictionary or list
option = Options()
response = Utils.render_content('html_string', json_array, option)
print(response)

```
Expand Down Expand Up @@ -106,7 +104,24 @@ query = stack.content_type("content_type_uid").query()
result = query.find()
if result is not None and 'entries' in result:
entry = result['entries']
for item in range:
for item in entry:
option = Option()
Utils.json_to_html(item, ['paragraph_text'], option)
```

## GraphQL SRTE

To get supercharged items from multiple entries, you need to provide the stack API key, delivery token, environment name, and content type’s UID.

```python
import contentstack

stack = contentstack.Stack('api_key','delivery_token','environment')
query = stack.content_type("content_type_uid").query()
result = query.find()
if result is not None and 'entries' in result:
entry = result['entries']
for item in entry:
option = Option()
GQL.json_to_html(item, ['paragraph_text'], option)
```
13 changes: 12 additions & 1 deletion changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@
**CHANGELOG**
================

*v1.2.0*
============

NEW FEATURE: GraphQl supercharged RTE

- GQL.jsonToHtml function support added


*v1.1.0*
============

NEW FEATURE: Supercharged RTE

- jsonToHtml function support added
- Utils.jsonToHtml function support added

*v0.2.0*
============
Expand Down
2 changes: 1 addition & 1 deletion contentstack_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
__title__ = 'contentstack_utils'
__author__ = 'contentstack'
__status__ = 'debug'
__version__ = '0.0.1'
__version__ = '1.1.0'
__endpoint__ = 'cdn.contentstack.io'
__contact__ = '[email protected]'
164 changes: 164 additions & 0 deletions contentstack_utils/automate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import json

from contentstack_utils.helper.converter import convert_style
from contentstack_utils.helper.metadata import Metadata
from contentstack_utils.helper.node_to_html import NodeToHtml
from contentstack_utils.render.options import Options


class Automate:

@staticmethod
def _str_from_embed_items(metadata, entry, option):
if isinstance(entry, list):
for node in entry:
uid = node['node']['uid']
if uid == metadata.get_item_uid:
return option.render_options(node['node'], metadata)
elif isinstance(entry, dict) and '_embedded_items' in entry:
items = entry['_embedded_items'].keys()
for item in items:
items_array = entry['_embedded_items'][item]
content = Automate._find_embedded_entry(items_array, metadata)
if content is not None:
return option.render_options(content, metadata)
return ''

@staticmethod
def _get_embedded_keys(entry, key_path, option: Options, render_callback):
if '_embedded_items' in entry:
if key_path is not None:
for path in key_path:
Automate._find_embed_keys(entry, path, option, render_callback)
else:
_embedded_items = entry['_embedded_items']
available_keys: list = _embedded_items.keys()
for path in available_keys:
Automate._find_embed_keys(entry, path, option, render_callback)

@staticmethod
def _find_embed_keys(entry, path, option: Options, render_callback):
keys = path.split('.')
Automate._get_content(keys, entry, option, render_callback)

@staticmethod
def _get_content(keys_array, entry, option: Options, render_callback):
if keys_array is not None and len(keys_array) > 0:
key = keys_array[0]
if len(keys_array) == 1 and keys_array[0] in entry:
var_content = entry[key]
if isinstance(var_content, (list, str, dict)):
entry[key] = render_callback(var_content, entry, option)
else:
keys_array.remove(key)
if key in entry and isinstance(entry[key], dict):
Automate._get_content(keys_array, entry[key], option, render_callback)
elif key in entry and isinstance(entry[key], list):
list_json = entry[key]
for node in list_json:
Automate._get_content(keys_array, node, option, render_callback)

@staticmethod
def is_json(self: object) -> bool:
try:
json.dumps(self)
return True
except ValueError:
return False

@staticmethod
def find_embed_keys(entry, path, option: Options, render_callback):
keys = path.split('.')
Automate.get_content(keys, entry, option, render_callback)

@staticmethod
def get_content(keys_array, entry, option: Options, render_callback):
if keys_array is not None and len(keys_array) > 0:
key = keys_array[0]
if len(keys_array) == 1 and keys_array[0] in entry:
var_content = entry[key]
if isinstance(var_content, (list, str, dict)):
entry[key] = render_callback(var_content, entry, option)
else:
keys_array.remove(key)
if key in entry and isinstance(entry[key], dict):
Automate.get_content(keys_array, entry[key], option, render_callback)
elif key in entry and isinstance(entry[key], list):
list_json = entry[key]
for node in list_json:
Automate.get_content(keys_array, node, option, render_callback)

@staticmethod
def _enumerate_content(content, entry, option):
if len(content) > 0:
if isinstance(content, list):
array_content = []
for item in content:
result = Automate._enumerate_content(item, entry, option)
array_content.append(result)
return array_content
if isinstance(content, dict):
if 'type' and 'children' in content:
if content['type'] == 'doc':
return Automate._raw_processing(content['children'], entry, option)
return ''

@staticmethod
def _raw_processing(children, entry, option):
array_container = []
for item in children:
if isinstance(item, dict):
array_container.append(Automate._extract_keys(item, entry, option))
temp = ''.join(array_container)
return temp

@staticmethod
def _extract_keys(item, entry, option: Options):
if 'type' not in item.keys() and 'text' in item.keys():
return NodeToHtml.text_node_to_html(item, option)

elif 'type' in item.keys():
node_style = item['type']
if node_style == 'reference':
metadata = Automate._return_metadata(item, node_style)
return Automate._str_from_embed_items(metadata=metadata, entry=entry, option=option)
else:
def call(children):
return Automate._raw_processing(children, entry, option)

return option.render_node(node_style, item, callback=call)
return ''

@staticmethod
def _find_embedded_entry(list_json: list, metadata: Metadata):
for obj in list_json:
if obj['uid'] == metadata.get_item_uid:
return obj
return None

@staticmethod
def _return_metadata(item, node_style):
attr = item['attrs']
text = Automate._get_child_text(item)
style = convert_style(attr['display-type'])
if attr['type'] == 'asset':
return Metadata(text, node_style,
attr['asset-uid'],
'sys-asset',
style, '', '')
else:
return Metadata(text, node_style,
attr['entry-uid'],
attr['content-type-uid'],
style, '', '')

@staticmethod
def _get_child_text(item):
text = ''
if 'children' in item.keys() and len(item['children']) > 0:
children = item['children']
for child in children:
if text in child.keys():
text = child['text']
break
return text
36 changes: 36 additions & 0 deletions contentstack_utils/gql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from contentstack_utils import Utils
from contentstack_utils.automate import Automate
from contentstack_utils.render.options import Options


class GQL(Automate):

@staticmethod
def json_to_html(gql_entry: dict, paths: list, option: Options):
if not Automate.is_json(gql_entry):
raise FileNotFoundError("Can't process invalid object")
if len(paths) > 0:
for path in paths:
Automate.find_embed_keys(gql_entry, path, option, render_callback=GQL._json_matcher)

@staticmethod
def __filter_content(content_dict):
embedded_items = None
if content_dict is not None and 'embedded_itemsConnection' in content_dict:
embedded_connection = content_dict['embedded_itemsConnection']
if 'edges' in embedded_connection:
embedded_items = embedded_connection['edges']
return embedded_items

@staticmethod
def _json_matcher(content_dict, entry, option):
embedded_items = GQL.__filter_content(content_dict)
if 'json' in content_dict:
json = content_dict['json']
if isinstance(json, dict):
return Automate._enumerate_content(json, entry=embedded_items, option=option)
elif isinstance(json, list):
json_container = []
for item in json:
json_container.append(Automate._enumerate_content(item, entry=embedded_items, option=option))
return json_container
8 changes: 4 additions & 4 deletions contentstack_utils/helper/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
def convert_style(style) -> StyleType:
if style == 'block':
return StyleType.BLOCK
elif style == 'inline':
if style == 'inline':
return StyleType.INLINE
elif style == 'link':
if style == 'link':
return StyleType.LINK
elif style == 'display':
if style == 'display':
return StyleType.DISPLAY
elif style == 'download':
if style == 'download':
return StyleType.DOWNLOAD
12 changes: 7 additions & 5 deletions contentstack_utils/render/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ class Options:
@staticmethod
def render_options(_obj: dict, metadata: Metadata):
if metadata.style_type.value == 'block':
_content_type_uid = ''
if '_content_type_uid' in _obj:
_content_type_uid = _obj['_content_type_uid']
return '<div><p>' + _title_or_uid(_obj) \
+ '</p><div><p>Content type: <span>' + _obj['_content_type_uid'] \
+ '</p><div><p>Content type: <span>' + _content_type_uid \
+ '</span></p></div>'
if metadata.style_type.value == 'inline':
return '<span>' + _title_or_uid(_obj) + '</span>'
Expand Down Expand Up @@ -67,11 +70,11 @@ def render_node(node_type, node_obj: dict, callback):
if node_type == 'p':
return "<p>" + inner_html + "</p>"
if node_type == 'a':
return "<a href=" + node_obj["attrs"]["href"] + ">" + inner_html + "</a>"
return "<a href=\"{}\">{}</a>".format(node_obj["attrs"]["href"], inner_html)
if node_type == 'img':
return "<img src=" + node_obj["attrs"]["src"] + " />" + inner_html + ""
return "<img src=\"{}\" />{}".format(node_obj["attrs"]["src"], inner_html)
if node_type == 'embed':
return "<iframe src=" + node_obj["attrs"]["src"] + ">" + inner_html + "</iframe>"
return "<iframe src={}>{}</iframe>".format(node_obj["attrs"]["src"], inner_html)
if node_type == 'h1':
return "<h1>" + inner_html + "</h1>"
if node_type == 'h2':
Expand Down Expand Up @@ -102,7 +105,6 @@ def render_node(node_type, node_obj: dict, callback):
return "<tfoot>" + inner_html + "</tfoot>"
if node_type == 'tr':
return "<tr>" + inner_html + "</tr>"

if node_type == 'th':
return "<th>" + inner_html + "</th>"
if node_type == 'td':
Expand Down
Loading