Source code for khoros.structures.grouphubs

# -*- coding: utf-8 -*-
"""
:Module:            khoros.structures.grouphubs
:Synopsis:          This module contains functions specific to group hubs within the Khoros Community platform
:Usage:             ``from khoros.structures import grouphubs``
:Example:           ``group_hub_url = grouphubs.create(khoros_object, gh_id, gh_title, disc_styles, return_url=True)``
:Created By:        Jeff Shurtliff
:Last Modified:     Jeff Shurtliff
:Modified Date:     23 May 2022
"""

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

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

# Define the default discussion styles to use when creating new group hubs
DEFAULT_DISCUSSION_STYLES = ['blog', 'contest', 'forum', 'idea', 'qanda', 'tkb']

# Define a global variable to identify all discussion styles enabled for the environment where available
all_discussion_styles = DEFAULT_DISCUSSION_STYLES


[docs] def create(khoros_object, group_id, group_title, description=None, membership_type=None, open_group=None, closed_group=None, hidden_group=None, discussion_styles=None, enable_blog=None, enable_contest=None, enable_forum=None, enable_idea=None, enable_qanda=None, enable_tkb=None, all_styles_default=True, parent_category_id=None, avatar_image_path=None, full_response=None, return_id=None, return_url=None, return_api_url=None, return_http_code=None, return_status=None, return_error_messages=None, split_errors=False): """This function creates a new group hub within a Khoros Community environment. .. versionchanged:: 2.7.2 Changed the data type for ``membership_type`` from ``dict`` to ``str`` in the docstring. .. versionadded:: 2.6.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :param group_id: The unique identifier (i.e. ``id`` field) for the new group hub **(Required)** :type group_id: str, int :param group_title: The title of the group hub **(Required)** :type group_title: str :param description: A brief description of the group hub :type description: str, None :param membership_type: The ``membership_type`` value (``open``, ``closed`` or ``closed_hidden``) :type membership_type: str, None :param open_group: Defines the group hub as an open group :type open_group: bool, None :param closed_group: Defines the group hub as a closed group :type closed_group: bool, None :param hidden_group: Defines the group hub as a closed and hidden group :type hidden_group: bool, None :param discussion_styles: A list of discussion styles that will be permitted in the group hub :type discussion_styles: list, None :param enable_blog: Defines that the **blog** discussion style should be enabled for the group hub :type enable_blog: bool, None :param enable_contest: Defines that the **contest** discussion style should be enabled for the group hub :type enable_contest: bool, None :param enable_forum: Defines that the **forum** discussion style should be enabled for the group hub :type enable_forum: bool, None :param enable_idea: Defines that the **idea** discussion style should be enabled for the group hub :type enable_idea: bool, None :param enable_qanda: Defines that the **Q&A** (``qanda``) discussion style should be enabled for the group hub :type enable_qanda: bool, None :param enable_tkb: Defines that the **TKB** (``tkb``) discussion style should be enabled for the group hub :type enable_tkb: bool, None :param all_styles_default: Defines that all discussion styles should be enabled if not otherwise specified :type all_styles_default: bool :param parent_category_id: The parent category identifier (if applicable) :type parent_category_id: str, int, None :param avatar_image_path: The file path to the avatar image to be uploaded (if applicable) :type avatar_image_path: str, None :param full_response: Determines whether the full, raw API response should be returned by the function .. caution:: This argument overwrites the ``return_id``, ``return_url``, ``return_api_url``, ``return_http_code``, ``return_status`` and ``return_error_messages`` arguments. :type full_response: bool, None :param return_id: Determines if the **ID** of the new group hub should be returned by the function :type return_id: bool, None :param return_url: Determines if the **URL** of the new group hub should be returned by the function :type return_url: bool, None :param return_api_url: Determines if the **API URL** of the new group hub should be returned by the function :type return_api_url: bool, None :param return_http_code: Determines if the **HTTP Code** of the API response should be returned by the function :type return_http_code: bool, None :param return_status: Determines if the **Status** of the API response should be returned by the function :type return_status: bool, None :param return_error_messages: Determines if any error messages associated with the API response should be returned by the function :type return_error_messages: bool, None :param split_errors: Defines whether or not error messages should be merged when applicable :type split_errors: bool :returns: Boolean value indicating a successful outcome (default), the full API response or one or more specific fields defined by function arguments :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError`, :py:exc:`khoros.errors.exceptions.InvalidPayloadValueError`, :py:exc:`khoros.errors.exceptions.APIConnectionError`, :py:exc:`khoros.errors.exceptions.POSTRequestError` """ payload = structure_payload(khoros_object, group_id, group_title, description, membership_type, open_group, closed_group, hidden_group, discussion_styles, enable_blog, enable_contest, enable_forum, enable_idea, enable_qanda, enable_tkb, all_styles_default, parent_category_id) api_url = f"{khoros_object.core['v2_base']}/grouphubs" if avatar_image_path: response = _create_group_hub_with_avatar(khoros_object, api_url, payload, avatar_image_path) else: response = _create_group_hub_without_avatar(khoros_object, api_url, payload) return api.deliver_v2_results(response, full_response, return_id, return_url, return_api_url, return_http_code, return_status, return_error_messages, split_errors)
def _create_group_hub_with_avatar(_khoros_object, _api_url, _payload, _avatar_image_path): """This function creates a group hub with both a JSON payload and an image file to use as its avatar. .. versionchanged:: 5.0.0 The function now passes a defined content-type value for the API header which was previously unused. :param _khoros_object: The core :py:class:`khoros.Khoros` object :type _khoros_object: class[khoros.Khoros] :param _api_url: The API URL to utilize in the API request :type _api_url: str :param _payload: The JSON payload to be used in the API request :type _payload: dict :param _avatar_image_path: The file path to the avatar image to be uploaded (if applicable) :type _avatar_image_path: str :returns: The API response from the multipart POST request :raises: :py:exc:`FileNotFoundError`, :py:exc:`khoros.errors.exceptions.APIConnectionError`, :py:exc:`khoros.errors.exceptions.POSTRequestError` """ _content_type = 'application/x-www-form-urlencoded' _full_payload = api.combine_json_and_avatar_payload(_payload, _avatar_image_path) response = api.post_request_with_retries(_api_url, _full_payload, khoros_object=_khoros_object, multipart=True, content_type=_content_type) return response def _create_group_hub_without_avatar(_khoros_object, _api_url, _payload): """This function creates a group hub with only a JSON payload and no avatar image. .. versionadded:: 2.6.0 :param _khoros_object: The core :py:class:`khoros.Khoros` object :type _khoros_object: class[khoros.Khoros] :param _api_url: The API URL to utilize in the API request :type _api_url: str :param _payload: The JSON payload to be used in the API request :type _payload: dict :returns: The API response from the POST request :raises: :py:exc:`khoros.errors.exceptions.APIConnectionError`, :py:exc:`khoros.errors.exceptions.POSTRequestError` """ _headers = {'content-type': 'application/json'} _response = api.post_request_with_retries(_api_url, _payload, khoros_object=_khoros_object, headers=_headers) return _response
[docs] def structure_payload(khoros_object, group_id, group_title, description=None, membership_type=None, open_group=None, closed_group=None, hidden_group=None, discussion_styles=None, enable_blog=None, enable_contest=None, enable_forum=None, enable_idea=None, enable_qanda=None, enable_tkb=None, all_styles_default=True, parent_category_id=None): """This function structures the payload to use in a Group Hub API request. .. versionchanged:: 2.7.3 Changed the ``grouphub`` value in the initial ``payload`` definition to be a dictionary rather than a string to mitigate a :py:exc:`TypeError` exception getting raised. .. versionchanged:: 2.7.2 Changed the data type for ``membership_type`` from ``dict`` to ``str`` in the docstring and fixed some bad logic raising false positive exceptions. .. versionadded:: 2.6.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :param group_id: The unique identifier (i.e. ``id`` field) for the new group hub **(Required)** :type group_id: str, int :param group_title: The title of the group hub **(Required)** :type group_title: str :param description: A brief description of the group hub :type description: str, None :param membership_type: The ``membership_type`` value (``open``, ``closed`` or ``closed_hidden``) :type membership_type: str, None :param open_group: Defines the group hub as an open group :type open_group: bool, None :param closed_group: Defines the group hub as a closed group :type closed_group: bool, None :param hidden_group: Defines the group hub as a closed and hidden group :type hidden_group: bool, None :param discussion_styles: A list of discussion styles that will be permitted in the group hub :type discussion_styles: list, None :param enable_blog: Defines that the **blog** discussion style should be enabled for the group hub :type enable_blog: bool, None :param enable_contest: Defines that the **contest** discussion style should be enabled for the group hub :type enable_contest: bool, None :param enable_forum: Defines that the **forum** discussion style should be enabled for the group hub :type enable_forum: bool, None :param enable_idea: Defines that the **idea** discussion style should be enabled for the group hub :type enable_idea: bool, None :param enable_qanda: Defines that the **Q&A** (``qanda``) discussion style should be enabled for the group hub :type enable_qanda: bool, None :param enable_tkb: Defines that the **TKB** (``tkb``) discussion style should be enabled for the group hub :type enable_tkb: bool, None :param all_styles_default: Defines that all discussion styles should be enabled if not otherwise specified :type all_styles_default: bool :param parent_category_id: The parent category identifier (if applicable) :type parent_category_id: str, int, None :returns: The properly formatted payload for the API request :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError`, :py:exc:`khoros.errors.exceptions.InvalidPayloadValueError` """ payload = {"grouphub": {}} refresh_enabled_discussion_styles(khoros_object) required_error_msg = "The 'group_id', 'group_title' and 'membership_type' fields are required " \ "to create a group hub." if not all((khoros_object, group_id, group_title, membership_type)): raise errors.exceptions.MissingRequiredDataError(required_error_msg) payload = _structure_simple_string_fields(payload, group_id, group_title, description) payload = _structure_membership_type(payload, membership_type, open_group, closed_group, hidden_group) payload = _structure_discussion_styles(payload, discussion_styles, enable_blog, enable_contest, enable_forum, enable_idea, enable_qanda, enable_tkb, all_styles_default) payload = _structure_parent_category(payload, parent_category_id) return payload
def _structure_simple_string_fields(_payload, _group_id, _group_title, _description=None): """This function populates the payload with the group hub ID, title and description. .. versionadded:: 2.6.0 :param _payload: The payload for a Group Hub API call :type _payload: dict :param _group_id: The unique identifier (ID) of the group hub :type _group_id: str :param _group_title: The title of the group hub :type _group_title: str :param _description: A brief description of the group hub :type _description: str, None :returns: The payload with the additional fields """ _payload['grouphub']['id'] = str(_group_id) _payload['grouphub']['title'] = str(_group_title) if _description: _payload['grouphub']['description'] = _description return _payload def _structure_membership_type(_payload, _membership_type, _open_group, _closed_group, _hidden_group): """This function populates the payload with the ``membership_type`` data. .. versionchanged:: 2.7.2 Changed the data type for ``membership_type`` from ``dict`` to ``str`` in the docstring. .. versionadded:: 2.6.0 :param _payload: The payload for a Group Hub API call :type _payload: dict :param _membership_type: The ``membership_type`` value (``open``, ``closed`` or ``closed_hidden``) :type _membership_type: str, None :param _open_group: Defines the group hub as an open group :type _open_group: bool, None :param _closed_group: Defines the group hub as a closed group :type _closed_group: bool, None :param _hidden_group: Defines the group hub as a closed and hidden group :type _hidden_group: bool, None :returns: The payload with the populated ``membership_type`` field :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError` """ _valid_membership_types = ['open', 'closed', 'closed_hidden'] _required_msg = "The membership type must be defined when creating a new group hub." if not any((_open_group, _closed_group, _hidden_group)) and _membership_type not in _valid_membership_types: raise errors.exceptions.MissingRequiredDataError(_required_msg) elif _membership_type and (_membership_type in _valid_membership_types): _payload['grouphub']['membership_type'] = _membership_type else: _types_and_values = {'open': _open_group, 'closed': _closed_group, 'closed_hidden': _hidden_group} for _type, _value in _types_and_values.items(): if _value: _payload['grouphub']['membership_type'] = _type break return _payload def _structure_discussion_styles(_payload, _discussion_styles=None, _enable_blog=None, _enable_contest=None, _enable_forum=None, _enable_idea=None, _enable_qanda=None, _enable_tkb=None, _all_styles_default=True): """This function defines the permitted discussion styles within the payload of a group hub API request. .. versionadded:: 2.6.0 :param _payload: The payload to which the information should be added :type _payload: dict :param _discussion_styles: A list of discussion styles that will be permitted in the group hub :type _discussion_styles: list, None :param _enable_blog: Defines that the **blog** discussion style should be enabled for the group hub :type _enable_blog: bool, None :param _enable_contest: Defines that the **contest** discussion style should be enabled for the group hub :type _enable_contest: bool, None :param _enable_forum: Defines that the **forum** discussion style should be enabled for the group hub :type _enable_forum: bool, None :param _enable_idea: Defines that the **idea** discussion style should be enabled for the group hub :type _enable_idea: bool, None :param _enable_qanda: Defines that the **Q&A** (``qanda``) discussion style should be enabled for the group hub :type _enable_qanda: bool, None :param _enable_tkb: Defines that the **TKB** (``tkb``) discussion style should be enabled for the group hub :type _enable_tkb: bool, None :param _all_styles_default: Defines that all discussion styles should be enabled if not otherwise specified :type _all_styles_default: bool :returns: The payload with the included ``conversation_styles`` field and value(s) :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError`, :py:exc:`khoros.errors.exceptions.InvalidPayloadValueError` """ _required_msg = "At least one discussion style must be defined when creating a new group hub." if not any((_discussion_styles, _enable_blog, _enable_contest, _enable_forum, _enable_idea, _enable_qanda, _enable_tkb)): if _all_styles_default: _discussion_styles = all_discussion_styles else: raise errors.exceptions.MissingRequiredDataError(_required_msg) if _discussion_styles: if isinstance(_discussion_styles, str): _discussion_styles = [_discussion_styles] elif not isinstance(_discussion_styles, list): raise errors.exceptions.InvalidPayloadValueError(value=_discussion_styles, field='conversation_styles') for _style in _discussion_styles: if _style not in all_discussion_styles: raise errors.exceptions.InvalidPayloadValueError(value=_style, field='conversation_styles') _payload['grouphub']['conversation_styles'] = _discussion_styles else: _discussion_styles = [] _discussion_toggles = { 'blog': _enable_blog, 'contest': _enable_contest, 'forum': _enable_forum, 'idea': _enable_idea, 'qanda': _enable_qanda, 'tkb': _enable_tkb } for _value, _toggle in _discussion_toggles.items(): if _toggle: _discussion_styles.append(_value) _payload['grouphub']['conversation_styles'] = _discussion_styles _payload = _remove_disabled_discussion_styles(_payload) return _payload def _remove_disabled_discussion_styles(_payload): """This function checks for any discussion styles that are disabled in the environment and removes them. :param _payload: The JSON payload to be used in an API request :type _payload: dict :returns: The payload with only enabled discussion styles """ for _style in _payload['grouphub']['conversation_styles']: if _style not in all_discussion_styles: errors.handlers.eprint(f"The discussion style '{_style}' will be removed from the payload as it is a " "disabled discussion style in the environment.") _payload['grouphub']['conversation_styles'].remove(_style) return _payload def _structure_parent_category(_payload, _parent_id): """This function structures the parent category field for a group hub if applicable. .. versionadded:: 2.6.0 :param _payload: The payload to which the parent category field should be added :type _payload: dict :param _parent_id: The parent category identifier :type _parent_id: int, str, None :returns: The payload with the added field if applicable """ if _parent_id: _parent_dict = {'id': str(_parent_id)} _payload['grouphub']['parent_category'] = _parent_dict return _payload
[docs] def get_total_count(khoros_object): """This function returns the total number of group hubs within the Khoros Community environment. .. versionadded:: 2.6.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :returns: The total number of group hubs as an integer """ return liql.get_total_count(khoros_object, 'grouphubs')
[docs] def get_grouphub_id(url): """This function retrieves the Group Hub ID for a given group hub when provided its URL. .. versionadded:: 2.6.0 :param url: The URL from which to parse out the Group Hub ID :type url: str :returns: The Group Hub ID retrieved from the URL :raises: :py:exc:`khoros.errors.exceptions.InvalidURLError` """ return base.get_structure_id(url)
[docs] def grouphub_exists(khoros_object, grouphub_id=None, grouphub_url=None): """This function checks to see if a group hub exists. .. versionadded:: 2.7.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :param grouphub_id: The ID of the group hub to check :type grouphub_id: str, None :param grouphub_url: The URL of the group hub to check :type grouphub_url: str, None :returns: Boolean value indicating whether or not the group hub already exists :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError` """ return base.structure_exists(khoros_object, 'grouphub', grouphub_id, grouphub_url)
[docs] def refresh_enabled_discussion_styles(khoros_object): """This function refreshes the ``all_discussion_styles`` global variable to match what is in the core object settings when applicable. .. versionchanged:: 5.0.0 Removed the redundant return statement. .. versionchanged:: 3.3.0 Updated ``khoros_object._settings`` to be ``khoros_object.core_settings``. .. versionadded:: 2.6.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :returns: None """ if 'discussion_styles' in khoros_object.core_settings: global all_discussion_styles all_discussion_styles = khoros_object.core_settings.get('discussion_styles')
def _verify_group_hub_id(_group_hub_id, _group_hub_url): """This function verifies the Group Hub ID and looks it up as necessary using the Group Hub URL. .. versionadded:: 2.6.0 :param _group_hub_id: The Group Hub ID supplied in the parent function :type _group_hub_id: str, None :param _group_hub_url: The Group Hub URL supplied in the parent function :type _group_hub_url: str, None :returns: The Group Hub ID :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError` """ if not any((_group_hub_id, _group_hub_url)): raise errors.exceptions.MissingRequiredDataError("An ID or URL for the group hub must be provided.") if not _group_hub_id: _group_hub_id = get_grouphub_id(_group_hub_url) return _group_hub_id def _structure_empty_payload(): """This function returns the empty payload for a grouphub that is ready to be populated. .. versionadded:: 2.6.0 :returns: The empty group hub payload as a dictionary """ return {"grouphub": {}}
[docs] def update_title(khoros_object, new_title, group_hub_id=None, group_hub_url=None, full_response=None, return_id=None, return_url=None, return_api_url=None, return_http_code=None, return_status=None, return_error_messages=None, split_errors=False): """This function updates the title of an existing group hub. .. versionadded:: 2.6.0 :param khoros_object: The core :py:class:`khoros.Khoros` object :type khoros_object: class[khoros.Khoros] :param new_title: The new title for the group hub :type new_title: str :param group_hub_id: The group hub ID that identifies the group hub to update (necessary if URL not provided) :type group_hub_id: str, None :param group_hub_url: The group hub URL that identifies the group hub to update (necessary if ID not provided) :type group_hub_url: str, None :param full_response: Determines whether the full, raw API response should be returned by the function .. caution:: This argument overwrites the ``return_id``, ``return_url``, ``return_api_url``, ``return_http_code``, ``return_status`` and ``return_error_messages`` arguments. :type full_response: bool, None :param return_id: Determines if the **ID** of the new group hub should be returned by the function :type return_id: bool, None :param return_url: Determines if the **URL** of the new group hub should be returned by the function :type return_url: bool, None :param return_api_url: Determines if the **API URL** of the new group hub should be returned by the function :type return_api_url: bool, None :param return_http_code: Determines if the **HTTP Code** of the API response should be returned by the function :type return_http_code: bool, None :param return_status: Determines if the **Status** of the API response should be returned by the function :type return_status: bool, None :param return_error_messages: Determines if any error messages associated with the API response should be returned by the function :type return_error_messages: bool, None :param split_errors: Defines whether or not error messages should be merged when applicable :type split_errors: bool :returns: Boolean value indicating a successful outcome (default), the full API response or one or more specific fields defined by function arguments :raises: :py:exc:`khoros.errors.exceptions.MissingRequiredDataError`, :py:exc:`khoros.errors.exceptions.APIConnectionError`, :py:exc:`khoros.errors.exceptions.PUTRequestError` """ group_hub_id = _verify_group_hub_id(group_hub_id, group_hub_url) payload = _structure_empty_payload() payload['grouphub'] = {"title": new_title} api_url = f"{khoros_object.core['v2_base']}/grouphubs/{group_hub_id}" response = api.put_request_with_retries(api_url, payload, khoros_object=khoros_object) return api.deliver_v2_results(response, full_response, return_id, return_url, return_api_url, return_http_code, return_status, return_error_messages, split_errors)