diff --git a/common/djangoapps/student/helpers.py b/common/djangoapps/student/helpers.py index f84af9acc3..9b4ce283e4 100644 --- a/common/djangoapps/student/helpers.py +++ b/common/djangoapps/student/helpers.py @@ -35,13 +35,16 @@ from common.djangoapps.student.models import ( username_exists_or_retired ) from common.djangoapps.util.password_policy_validators import normalize_password -from lms.djangoapps.certificates.api import get_certificate_url, has_html_certificates_enabled +from lms.djangoapps.certificates.api import ( + certificates_viewable_for_course, + get_certificate_url, + has_html_certificates_enabled +) from lms.djangoapps.certificates.models import CertificateStatuses, certificate_status_for_student from lms.djangoapps.grades.api import CourseGradeFactory from lms.djangoapps.verify_student.models import VerificationDeadline from lms.djangoapps.verify_student.services import IDVerificationService from lms.djangoapps.verify_student.utils import is_verification_expiring_soon, verification_for_datetime -from openedx.core.djangoapps.certificates.api import certificates_viewable_for_course from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.theming.helpers import get_themes from openedx.core.djangoapps.user_authn.utils import is_safe_login_or_logout_redirect @@ -396,7 +399,7 @@ def create_or_set_user_attribute_created_on_site(user, site): Create or Set UserAttribute indicating the site the user account was created on. User maybe created on 'courses.edx.org', or a white-label site. Due to the very high traffic on this table we now ignore the default site (eg. 'courses.edx.org') and - code which comsumes this attribute should assume a 'created_on_site' which doesn't exist + code which consumes this attribute should assume a 'created_on_site' which doesn't exist belongs to the default site. """ if site and site.id != settings.SITE_ID: @@ -696,7 +699,7 @@ def get_resume_urls_for_enrollments(user, enrollments): enrollments (list): a list of user enrollments Returns: - resume_course_urls (OrderedDict): an OrderdDict of urls + resume_course_urls (OrderedDict): an OrderedDict of urls key: CourseKey value: url to the last completed block if the value is '', then the user has not completed any blocks in the course run diff --git a/lms/djangoapps/certificates/api.py b/lms/djangoapps/certificates/api.py index 85b649ea0a..7a49846a8a 100644 --- a/lms/djangoapps/certificates/api.py +++ b/lms/djangoapps/certificates/api.py @@ -9,6 +9,8 @@ certificates models or any other certificates modules. import logging +from datetime import datetime +from pytz import UTC from django.contrib.auth import get_user_model from django.core.exceptions import ObjectDoesNotExist @@ -44,7 +46,6 @@ from lms.djangoapps.certificates.utils import ( get_certificate_url as _get_certificate_url, has_html_certificates_enabled as _has_html_certificates_enabled ) -from openedx.core.djangoapps.certificates.api import certificates_viewable_for_course from openedx.core.djangoapps.content.course_overviews.models import CourseOverview log = logging.getLogger("edx.certificate") @@ -583,6 +584,30 @@ def get_certificate_footer_context(): return data +def certificates_viewable_for_course(course): + """ + Returns True if certificates are viewable for any student enrolled in the course, False otherwise. + """ + if course.self_paced: + return True + if ( + course.certificates_display_behavior in ('early_with_info', 'early_no_info') + or course.certificates_show_before_end + ): + return True + if ( + course.certificate_available_date + and course.certificate_available_date <= datetime.now(UTC) + ): + return True + if ( + course.certificate_available_date is None + and course.has_ended() + ): + return True + return False + + def get_allowlisted_users(course_key): """ Return the users who are on the allowlist for this course run diff --git a/lms/djangoapps/certificates/apis/v0/views.py b/lms/djangoapps/certificates/apis/v0/views.py index a7192ab724..7bd98e4a9a 100644 --- a/lms/djangoapps/certificates/apis/v0/views.py +++ b/lms/djangoapps/certificates/apis/v0/views.py @@ -15,10 +15,13 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView -from lms.djangoapps.certificates.api import get_certificate_for_user, get_certificates_for_user +from lms.djangoapps.certificates.api import ( + certificates_viewable_for_course, + get_certificate_for_user, + get_certificates_for_user +) from lms.djangoapps.certificates.apis.v0.permissions import IsOwnerOrPublicCertificates from openedx.core.djangoapps.catalog.utils import get_course_run_details -from openedx.core.djangoapps.certificates.api import certificates_viewable_for_course from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.user_api.accounts.api import visible_fields from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser diff --git a/lms/djangoapps/certificates/views/webview.py b/lms/djangoapps/certificates/views/webview.py index bf16ee968c..9572a6dae4 100644 --- a/lms/djangoapps/certificates/views/webview.py +++ b/lms/djangoapps/certificates/views/webview.py @@ -28,6 +28,7 @@ from common.djangoapps.util.views import handle_500 from lms.djangoapps.badges.events.course_complete import get_completion_badge from lms.djangoapps.badges.utils import badges_enabled from lms.djangoapps.certificates.api import ( + certificates_viewable_for_course, get_active_web_certificate, get_certificate_footer_context, get_certificate_header_context, @@ -43,7 +44,7 @@ from lms.djangoapps.certificates.models import ( from lms.djangoapps.certificates.permissions import PREVIEW_CERTIFICATES from lms.djangoapps.certificates.utils import emit_certificate_event, get_certificate_url from openedx.core.djangoapps.catalog.utils import get_course_run_details -from openedx.core.djangoapps.certificates.api import certificates_viewable_for_course, display_date_for_certificate +from openedx.core.djangoapps.certificates.api import display_date_for_certificate from openedx.core.djangoapps.lang_pref.api import get_closest_released_language from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.lib.courses import course_image_url diff --git a/openedx/core/djangoapps/certificates/api.py b/openedx/core/djangoapps/certificates/api.py index fd6ea63a46..8104dac7a3 100644 --- a/openedx/core/djangoapps/certificates/api.py +++ b/openedx/core/djangoapps/certificates/api.py @@ -7,7 +7,8 @@ import logging from datetime import datetime from pytz import UTC -from lms.djangoapps.certificates.models import CertificateStatuses, CertificateWhitelist +from lms.djangoapps.certificates import api as certs_api +from lms.djangoapps.certificates.models import CertificateStatuses from openedx.core.djangoapps.certificates.config import waffle from common.djangoapps.student.models import CourseEnrollment @@ -26,30 +27,6 @@ def _enabled_and_instructor_paced(course): return False -def certificates_viewable_for_course(course): - """ - Returns True if certificates are viewable for any student enrolled in the course, False otherwise. - """ - if course.self_paced: - return True - if ( - course.certificates_display_behavior in ('early_with_info', 'early_no_info') - or course.certificates_show_before_end - ): - return True - if ( - course.certificate_available_date - and course.certificate_available_date <= datetime.now(UTC) - ): - return True - if ( - course.certificate_available_date is None - and course.has_ended() - ): - return True - return False - - def is_certificate_valid(certificate): """ Returns True if the student has a valid, verified certificate for this course, False otherwise. @@ -57,17 +34,20 @@ def is_certificate_valid(certificate): return CourseEnrollment.is_enrolled_as_verified(certificate.user, certificate.course_id) and certificate.is_valid() -def can_show_certificate_message(course, student, course_grade, certificates_enabled_for_course): # lint-amnesty, pylint: disable=missing-function-docstring - is_whitelisted = CertificateWhitelist.objects.filter(user=student, course_id=course.id, whitelist=True).exists() +def can_show_certificate_message(course, student, course_grade, certificates_enabled_for_course): + """ + Returns True if a course certificate message can be shown + """ + is_allowlisted = certs_api.is_on_allowlist(student, course.id) auto_cert_gen_enabled = auto_certificate_generation_enabled() has_active_enrollment = CourseEnrollment.is_enrolled(student, course.id) - certificates_are_viewable = certificates_viewable_for_course(course) + certificates_are_viewable = certs_api.certificates_viewable_for_course(course) return ( (auto_cert_gen_enabled or certificates_enabled_for_course) and has_active_enrollment and certificates_are_viewable and - (course_grade.passed or is_whitelisted) + (course_grade.passed or is_allowlisted) ) diff --git a/openedx/features/learner_profile/views/learner_achievements.py b/openedx/features/learner_profile/views/learner_achievements.py index 9a6d7cae3e..6a7a07e339 100644 --- a/openedx/features/learner_profile/views/learner_achievements.py +++ b/openedx/features/learner_profile/views/learner_achievements.py @@ -7,7 +7,6 @@ from django.template.loader import render_to_string from web_fragments.fragment import Fragment from lms.djangoapps.certificates import api as certificate_api -from openedx.core.djangoapps.certificates.api import certificates_viewable_for_course from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.plugin_api.views import EdxFragmentView @@ -45,7 +44,7 @@ class LearnerAchievementsFragmentView(EdxFragmentView): try: course_overview = CourseOverview.get_from_id(course_key) course_certificate['course'] = course_overview - if certificates_viewable_for_course(course_overview): + if certificate_api.certificates_viewable_for_course(course_overview): # add certificate into passing certificate list only if it's a PDF certificate # or there is an active certificate configuration. if course_certificate['is_pdf_certificate'] or course_overview.has_any_active_web_certificate: