<commit_before>"""
Course Duration Limit Configuration Models
"""

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone

from course_modes.models import CourseMode
from lms.djangoapps.courseware.masquerade import (
    get_course_masquerade,
    get_masquerade_role,
    is_masquerading_as_specific_student
)
from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel
from openedx.core.djangoapps.config_model_utils.utils import is_in_holdback
from openedx.features.content_type_gating.helpers import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
from openedx.features.course_duration_limits.config import (
    CONTENT_TYPE_GATING_FLAG,
    FEATURE_BASED_ENROLLMENT_GLOBAL_KILL_FLAG,
)
from student.models import CourseEnrollment
from student.role_helpers import has_staff_roles
from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID


@python_2_unicode_compatible
class CourseDurationLimitConfig(StackedConfigurationModel):
    """
    Configuration to manage the Course Duration Limit facility.

    .. no_pii:
    """

    STACKABLE_FIELDS = ('enabled', 'enabled_as_of')

    enabled_as_of = models.DateTimeField(
        default=None,
        null=True,
        verbose_name=_('Enabled As Of'),
        blank=True,
        help_text=_(
            'If the configuration is Enabled, then all enrollments '
            'created after this date and time (UTC) will be affected.'
        )
    )

    @classmethod
    def has_full_access_role_in_masquerade(cls, user, course_key, course_masquerade):
        """
        When masquerading, the course duration limits will never trigger the course to expire, redirecting the user.
        The roles of the masquerade user are still used to determine whether the course duration limit banner displays.
        Another banner also displays if the course is expired for the masquerade user.
        Both banners will appear if the masquerade user does not have any of the following roles:
        Staff, Instructor, Beta Tester, Forum Community TA, Forum Group Moderator, Forum Moderator, Forum Administrator
        """
        masquerade_role = get_masquerade_role(user, course_key)
        verified_mode_id = settings.COURSE_ENROLLMENT_MODES.get(CourseMode.VERIFIED, {}).get('id')
        # Masquerading users can select the the role of a verified users without selecting a specific user
        is_verified = (course_masquerade.user_partition_id == ENROLLMENT_TRACK_PARTITION_ID
                       and course_masquerade.group_id == verified_mode_id)
        # Masquerading users can select the role of staff without selecting a specific user
        is_staff = masquerade_role == 'staff'
        # Masquerading users can select other full access roles for which content type gating is disabled
        is_full_access = (course_masquerade.user_partition_id == CONTENT_GATING_PARTITION_ID
                          and course_masquerade.group_id == CONTENT_TYPE_GATE_GROUP_IDS['full_access'])
        # When masquerading as a specific user, we can check that user's staff roles as we would with a normal user
        is_staff_role = False
        if course_masquerade.user_name:
            is_staff_role = has_staff_roles(user, course_key)

        if is_verified or is_full_access or is_staff or is_staff_role:
            return True
        return False

    @classmethod
    def enabled_for_enrollment(cls, enrollment=None, user=None, course_key=None):
        """
        Return whether Course Duration Limits are enabled for this enrollment.

        Course Duration Limits are enabled for an enrollment if they are enabled for
        the course being enrolled in (either specifically, or via a containing context,
        such as the org, site, or globally), and if the configuration is specified to be
        ``enabled_as_of`` before the enrollment was created.

        Only one of enrollment and (user, course_key) may be specified at a time.

        Arguments:
            enrollment: The enrollment being queried.
            user: The user being queried.
            course_key: The CourseKey of the course being queried.
        """

        if FEATURE_BASED_ENROLLMENT_GLOBAL_KILL_FLAG.is_enabled():
            return False

        if CONTENT_TYPE_GATING_FLAG.is_enabled():
            return True

        if enrollment is not None and (user is not None or course_key is not None):
            raise ValueError('Specify enrollment or user/course_key, but not both')

        if enrollment is None and (user is None or course_key is None):
            raise ValueError('Both user and course_key must be specified if no enrollment is provided')

        if enrollment is None and user is None and course_key is None:
            raise ValueError('At least one of enrollment or user and course_key must be specified')

        if course_key is None:
            course_key = enrollment.course_id

        if enrollment is None:
            enrollment = CourseEnrollment.get_enrollment(user, course_key)

        if user is None and enrollment is not None:
            user = enrollment.user

        if user and user.id:
            course_masquerade = get_course_masquerade(user, course_key)
            if course_masquerade:
                if cls.has_full_access_role_in_masquerade(user, course_key, course_masquerade):
                    return False
            elif has_staff_roles(user, course_key):
                return False

        no_masquerade = get_course_masquerade(user, course_key) is None
        student_masquerade = is_masquerading_as_specific_student(user, course_key)

        # check if user is in holdback
        if (no_masquerade or student_masquerade) and is_in_holdback(user):
            return False

        # enrollment might be None if the user isn't enrolled. In that case,
        # return enablement as if the user enrolled today
        # Also, ignore enrollment creation date if the user is masquerading.
        if enrollment is None or not no_masquerade:
            return cls.enabled_for_course(course_key=course_key, target_datetime=timezone.now())
        else:
            current_config = cls.current(course_key=enrollment.course_id)
            return current_config.enabled_as_of_datetime(target_datetime=enrollment.created)

    @classmethod
    def enabled_for_course(cls, course_key, target_datetime=None):
        """
        Return whether Course Duration Limits are enabled for this course as of a particular date.

        Course Duration Limits are enabled for a course on a date if they are enabled either specifically,
        or via a containing context, such as the org, site, or globally, and if the configuration
        is specified to be ``enabled_as_of`` before ``target_datetime``.

        Only one of enrollment and (user, course_key) may be specified at a time.

        Arguments:
            course_key: The CourseKey of the course being queried.<commit_msg>stop ignoring enrollment creation date if the user is masquerading<commit_after>"""
Course Duration Limit Configuration Models
"""

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone

from course_modes.models import CourseMode
from lms.djangoapps.courseware.masquerade import (
    get_course_masquerade,
    get_masquerade_role,
    is_masquerading_as_specific_student
)
from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel
from openedx.core.djangoapps.config_model_utils.utils import is_in_holdback
from openedx.features.content_type_gating.helpers import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
from openedx.features.course_duration_limits.config import (
    CONTENT_TYPE_GATING_FLAG,
    FEATURE_BASED_ENROLLMENT_GLOBAL_KILL_FLAG,
)
from student.models import CourseEnrollment
from student.role_helpers import has_staff_roles
from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID


@python_2_unicode_compatible
class CourseDurationLimitConfig(StackedConfigurationModel):
    """
    Configuration to manage the Course Duration Limit facility.

    .. no_pii:
    """

    STACKABLE_FIELDS = ('enabled', 'enabled_as_of')

    enabled_as_of = models.DateTimeField(
        default=None,
        null=True,
        verbose_name=_('Enabled As Of'),
        blank=True,
        help_text=_(
            'If the configuration is Enabled, then all enrollments '
            'created after this date and time (UTC) will be affected.'
        )
    )

    @classmethod
    def has_full_access_role_in_masquerade(cls, user, course_key, course_masquerade):
        """
        When masquerading, the course duration limits will never trigger the course to expire, redirecting the user.
        The roles of the masquerade user are still used to determine whether the course duration limit banner displays.
        Another banner also displays if the course is expired for the masquerade user.
        Both banners will appear if the masquerade user does not have any of the following roles:
        Staff, Instructor, Beta Tester, Forum Community TA, Forum Group Moderator, Forum Moderator, Forum Administrator
        """
        masquerade_role = get_masquerade_role(user, course_key)
        verified_mode_id = settings.COURSE_ENROLLMENT_MODES.get(CourseMode.VERIFIED, {}).get('id')
        # Masquerading users can select the the role of a verified users without selecting a specific user
        is_verified = (course_masquerade.user_partition_id == ENROLLMENT_TRACK_PARTITION_ID
                       and course_masquerade.group_id == verified_mode_id)
        # Masquerading users can select the role of staff without selecting a specific user
        is_staff = masquerade_role == 'staff'
        # Masquerading users can select other full access roles for which content type gating is disabled
        is_full_access = (course_masquerade.user_partition_id == CONTENT_GATING_PARTITION_ID
                          and course_masquerade.group_id == CONTENT_TYPE_GATE_GROUP_IDS['full_access'])
        # When masquerading as a specific user, we can check that user's staff roles as we would with a normal user
        is_staff_role = False
        if course_masquerade.user_name:
            is_staff_role = has_staff_roles(user, course_key)

        if is_verified or is_full_access or is_staff or is_staff_role:
            return True
        return False

    @classmethod
    def enabled_for_enrollment(cls, enrollment=None, user=None, course_key=None):
        """
        Return whether Course Duration Limits are enabled for this enrollment.

        Course Duration Limits are enabled for an enrollment if they are enabled for
        the course being enrolled in (either specifically, or via a containing context,
        such as the org, site, or globally), and if the configuration is specified to be
        ``enabled_as_of`` before the enrollment was created.

        Only one of enrollment and (user, course_key) may be specified at a time.

        Arguments:
            enrollment: The enrollment being queried.
            user: The user being queried.
            course_key: The CourseKey of the course being queried.
        """

        if FEATURE_BASED_ENROLLMENT_GLOBAL_KILL_FLAG.is_enabled():
            return False

        if CONTENT_TYPE_GATING_FLAG.is_enabled():
            return True

        if enrollment is not None and (user is not None or course_key is not None):
            raise ValueError('Specify enrollment or user/course_key, but not both')

        if enrollment is None and (user is None or course_key is None):
            raise ValueError('Both user and course_key must be specified if no enrollment is provided')

        if enrollment is None and user is None and course_key is None:
            raise ValueError('At least one of enrollment or user and course_key must be specified')

        if course_key is None:
            course_key = enrollment.course_id

        if enrollment is None:
            enrollment = CourseEnrollment.get_enrollment(user, course_key)

        if user is None and enrollment is not None:
            user = enrollment.user

        if user and user.id:
            course_masquerade = get_course_masquerade(user, course_key)
            if course_masquerade:
                if cls.has_full_access_role_in_masquerade(user, course_key, course_masquerade):
                    return False
            elif has_staff_roles(user, course_key):
                return False

        is_masquerading = get_course_masquerade(user, course_key)
        no_masquerade = is_masquerading is None
        student_masquerade = is_masquerading_as_specific_student(user, course_key)

        # check if user is in holdback
        if (no_masquerade or student_masquerade) and is_in_holdback(user):
            return False

        not_student_masquerade = is_masquerading and not student_masquerade

        # enrollment might be None if the user isn't enrolled. In that case,
        # return enablement as if the user enrolled today
        # When masquerading as a user group rather than a specific learner,
        # course duration limits will be on if they are on for the course.
        # When masquerading as a specific learner, course duration limits
        # will be on if they are currently on for the learner.
        if enrollment is None or not_student_masquerade:
            return cls.enabled_for_course(course_key=course_key, target_datetime=timezone.now())
        else:
            current_config = cls.current(course_key=enrollment.course_id)
            return current_config.enabled_as_of_datetime(target_datetime=enrollment.created)

    @classmethod
    def enabled_for_course(cls, course_key, target_datetime=None):
        """
        Return whether Course Duration Limits are enabled for this course as of a particular date.

        Course Duration Limits are enabled for a course on a date if they are enabled either specifically,
        or via a containing context, such as the org, site, or globally, and if the configuration
        is specified to be ``enabled_as_of`` before ``target_datetime``.

        Only one of enrollment and (user, course_key) may be specified at a time.

        Arguments:
            course_key: The CourseKey of the course being queried.
{'fraction_non_alphanumeric': 0.049589557260126496, 'fraction_numerical': 0.0004037141703673799, 'mean_word_length': 3.109206524744263, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '6195701', 'n_tokens_mistral': 4068, 'n_tokens_neox': 3918, 'n_words': 1396}