diff --git a/.gitignore b/.gitignore index 47224ed..76e1f93 100644 --- a/.gitignore +++ b/.gitignore @@ -84,7 +84,7 @@ target/ # pyenv .python-version -# celery beat schedule file +# celery beat schedule self celerybeat-schedule # SageMath parsed files diff --git a/README.md b/README.md index b9cf9cb..7500757 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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) ``` @@ -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) +``` diff --git a/changelog.rst b/changelog.rst index fda7a6e..14b2f48 100644 --- a/changelog.rst +++ b/changelog.rst @@ -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* ============ diff --git a/contentstack_utils/__init__.py b/contentstack_utils/__init__.py index d52bcd9..3baf961 100644 --- a/contentstack_utils/__init__.py +++ b/contentstack_utils/__init__.py @@ -17,6 +17,6 @@ __title__ = 'contentstack_utils' __author__ = 'contentstack' __status__ = 'debug' -__version__ = '0.0.1' +__version__ = '1.1.0' __endpoint__ = 'cdn.contentstack.io' __contact__ = 'support@contentstack.com' diff --git a/contentstack_utils/automate.py b/contentstack_utils/automate.py new file mode 100644 index 0000000..11b7409 --- /dev/null +++ b/contentstack_utils/automate.py @@ -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 diff --git a/contentstack_utils/gql.py b/contentstack_utils/gql.py new file mode 100644 index 0000000..58d32ea --- /dev/null +++ b/contentstack_utils/gql.py @@ -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 diff --git a/contentstack_utils/helper/converter.py b/contentstack_utils/helper/converter.py index e18f7d8..803bac9 100644 --- a/contentstack_utils/helper/converter.py +++ b/contentstack_utils/helper/converter.py @@ -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 diff --git a/contentstack_utils/render/options.py b/contentstack_utils/render/options.py index 1857263..7c63ecb 100644 --- a/contentstack_utils/render/options.py +++ b/contentstack_utils/render/options.py @@ -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 '
' + _title_or_uid(_obj) \ - + '
Content type: ' + _obj['_content_type_uid'] \ + + '
Content type: ' + _content_type_uid \ + '
" + inner_html + "
" if node_type == 'a': - return "" + inner_html + "" + return "{}".format(node_obj["attrs"]["href"], inner_html) if node_type == 'img': - return "