Skip to content

Commit

Permalink
Merge branch 'master' into get_problem_responses-to-drf
Browse files Browse the repository at this point in the history
  • Loading branch information
awais786 authored Oct 9, 2024
2 parents 4abb7ed + 7316111 commit a151e55
Show file tree
Hide file tree
Showing 58 changed files with 605 additions and 558 deletions.
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: 2
build:
os: "ubuntu-22.04"
tools:
python: "3.8"
python: "3.12"

sphinx:
configuration: docs/conf.py
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,3 @@ class CourseSettingsSerializer(serializers.Serializer):
show_min_grade_warning = serializers.BooleanField()
sidebar_html_enabled = serializers.BooleanField()
upgrade_deadline = serializers.DateTimeField(allow_null=True)
use_v2_cert_display_settings = serializers.BooleanField()
2 changes: 0 additions & 2 deletions cms/djangoapps/contentstore/rest_api/v1/views/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ def get(self, request: Request, course_id: str):
"show_min_grade_warning": false,
"sidebar_html_enabled": true,
"upgrade_deadline": null,
"use_v2_cert_display_settings": false
}
```
"""
Expand All @@ -112,7 +111,6 @@ def get(self, request: Request, course_id: str):
'course_display_name_with_default': course_block.display_name_with_default,
'platform_name': settings.PLATFORM_NAME,
'licensing_enabled': settings.FEATURES.get("LICENSING", False),
'use_v2_cert_display_settings': settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS", False),
})

serializer = CourseSettingsSerializer(settings_context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ def test_course_settings_response(self):
"show_min_grade_warning": False,
"upgrade_deadline": None,
"licensing_enabled": False,
"use_v2_cert_display_settings": False,
}

self.assertEqual(response.status_code, status.HTTP_200_OK)
Expand Down
11 changes: 0 additions & 11 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,17 +468,6 @@
# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/26106
'ENABLE_HELP_LINK': True,

# .. toggle_name: FEATURES['ENABLE_V2_CERT_DISPLAY_SETTINGS']
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
# .. toggle_description: Whether to use the reimagined certificates_display_behavior and certificate_available_date
# .. settings. Will eventually become the default.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2021-07-26
# .. toggle_target_removal_date: 2021-10-01
# .. toggle_tickets: 'https://openedx.atlassian.net/browse/MICROBA-1405'
'ENABLE_V2_CERT_DISPLAY_SETTINGS': False,

# .. toggle_name: FEATURES['ENABLE_INTEGRITY_SIGNATURE']
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
Expand Down
2 changes: 1 addition & 1 deletion cms/templates/emails/course_creator_admin_user_pending.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<%! from django.utils.translation import gettext as _ %>
${_("User '{user}' with e-mail {email} has requested {studio_name} course creator privileges on edge.").format(
${_("User '{user}' with e-mail {email} has requested {studio_name} course creator privileges.").format(
user=user_name, email=user_email, studio_name=settings.STUDIO_SHORT_NAME,
)}
${_("To grant or deny this request, use the course creator admin table.")}
Expand Down
68 changes: 27 additions & 41 deletions cms/templates/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
${show_min_grade_warning | n, dump_js_escaped_json},
${can_show_certificate_available_date_field(context_course) | n, dump_js_escaped_json},
"${upgrade_deadline | n, js_escaped_string}",
${settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS") | n, dump_js_escaped_json}
);
});
</%block>
Expand Down Expand Up @@ -251,58 +250,45 @@ <h2 class="title-2">${_('Course Schedule')}</h2>
</li>
</ol>

<%
use_v2_cert_display_settings = settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS", False)
%>
% if can_show_certificate_available_date_field(context_course):
<ol class="list-input">
<li class="field-group field-group-certificate-available" id="certificate-available">
<div class="field date" id="field-certificates-display-behavior">
<label for="certificates-display-behavior">${_("Certificates Display Behavior")}</label>
% if use_v2_cert_display_settings:
<select id="certificates-display-behavior">
<option value="early_no_info">${_("Immediately upon passing")}</option>
<option value="end">${_("End date of course")}</option>
<option value="end_with_date">${_("A date after the course end date")}</option>
</select>
% else:
<input id="certificates-display-behavior" type="text">
% endif
<select id="certificates-display-behavior">
<option value="early_no_info">${_("Immediately upon passing")}</option>
<option value="end">${_("End date of course")}</option>
<option value="end_with_date">${_("A date after the course end date")}</option>
</select>
<span class="tip tip-stacked">${_("Certificates are awarded at the end of a course run")}</span>

% if use_v2_cert_display_settings:
<!-- Collapsible -->
<div class="collapsible">
<div id="certificate-display-behavior-collapsible-trigger" class="collapsible-trigger" role="button" tabindex="0" aria-expanded="false">
<span>
<span class="icon icon-inline fa fa-info-circle" aria-hidden="true"></span>
${_("Read more about this setting")}
</span>
<!-- Collapsible -->
<div class="collapsible">
<div id="certificate-display-behavior-collapsible-trigger" class="collapsible-trigger" role="button" tabindex="0" aria-expanded="false">
<span>
<span class="icon icon-inline fa fa-info-circle" aria-hidden="true"></span>
${_("Read more about this setting")}
</span>
</div>
<div id="certificate-display-behavior-collapsible-content" class="collapsible-content collapsed">
<p>${_("In all configurations of this setting, certificates are generated for learners as soon as they achieve the passing threshold in the course (which can occur before a final assignment based on course design)")}</p>
<div>
<div class="collapsible-description-heading">${_("Immediately upon passing")}</div>
<div class="collapsible-description-description">${_("Learners can access their certificate as soon as they achieve a passing grade above the course grade threshold. Note: learners can achieve a passing grade before encountering all assignments in some course configurations.")}</div>
</div>
<div id="certificate-display-behavior-collapsible-content" class="collapsible-content collapsed">
<p>${_("In all configurations of this setting, certificates are generated for learners as soon as they achieve the passing threshold in the course (which can occur before a final assignment based on course design)")}</p>
<div>
<div class="collapsible-description-heading">${_("Immediately upon passing")}</div>
<div class="collapsible-description-description">${_("Learners can access their certificate as soon as they achieve a passing grade above the course grade threshold. Note: learners can achieve a passing grade before encountering all assignments in some course configurations.")}</div>
</div>
<div>
<div class="collapsible-description-heading">${_("On course end date")}</div>
<div class="collapsible-description-description">${_("Learners with passing grades can access their certificate once the end date of the course has elapsed.")}</div>
</div>
<div>
<div class="collapsible-description-heading">${_("A date after the course end date")}</div>
<div class="collapsible-description-description">${_("Learners with passing grades can access their certificate after the date that you set has elapsed.")}</div>
</div>
<div>
<div class="collapsible-description-heading">${_("On course end date")}</div>
<div class="collapsible-description-description">${_("Learners with passing grades can access their certificate once the end date of the course has elapsed.")}</div>
</div>
<div>
<div class="collapsible-description-heading">${_("A date after the course end date")}</div>
<div class="collapsible-description-description">${_("Learners with passing grades can access their certificate after the date that you set has elapsed.")}</div>
</div>
</div>
% endif
</div>
</div>

% if use_v2_cert_display_settings:
<div class="field date hidden" id="field-certificate-available-date" >
% else:
<div class="field date" id="field-certificate-available-date" >
% endif
<div class="field date hidden" id="field-certificate-available-date" >
<label for="certificate-available-date">${_("Certificates Available Date")}</label>
<input type="text" class="certificate-available-date date start datepicker" id="certificate-available-date" placeholder="MM/DD/YYYY" autocomplete="off" />
<span class="icon icon-inline fa fa-calendar-check-o datepicker-icon" aria-hidden="true"></span>
Expand Down
21 changes: 7 additions & 14 deletions common/djangoapps/student/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,21 +646,14 @@ def _is_certificate_earned_but_not_available(course_overview, status):
(bool): True if the user earned the certificate but it's hidden due to display behavior, else False
"""
if settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS"):
return (
not certificates_viewable_for_course(course_overview)
and CertificateStatuses.is_passing_status(status)
and course_overview.certificates_display_behavior in (
CertificatesDisplayBehaviors.END_WITH_DATE,
CertificatesDisplayBehaviors.END
)
)
else:
return (
not certificates_viewable_for_course(course_overview) and
CertificateStatuses.is_passing_status(status) and
course_overview.certificate_available_date
return (
not certificates_viewable_for_course(course_overview)
and CertificateStatuses.is_passing_status(status)
and course_overview.certificates_display_behavior in (
CertificatesDisplayBehaviors.END_WITH_DATE,
CertificatesDisplayBehaviors.END
)
)


def process_survey_link(survey_link, user):
Expand Down
11 changes: 11 additions & 0 deletions common/djangoapps/third_party_auth/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Third Party Auth
----------------

This djangoapp provides the views and workflows for authenticating into edx-platform with third-party applications, including both OAuth and SAML workflows.

We make use of the `social-auth-app-django`_ as our backend library for this djangoapp.

To enable this feature, check out the `third party authentication documentation`.

.. _social-auth-app-django: https://github.com/python-social-auth/social-app-django
.. _third party authentication documentation: https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/configuration/tpa/index.html
22 changes: 16 additions & 6 deletions common/djangoapps/third_party_auth/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ class SAMLProviderConfigForm(forms.ModelForm):
class SAMLProviderConfigAdmin(KeyedConfigurationModelAdmin):
""" Django Admin class for SAMLProviderConfig """
form = SAMLProviderConfigForm
search_fields = ['display_name']

def get_queryset(self, request):
"""
Filter the queryset to exclude the archived records.
Filter the queryset to exclude the archived records unless it's the /change/ view.
"""
queryset = super().get_queryset(request).exclude(archived=True)
return queryset
if request.path.endswith('/change/'):
return self.model.objects.all()
return super().get_queryset(request).exclude(archived=True)

def archive_provider_configuration(self, request, queryset):
"""
Expand Down Expand Up @@ -99,7 +101,15 @@ def name_with_update_link(self, instance):
Record name with link for the change view.
"""
if not instance.is_active:
return instance.name
update_url = reverse(
f'admin:{self.model._meta.app_label}_{self.model._meta.model_name}_change',
args=[instance.pk]
)
return format_html(
'<a href="{}" style="color: #999999;">{}</a>',
update_url,
f'{instance.name}'
)

update_url = reverse(f'admin:{self.model._meta.app_label}_{self.model._meta.model_name}_add')
update_url += f'?source={instance.pk}'
Expand Down Expand Up @@ -167,11 +177,11 @@ def upload_csv(self, request, slug):
# Always redirect back to the SAMLProviderConfig listing page
return HttpResponseRedirect(reverse('admin:third_party_auth_samlproviderconfig_changelist'))

def change_view(self, request, object_slug, form_url='', extra_context=None):
def change_view(self, request, object_id, form_url='', extra_context=None):
""" Extend the change view to include CSV upload. """
extra_context = extra_context or {}
extra_context['show_csv_upload'] = True
return super().change_view(request, object_slug, form_url, extra_context)
return super().change_view(request, object_id, form_url, extra_context)

def csv_uuid_update_button(self, obj):
""" Add CSV upload button to the form. """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ def test_verification_signal(self):
"""
Verification signal is sent upon approval.
"""
with mock.patch('openedx_events.learning.signals.IDV_ATTEMPT_APPROVED.send_event') as mock_signal:
with mock.patch('openedx.core.djangoapps.signals.signals.LEARNER_SSO_VERIFIED.send_robust') as mock_signal:
# Begin the pipeline.
pipeline.set_id_verification_status(
auth_entry=pipeline.AUTH_ENTRY_LOGIN,
Expand Down
Binary file modified common/static/data/geoip/GeoLite2-Country.mmdb
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
<!-- The default stylesheet will set the body min-height to 100% (a common strategy to allow for background
images to fill the viewport), but this has the undesireable side-effect of causing an infinite loop via the onResize
event listeners below, in certain situations. Resetting it to the default "auto" skirts the problem.-->
<body style="min-height: auto">
<body style="min-height: auto; background-color: white;">
<!-- fragment body -->
{{ fragment.body_html | safe }}
<!-- fragment foot -->
Expand Down
1 change: 1 addition & 0 deletions lms/djangoapps/bulk_email/message_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ class BulkEmail(BaseMessageType):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.options['from_address'] = kwargs['context']['from_address']
self.options['transactional'] = True
15 changes: 4 additions & 11 deletions lms/djangoapps/certificates/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import logging
from datetime import datetime

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
Expand Down Expand Up @@ -286,12 +285,9 @@ def certificate_downloadable_status(student, course_key):

course_overview = get_course_overview_or_none(course_key)

if settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS"):
display_behavior_is_valid = (
course_overview.certificates_display_behavior == CertificatesDisplayBehaviors.END_WITH_DATE
)
else:
display_behavior_is_valid = True
display_behavior_is_valid = (
course_overview.certificates_display_behavior == CertificatesDisplayBehaviors.END_WITH_DATE
)

if (
not certificates_viewable_for_course(course_overview)
Expand Down Expand Up @@ -837,10 +833,7 @@ def can_show_certificate_message(course, student, course_grade, certificates_ena

def _course_uses_available_date(course):
"""Returns if the course has an certificate_available_date set and that it should be used"""
if settings.FEATURES.get("ENABLE_V2_CERT_DISPLAY_SETTINGS"):
display_behavior_is_valid = course.certificates_display_behavior == CertificatesDisplayBehaviors.END_WITH_DATE
else:
display_behavior_is_valid = True
display_behavior_is_valid = course.certificates_display_behavior == CertificatesDisplayBehaviors.END_WITH_DATE

return (
can_show_certificate_available_date_field(course)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ workspace {

grades_app -> signal_handlers "Emits COURSE_GRADE_NOW_PASSED signal"
verify_student_app -> signal_handlers "Emits IDV_ATTEMPT_APPROVED signal"
verify_student_app -> signal_handlers "Emits LEARNER_SSO_VERIFIED signal"
verify_student_app -> signal_handlers "Emits PHOTO_VERIFICATION_APPROVED signal"
student_app -> signal_handlers "Emits ENROLLMENT_TRACK_UPDATED signal"
allowlist -> signal_handlers "Emits APPEND_CERTIFICATE_ALLOWLIST signal"
signal_handlers -> generation_handler "Invokes generate_allowlist_certificate()"
Expand Down
29 changes: 23 additions & 6 deletions lms/djangoapps/certificates/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
from openedx.core.djangoapps.signals.signals import (
COURSE_GRADE_NOW_FAILED,
COURSE_GRADE_NOW_PASSED,
LEARNER_SSO_VERIFIED,
PHOTO_VERIFICATION_APPROVED,
)
from openedx_events.learning.signals import EXAM_ATTEMPT_REJECTED, IDV_ATTEMPT_APPROVED

Expand Down Expand Up @@ -117,17 +119,13 @@ def _listen_for_failing_grade(sender, user, course_id, grade, **kwargs): # pyli
log.info(f'Certificate marked not passing for {user.id} : {course_id} via failing grade')


@receiver(IDV_ATTEMPT_APPROVED, dispatch_uid="learner_track_changed")
def _listen_for_id_verification_status_changed(sender, signal, **kwargs): # pylint: disable=unused-argument
def _handle_id_verification_approved(user):
"""
Listen for a signal indicating that the user's id verification status has changed.
Generate a certificate for the user if they are now verified
"""
if not auto_certificate_generation_enabled():
return

event_data = kwargs.get('idv_attempt')
user = User.objects.get(id=event_data.user.id)

user_enrollments = CourseEnrollment.enrollments_for_user(user=user)
expected_verification_status = IDVerificationService.user_status(user)
expected_verification_status = expected_verification_status['status']
Expand All @@ -145,6 +143,25 @@ def _listen_for_id_verification_status_changed(sender, signal, **kwargs): # pyl
)


@receiver(LEARNER_SSO_VERIFIED, dispatch_uid="sso_learner_verified")
@receiver(PHOTO_VERIFICATION_APPROVED, dispatch_uid="photo_verification_approved")
def _listen_for_sso_verification_approved(sender, user, **kwargs): # pylint: disable=unused-argument
"""
Listen for a signal on SSOVerification indicating that the user has been verified.
"""
_handle_id_verification_approved(user)


@receiver(IDV_ATTEMPT_APPROVED, dispatch_uid="openedx_idv_attempt_approved")
def _listen_for_id_verification_approved_event(sender, signal, **kwargs): # pylint: disable=unused-argument
"""
Listen for an openedx event indicating that the user's id verification status has changed.
"""
event_data = kwargs.get('idv_attempt')
user = User.objects.get(id=event_data.user.id)
_handle_id_verification_approved(user)


@receiver(ENROLLMENT_TRACK_UPDATED)
def _listen_for_enrollment_mode_change(sender, user, course_key, mode, **kwargs): # pylint: disable=unused-argument
"""
Expand Down
Loading

0 comments on commit a151e55

Please sign in to comment.