<commit_before>"""
Provide django models to back the discussions app
"""
from __future__ import annotations

import logging
from collections import namedtuple
from typing import List, Type, TypeVar

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_mysql.models import ListCharField
from enum import Enum  # lint-amnesty, pylint: disable=wrong-import-order
from jsonfield import JSONField
from lti_consumer.models import LtiConfiguration
from model_utils.models import TimeStampedModel
from opaque_keys.edx.django.models import LearningContextKeyField, UsageKeyField
from opaque_keys.edx.keys import CourseKey
from simple_history.models import HistoricalRecords

from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers

log = logging.getLogger(__name__)

ProviderExternalLinks = namedtuple(
    'ProviderExternalLinks',
    ['learn_more', 'configuration', 'general', 'accessibility', 'contact_email']
)


class Provider:
    """
    List of Discussion providers.
    """
    LEGACY = 'legacy'
    ED_DISCUSS = 'ed-discuss'
    INSCRIBE = 'inscribe'
    PIAZZA = 'piazza'
    YELLOWDIG = 'yellowdig'
    OPEN_EDX = 'openedx'


DEFAULT_PROVIDER_TYPE = Provider.LEGACY
DEFAULT_CONFIG_ENABLED = True


class Features(Enum):
    """
    Features to be used/mapped in discussion providers
    """

    # Basic Supported Features
    PRIMARY_DISCUSSION_APP_EXPERIENCE = ('primary-discussion-app-experience', 'basic')
    BASIC_CONFIGURATION = ('basic-configuration', 'basic')
    # DISCUSSION_PAGE = ('discussion-page', 'basic')

    # Partially Supported Features
    QUESTION_DISCUSSION_SUPPORT = ('question-discussion-support', 'partial')
    COMMUNITY_TA_SUPPORT = ('community-ta-support', 'partial')
    REPORT_FLAG_CONTENT_TO_MODERATORS = ('report/flag-content-to-moderators', 'partial')
    LTI_ADVANCED_SHARING_MODE = ('lti-advanced-sharing-mode', 'partial')
    AUTOMATIC_LEARNER_ENROLLMENT = ('automatic-learner-enrollment', 'partial')
    ANONYMOUS_POSTING = ('anonymous-posting', 'partial')
    INTERNATIONALIZATION_SUPPORT = ('internationalization-support', 'partial')
    EMAIL_NOTIFICATIONS = ('email-notifications', 'partial')
    WCAG_2_0_SUPPORT = ('wcag-2.0-support', 'partial')
    BLACKOUT_DISCUSSION_DATES = ('blackout-discussion-dates', 'partial')
    # WCAG_2_1 = ('wcag-2.1', 'partial')
    # EMBEDDED_COURSE_SECTIONS = ('embedded-course-sections', 'basic')

    # Fully Supported Features
    COURSE_COHORT_SUPPORT = ('course-cohort-support', 'full')
    RESEARCH_DATA_EVENTS = ('research-data-events', 'full')

    # Commonly Requested Features
    IN_PLATFORM_NOTIFICATIONS = ('in-platform-notifications', 'common')
    DISCUSSION_CONTENT_PROMPTS = ('discussion-content-prompts', 'common')
    GRADED_DISCUSSIONS = ('graded-discussions', 'common')
    DIRECT_MESSAGES_FROM_INSTRUCTORS = ('direct-messages-from-instructors', 'common')
    USER_MENTIONS = ('user-mentions', 'common')

    def __init__(self, feature_id, feature_support_type):
        self.feature_id = feature_id
        self.feature_support_type = feature_support_type

    @property
    def value(self):  # pylint: disable=invalid-overridden-method
        return self.feature_id

    @property
    def support(self):
        return self.feature_support_type


def pii_sharing_required_message(provider_name):
    """
    Build an i18n'ed message stating PII sharing is required for the provider.
    """
    return _(
        '{provider} requires that LTI advanced sharing be enabled for your course,'
        ' as this provider uses email address and username to personalize'
        ' the experience. Please contact {support_contact} to enable this feature.'
    ).format(
        provider=provider_name,
        support_contact=(
            configuration_helpers.get_value(
                'CONTACT_EMAIL',
                getattr(settings, 'CONTACT_EMAIL', _('technical support'))
            )
        )
    )


