Source code for khoros.objects.tags

# -*- coding: utf-8 -*-
"""
:Module:            khoros.objects.tags
:Synopsis:          This module includes functions that handle tags within a Khoros Community environment
:Usage:             ``from khoros.objects import tags``
:Example:           ``tags.add_single_tag_to_message('tutorial', 123)``
:Created By:        Jeff Shurtliff
:Last Modified:     Jeff Shurtliff
:Modified Date:     01 Oct 2022
"""

from .. import api, liql, errors
from ..utils import core_utils, log_utils

# Initialize the logger for this module
logger = log_utils.initialize_logging(__name__)

ITERABLE_TYPES = (list, tuple, set)


[docs] def structure_single_tag_payload(tag_text): """This function structures the payload for a single tag. .. versionadded:: 2.8.0 :param tag_text: The tag to be included in the payload :type tag_text: str :returns: The payload as a dictionary :raises: :py:exc:`khoros.errors.exceptions.InvalidPayloadValueError` """ if not isinstance(tag_text, str): raise errors.exceptions.InvalidPayloadValueError("The tag text must be in string format") payload = { "data": { "type": "tag", "text": tag_text } } return payload
[docs] def add_single_tag_to_message(khoros_object, tag, msg_id, allow_exceptions=False): """This function adds a single tag to an existing message. .. versionchanged:: 5.0.0 Removed the redundant return statement. .. versionadded:: 2.8.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :param tag: The tag value to be added :type tag: str :param msg_id: The unique identifier for the message :type msg_id: str, int :param allow_exceptions: Determines if exceptions are permitted to be raised (``False`` by default) :type allow_exceptions: bool :returns: None :raises: :py:exc:`khoros.errors.exceptions.POSTRequestError` """ payload = structure_single_tag_payload(tag) api_url = f"{khoros_object.core['v2_base']}/messages/{msg_id}/tags" response = api.post_request_with_retries(api_url, payload, khoros_object=khoros_object) if response['status'] != 'success': api_error = errors.handlers.get_error_from_json(response) if allow_exceptions: raise errors.exceptions.POSTRequestError(api_error) else: errors.handlers.eprint(api_error)
# TODO: Add logging
[docs] def get_tags_for_message(khoros_object, msg_id): """This function retrieves the tags for a given message. .. versionadded:: 2.8.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :param msg_id: The Message ID for the message from which to retrieve tags :type msg_id: str, int :returns: A list of tags associated with the message """ tag_list = [] query = f"SELECT text FROM tags WHERE messages.id = '{msg_id}'" # nosec response = liql.perform_query(khoros_object, liql_query=query, verify_success=True) entries = api.get_items_list(response) for entry in entries: tag_list.append(entry['text']) return tag_list
[docs] def structure_tags_for_message(*tags, khoros_object=None, msg_id=None, overwrite=False, ignore_non_strings=False, wrap_json=False): """This function structures tags to use within the payload for creating or updating a message. .. versionchanged:: 4.3.0 Introduced the ``wrap_json`` parameter to wrap the tags in a dictionary within the ``items`` key. .. versionchanged:: 4.1.0 The missing type declaration for the ``overwrite`` parameter has been added to the docstring. .. versionadded:: 2.8.0 :param tags: One or more tags or list of tags to be structured :type tags: str, list, tuple, set :param khoros_object: The core :py:class:`khoros.Khoros` object .. note:: The core object is only necessary when providing a Message ID as it will be needed to retrieve the existing tags from the message. :type khoros_object: class[khoros.Khoros], None :param msg_id: Message ID of an existing message so that its existing tags can be retrieved (optional) :type msg_id: str, int, None :param overwrite: Determines if tags should overwrite any existing tags (where applicable) or if the tags should be appended to the existing tags (default) :type overwrite: bool :param ignore_non_strings: Determines if non-strings (excluding iterables) should be ignored rather than converted to strings (``False`` by default) :type ignore_non_strings: bool :param wrap_json: Determines if the list of tags should be wrapped in the ``{"items": []}`` JSON structure -- In other words, a dictionary rather than a list (``False`` by default) :type wrap_json: bool :returns: A list of properly formatted tags to act as the value for the ``tags`` field in the message payload :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError` """ formatted_list, existing_tags = [], [] if msg_id and not any((overwrite, khoros_object)): raise errors.exceptions.MissingRequiredDataError('The core Khoros object must be provided when supplying ' 'a Message ID') elif msg_id and not overwrite: errors.handlers.verify_core_object_present(khoros_object) existing_tags = get_tags_for_message(khoros_object, msg_id) formatted_list = _format_tag_data(tags, ignore_non_strings) existing_tags = _format_tag_data(existing_tags, ignore_non_strings) complete_tags = core_utils.merge_and_dedup(formatted_list, existing_tags) complete_tags = {'items': complete_tags} if wrap_json else complete_tags return complete_tags
def _format_tag_data(_collection, _ignore_non_strings): """This function is used by :py:func:`khoros.objects.tags.structure_tags_for_message` to format the tag data. .. versionadded:: 2.8.0 """ _formatted_list = [] _collection = _get_low_level_tags(_collection) for _tag_or_list in _collection: if not isinstance(_tag_or_list, str) and _ignore_non_strings: continue else: _tag_or_list = (str(_tag_or_list),) for _tag in _tag_or_list: _tag = { "type": "tag", "text": _tag } _formatted_list.append(_tag) return _formatted_list def _get_low_level_tags(_collection): """This function expands a multi-level iterable to get the low-level tags. :param _collection: Iterable with one or more levels or a single tag :type _collection: list, tuple, set, str, int, float :returns: A list of all low-level tags (pre-formatted) """ _low_level_tags, _iterables = [], [] for _item in _collection: if type(_item) not in ITERABLE_TYPES: _low_level_tags.append(_item) else: _iterables.append(_item) while len(_iterables) > 0: _item = _iterables.pop() for _sub_item in _item: if type(_sub_item) not in ITERABLE_TYPES: _low_level_tags.append(_sub_item) else: _iterables.append(_sub_item) return _low_level_tags
[docs] def add_tags_to_message(khoros_object, tags, msg_id, allow_exceptions=False): """This function adds one or more tags to an existing message. .. versionchanged:: 5.0.0 Removed the redundant return statement. .. versionadded:: 2.8.0 ..caution:: This function is not the most effective way to add multiple tags to a message. It is recommended that the :py:func:`khoros.objects.messages.update` function be used instead with its ``tags`` argument, which is more efficient and performance-conscious. :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :param tags: One or more tags to be added to the message :type tags: str, tuple, list, set :param msg_id: The unique identifier for the message :type msg_id: str, int :param allow_exceptions: Determines if exceptions are permitted to be raised (``False`` by default) :type allow_exceptions: bool :returns: None :raises: :py:exc:`khoros.errors.exceptions.POSTRequestError` """ tags = (tags, ) if isinstance(tags, str) else tags for tag in tags: add_single_tag_to_message(khoros_object, str(tag), msg_id, allow_exceptions)