AVAILABLE_PROVIDER_MAP = {
    Provider.LEGACY: {
        'features': [
            Features.BASIC_CONFIGURATION.value,
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
            Features.COURSE_COHORT_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
        ],
        'supports_lti': False,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [],
        'has_full_support': True
    },
    Provider.OPEN_EDX: {
        'features': [
            Features.BASIC_CONFIGURATION.value,
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
            Features.COURSE_COHORT_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
        ],
        'supports_lti': False,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [],
        'has_full_support': True,
        'supports_in_context_discussions': True,
        'visible': False,
    },
    Provider.ED_DISCUSS: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.LTI_ADVANCED_SHARING_MODE.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
            Features.IN_PLATFORM_NOTIFICATIONS.value,
            Features.USER_MENTIONS.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='https://edstem.org/us/',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [pii_sharing_required_message('Ed Discussion')],
        'has_full_support': False
    },
    Provider.INSCRIBE: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.LTI_ADVANCED_SHARING_MODE.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
            Features.IN_PLATFORM_NOTIFICATIONS.value,
            Features.DISCUSSION_CONTENT_PROMPTS.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='https://www.inscribeapp.com/',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [pii_sharing_required_message('InScribe')],
        'has_full_support': False
    },
    Provider.PIAZZA: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.LTI_ADVANCED_SHARING_MODE.value,
            Features.ANONYMOUS_POSTING.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='https://piazza.com/product/overview',
            configuration='https://support.piazza.com/support/solutions/articles/48001065447-configure-piazza-within-edx',  # pylint: disable=line-too-long
            general='https://support.piazza.com/',
            accessibility='https://piazza.com/product/accessibility',
            contact_email='<EMAIL>',
        )._asdict(),
        'messages': [],
        'has_full_support': False
    },
    Provider.YELLOWDIG: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
            Features.IN_PLATFORM_NOTIFICATIONS.value,
            Features.GRADED_DISCUSSIONS.value,
            Features.DIRECT_MESSAGES_FROM_INSTRUCTORS.value,
            Features.USER_MENTIONS.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='https://www.youtube.com/watch?v=ZACief-qMwY',
            configuration='',
            general='https://hubs.ly/H0J5Bn70',
            accessibility='',<commit_msg>chore: update lti providers video link (#29802)<commit_after>"""
Provide django models to back the discussions app
"""
from __future__ import annotations

import logging
from collections import namedtuple
from typing import List, Type, TypeVar

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_mysql.models import ListCharField
from enum import Enum  # lint-amnesty, pylint: disable=wrong-import-order
from jsonfield import JSONField
from lti_consumer.models import LtiConfiguration
from model_utils.models import TimeStampedModel
from opaque_keys.edx.django.models import LearningContextKeyField, UsageKeyField
from opaque_keys.edx.keys import CourseKey
from simple_history.models import HistoricalRecords

from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers

log = logging.getLogger(__name__)

ProviderExternalLinks = namedtuple(
    'ProviderExternalLinks',
    ['learn_more', 'configuration', 'general', 'accessibility', 'contact_email']
)


class Provider:
    """
    List of Discussion providers.
    """
    LEGACY = 'legacy'
    ED_DISCUSS = 'ed-discuss'
    INSCRIBE = 'inscribe'
    PIAZZA = 'piazza'
    YELLOWDIG = 'yellowdig'
    OPEN_EDX = 'openedx'


DEFAULT_PROVIDER_TYPE = Provider.LEGACY
DEFAULT_CONFIG_ENABLED = True


class Features(Enum):
    """
    Features to be used/mapped in discussion providers
    """

    # Basic Supported Features
    PRIMARY_DISCUSSION_APP_EXPERIENCE = ('primary-discussion-app-experience', 'basic')
    BASIC_CONFIGURATION = ('basic-configuration', 'basic')
    # DISCUSSION_PAGE = ('discussion-page', 'basic')

    # Partially Supported Features
    QUESTION_DISCUSSION_SUPPORT = ('question-discussion-support', 'partial')
    COMMUNITY_TA_SUPPORT = ('community-ta-support', 'partial')
    REPORT_FLAG_CONTENT_TO_MODERATORS = ('report/flag-content-to-moderators', 'partial')
    LTI_ADVANCED_SHARING_MODE = ('lti-advanced-sharing-mode', 'partial')
    AUTOMATIC_LEARNER_ENROLLMENT = ('automatic-learner-enrollment', 'partial')
    ANONYMOUS_POSTING = ('anonymous-posting', 'partial')
    INTERNATIONALIZATION_SUPPORT = ('internationalization-support', 'partial')
    EMAIL_NOTIFICATIONS = ('email-notifications', 'partial')
    WCAG_2_0_SUPPORT = ('wcag-2.0-support', 'partial')
    BLACKOUT_DISCUSSION_DATES = ('blackout-discussion-dates', 'partial')
    # WCAG_2_1 = ('wcag-2.1', 'partial')
    # EMBEDDED_COURSE_SECTIONS = ('embedded-course-sections', 'basic')

    # Fully Supported Features
    COURSE_COHORT_SUPPORT = ('course-cohort-support', 'full')
    RESEARCH_DATA_EVENTS = ('research-data-events', 'full')

    # Commonly Requested Features
    IN_PLATFORM_NOTIFICATIONS = ('in-platform-notifications', 'common')
    DISCUSSION_CONTENT_PROMPTS = ('discussion-content-prompts', 'common')
    GRADED_DISCUSSIONS = ('graded-discussions', 'common')
    DIRECT_MESSAGES_FROM_INSTRUCTORS = ('direct-messages-from-instructors', 'common')
    USER_MENTIONS = ('user-mentions', 'common')

    def __init__(self, feature_id, feature_support_type):
        self.feature_id = feature_id
        self.feature_support_type = feature_support_type

    @property
    def value(self):  # pylint: disable=invalid-overridden-method
        return self.feature_id

    @property
    def support(self):
        return self.feature_support_type


def pii_sharing_required_message(provider_name):
    """
    Build an i18n'ed message stating PII sharing is required for the provider.
    """
    return _(
        '{provider} requires that LTI advanced sharing be enabled for your course,'
        ' as this provider uses email address and username to personalize'
        ' the experience. Please contact {support_contact} to enable this feature.'
    ).format(
        provider=provider_name,
        support_contact=(
            configuration_helpers.get_value(
                'CONTACT_EMAIL',
                getattr(settings, 'CONTACT_EMAIL', _('technical support'))
            )
        )
    )


AVAILABLE_PROVIDER_MAP = {
    Provider.LEGACY: {
        'features': [
            Features.BASIC_CONFIGURATION.value,
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
            Features.COURSE_COHORT_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
        ],
        'supports_lti': False,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [],
        'has_full_support': True
    },
    Provider.OPEN_EDX: {
        'features': [
            Features.BASIC_CONFIGURATION.value,
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
            Features.COURSE_COHORT_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
        ],
        'supports_lti': False,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [],
        'has_full_support': True,
        'supports_in_context_discussions': True,
        'visible': False,
    },
    Provider.ED_DISCUSS: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.LTI_ADVANCED_SHARING_MODE.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
            Features.IN_PLATFORM_NOTIFICATIONS.value,
            Features.USER_MENTIONS.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='https://edstem.org/us/',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [pii_sharing_required_message('Ed Discussion')],
        'has_full_support': False
    },
    Provider.INSCRIBE: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.LTI_ADVANCED_SHARING_MODE.value,
            Features.AUTOMATIC_LEARNER_ENROLLMENT.value,
            Features.ANONYMOUS_POSTING.value,
            Features.INTERNATIONALIZATION_SUPPORT.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
            Features.IN_PLATFORM_NOTIFICATIONS.value,
            Features.DISCUSSION_CONTENT_PROMPTS.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='',
            configuration='',
            general='https://www.inscribeapp.com/',
            accessibility='',
            contact_email='',
        )._asdict(),
        'messages': [pii_sharing_required_message('InScribe')],
        'has_full_support': False
    },
    Provider.PIAZZA: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.LTI_ADVANCED_SHARING_MODE.value,
            Features.ANONYMOUS_POSTING.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.BLACKOUT_DISCUSSION_DATES.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='https://piazza.com/product/overview',
            configuration='https://support.piazza.com/support/solutions/articles/48001065447-configure-piazza-within-edx',  # pylint: disable=line-too-long
            general='https://support.piazza.com/',
            accessibility='https://piazza.com/product/accessibility',
            contact_email='<EMAIL>',
        )._asdict(),
        'messages': [],
        'has_full_support': False
    },
    Provider.YELLOWDIG: {
        'features': [
            Features.PRIMARY_DISCUSSION_APP_EXPERIENCE.value,
            Features.BASIC_CONFIGURATION.value,
            Features.QUESTION_DISCUSSION_SUPPORT.value,
            Features.COMMUNITY_TA_SUPPORT.value,
            Features.REPORT_FLAG_CONTENT_TO_MODERATORS.value,
            Features.EMAIL_NOTIFICATIONS.value,
            Features.WCAG_2_0_SUPPORT.value,
            Features.RESEARCH_DATA_EVENTS.value,
            Features.IN_PLATFORM_NOTIFICATIONS.value,
            Features.GRADED_DISCUSSIONS.value,
            Features.DIRECT_MESSAGES_FROM_INSTRUCTORS.value,
            Features.USER_MENTIONS.value,
        ],
        'supports_lti': True,
        'external_links': ProviderExternalLinks(
            learn_more='https://youtu.be/oOcvjjMVFAw',
            configuration='',
            general='https://hubs.ly/H0J5Bn70',
            accessibility='',
{'fraction_non_alphanumeric': 0.09420928183967732, 'fraction_numerical': 0.0036626640085307615, 'mean_word_length': 2.844234539297808, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 16, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7050034', 'n_tokens_mistral': 6636, 'n_tokens_neox': 6280, 'n_words': 919}