Merge branch 'master' into BOM-2374-entitlements

This commit is contained in:
Awais Qureshi
2021-03-02 16:35:30 +05:00
committed by GitHub
306 changed files with 3000 additions and 2569 deletions

View File

@@ -20,7 +20,13 @@ jobs:
python-version: ["3.8"]
steps:
- name: setup target branch
run: echo "target_branch=$(if ['${{ github.event.inputs.branch }}' = '']; then echo 'master'; else echo '${{ github.event.inputs.branch }}'; fi)" >> $GITHUB_ENV
- uses: actions/checkout@v1
with:
ref: ${{ env.target_branch }}
- name: setup python
uses: actions/setup-python@v2
with:
@@ -34,9 +40,6 @@ jobs:
cd $GITHUB_WORKSPACE
make upgrade
- name: setup target branch
run: echo "target_branch=$(if ['${{ github.event.inputs.branch }}' = '']; then echo 'master'; else echo '${{ github.event.inputs.branch }}'; fi)" >> $GITHUB_ENV
- name: setup testeng-ci
run: |
git clone https://github.com/edx/testeng-ci.git

View File

@@ -1,6 +1,6 @@
############################
########################
Contributing to Open edX
############################
########################
Contributions to Open edX are very welcome, and strongly encouraged! We've
put together `some documentation that describes our contribution process`_,
@@ -99,19 +99,24 @@ to ask clarifying questions on the ticket as needed, too, if anything is unclear
Step 1: Sign a Contribution Agreement
=====================================
Before edX can accept any code contributions from you, you'll need to sign
the `individual contributor agreement`_ and send it in. This confirms
that you have the authority to contribute the code in the pull request and
ensures that edX can relicense it.
Before edX can accept any code contributions from you, you'll need to sign the
`Individual Contributor Agreement`_. This confirms that you have the authority
to contribute the code in the pull request and ensures that edX can re-license
it.
You should print out the agreement and sign it. Then scan (or photograph) the
signed agreement and email it to the email address indicated on the agreement.
Alternatively, you're also free to physically mail the agreement to the street
address on the agreement. Once we have your agreement in hand, we can begin
reviewing and merging your work.
.. _Individual Contributor Agreement: https://openedx.org/cla
If you will be contributing code on behalf of your employer or another
institution you are affiliated with, please reach out by email to legal@edx.org
to request the Entity Contributor Agreement.
Once we have received and processed your agreement, we will reach out to you by
email to confirm. (Please make sure to indicate your email correctly in the
agreement.) After that we can begin reviewing and merging your work.
Step 2: Fork, Commit, and Pull Request
======================================
GitHub has some great documentation on `how to fork a git repository`_. Once
you've done that, make your changes and `send us a pull request`_! Be sure to
include a detailed description for your pull request, so that a community
@@ -214,5 +219,3 @@ Expectations You Have of Us
3. Once we have determined through visual review that your code is not
malicious, we will run a Jenkins build on your branch.
.. _individual contributor agreement: https://open.edx.org/wp-content/uploads/2019/01/individual-contributor-agreement.pdf

View File

@@ -67,7 +67,9 @@ pre-requirements: ## install Python requirements for running pip-tools
pip install -qr requirements/edx/pip-tools.txt
requirements: pre-requirements ## install development environment requirements
pip-sync -q requirements/edx/development.txt requirements/edx/private.*
# The "$(wildcard..)" is to include private.txt if it exists, and make no mention
# of it if it does not. Shell wildcarding can't do that with default options.
pip-sync -q requirements/edx/development.txt $(wildcard requirements/edx/private.txt)
shell: ## launch a bash shell in a Docker container with all edx-platform dependencies installed
docker run -it -e "NO_PYTHON_UNINSTALL=1" -e "PIP_INDEX_URL=https://pypi.python.org/simple" -e TERM \
@@ -130,4 +132,3 @@ docker_push: docker_tag docker_auth ## push to docker hub
docker push "openedx/edx-platform:${GITHUB_SHA}-newrelic"
docker push 'openedx/edx-platform:latest-devstack'
docker push "openedx/edx-platform:${GITHUB_SHA}-devstack"

View File

@@ -86,5 +86,5 @@ Reporting Security Issues
Please do not report security issues in public. Please email
security@edx.org.
.. _individual contributor agreement: https://openedx.org/wp-content/uploads/2019/01/individual-contributor-agreement.pdf
.. _individual contributor agreement: https://openedx.org/cla
.. _CONTRIBUTING: https://github.com/edx/edx-platform/blob/master/CONTRIBUTING.rst

View File

@@ -45,7 +45,7 @@ class CourseQualityViewTest(BaseCourseViewTest):
'number_with_highlights': 0,
'total_visible': 1,
'total_number': 1,
'highlights_enabled': False,
'highlights_enabled': True,
'highlights_active_for_course': False,
},
'subsections': {

View File

@@ -9,7 +9,6 @@ from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from scipy import stats
from cms.djangoapps.contentstore.views.item import highlights_setting
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
from openedx.core.lib.cache_utils import request_cached
from openedx.core.lib.graph_traversals import traverse_pre_order
@@ -149,7 +148,7 @@ class CourseQualityView(DeveloperErrorViewMixin, GenericAPIView):
total_visible=len(visible_sections),
number_with_highlights=len(sections_with_highlights),
highlights_active_for_course=course.highlights_enabled_for_messaging,
highlights_enabled=highlights_setting.is_enabled(),
highlights_enabled=True, # used to be controlled by a waffle switch, now just always enabled
)
def _subsections_quality(self, course, request): # lint-amnesty, pylint: disable=missing-function-docstring

View File

@@ -1,7 +1,6 @@
"""Views for items (modules)."""
import hashlib # lint-amnesty, pylint: disable=unused-import
import logging
from collections import OrderedDict
from datetime import datetime
@@ -21,7 +20,6 @@ from edx_proctoring.api import (
get_exam_configuration_dashboard_url
)
from edx_proctoring.exceptions import ProctoredExamNotFoundException
from edx_toggles.toggles import LegacyWaffleSwitch
from help_tokens.core import HelpUrlExpert
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import LibraryUsageLocator
@@ -36,7 +34,6 @@ from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from cms.djangoapps.xblock_config.models import CourseEditLTIFieldsEnabledFlag
from cms.lib.xblock.authoring_mixin import VISIBILITY_VIEW
from common.djangoapps.edxmako.shortcuts import render_to_string
from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
from openedx.core.lib.gating import api as gating_api
from openedx.core.lib.xblock_utils import hash_resource, request_token, wrap_xblock, wrap_xblock_aside
from openedx.core.djangoapps.bookmarks import api as bookmarks_api
@@ -92,9 +89,6 @@ NEVER = lambda x: False
ALWAYS = lambda x: True
highlights_setting = LegacyWaffleSwitch('dynamic_pacing', 'studio_course_update', __name__)
def _filter_entrance_exam_grader(graders):
"""
If the entrance exams feature is enabled we need to hide away the grader from
@@ -1262,8 +1256,8 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
'highlights_enabled_for_messaging': course.highlights_enabled_for_messaging,
})
xblock_info.update({
'highlights_enabled': highlights_setting.is_enabled(),
'highlights_preview_only': not COURSE_UPDATE_WAFFLE_FLAG.is_enabled(course.id),
'highlights_enabled': True, # used to be controlled by a waffle switch, now just always enabled
'highlights_preview_only': False, # used to be controlled by a waffle flag, now just always disabled
'highlights_doc_url': HelpUrlExpert.the_one().url_for_token('content_highlights'),
})

View File

@@ -13,7 +13,6 @@ from django.test import TestCase
from django.test.client import RequestFactory
from django.urls import reverse
from edx_proctoring.exceptions import ProctoredExamNotFoundException
from edx_toggles.toggles.testutils import override_waffle_switch
from mock import Mock, PropertyMock, patch
from opaque_keys import InvalidKeyError
from opaque_keys.edx.asides import AsideUsageKeyV2
@@ -66,7 +65,6 @@ from ..item import (
_xblock_type_and_display_name,
add_container_page_publishing_info,
create_xblock_info,
highlights_setting
)
@@ -2696,12 +2694,8 @@ class TestXBlockInfo(ItemTest):
def test_highlights_enabled(self):
self.course.highlights_enabled_for_messaging = True
self.store.update_item(self.course, None)
chapter = self.store.get_item(self.chapter.location)
with override_waffle_switch(highlights_setting, active=True):
chapter_xblock_info = create_xblock_info(chapter)
course_xblock_info = create_xblock_info(self.course)
self.assertTrue(chapter_xblock_info['highlights_enabled'])
self.assertTrue(course_xblock_info['highlights_enabled_for_messaging'])
course_xblock_info = create_xblock_info(self.course)
self.assertTrue(course_xblock_info['highlights_enabled_for_messaging'])
def validate_course_xblock_info(self, xblock_info, has_child_info=True, course_outline=False):
"""
@@ -2731,7 +2725,7 @@ class TestXBlockInfo(ItemTest):
self.assertEqual(xblock_info['due'], None)
self.assertEqual(xblock_info['format'], None)
self.assertEqual(xblock_info['highlights'], self.chapter.highlights)
self.assertFalse(xblock_info['highlights_enabled'])
self.assertTrue(xblock_info['highlights_enabled'])
# Finally, validate the entire response for consistency
self.validate_xblock_info_consistency(xblock_info, has_child_info=has_child_info)

View File

@@ -2023,6 +2023,11 @@ INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT = {}
BASE_COOKIE_DOMAIN = 'localhost'
# This limits the type of roles that are submittable via the `student` app's manual enrollment
# audit API. While this isn't used in CMS, it is used via Enterprise which is installed in
# the CMS. Without this, we get errors.
MANUAL_ENROLLMENT_ROLE_CHOICES = ['Learner', 'Support', 'Partner']
############## Settings for the Discovery App ######################
COURSE_CATALOG_URL_ROOT = 'http://localhost:8008'
@@ -2335,6 +2340,9 @@ DISABLE_DEPRECATED_SIGNUP_URL = False
LOGISTRATION_RATELIMIT_RATE = '100/5m'
LOGISTRATION_PER_EMAIL_RATELIMIT_RATE = '30/5m'
LOGISTRATION_API_RATELIMIT = '20/m'
RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT = '30/7d'
RESET_PASSWORD_API_RATELIMIT = '30/7d'
##### REGISTRATION RATE LIMIT SETTINGS #####
REGISTRATION_VALIDATION_RATELIMIT = '30/7d'

View File

@@ -331,7 +331,8 @@ LOGISTRATION_PER_EMAIL_RATELIMIT_RATE = '6/5m'
LOGISTRATION_API_RATELIMIT = '5/m'
REGISTRATION_VALIDATION_RATELIMIT = '5/minute'
RESET_PASSWORD_TOKEN_VALIDATE_API_RATELIMIT = '2/m'
RESET_PASSWORD_API_RATELIMIT = '2/m'
# Don't tolerate deprecated edx-platform import usage in tests.
ERROR_ON_DEPRECATED_EDX_PLATFORM_IMPORTS = True

View File

@@ -573,8 +573,7 @@ describe('CourseOutlinePage', function() {
});
describe('Content Highlights', function() {
var createCourse, createCourseWithHighlights, createCourseWithHighlightsDisabled,
clickSaveOnModal, clickCancelOnModal;
let createCourse, createCourseWithHighlights, clickSaveOnModal, clickCancelOnModal;
beforeEach(function() {
setSelfPaced();
@@ -592,11 +591,6 @@ describe('CourseOutlinePage', function() {
createCourse({highlights: highlights});
};
createCourseWithHighlightsDisabled = function() {
var highlightsDisabled = {highlights_enabled: false};
createCourse(highlightsDisabled, highlightsDisabled);
};
clickSaveOnModal = function() {
$('.wrapper-modal-window .action-save').click();
};
@@ -643,11 +637,6 @@ describe('CourseOutlinePage', function() {
$('button.status-highlights-enabled-value').click();
};
it('does not display settings when disabled', function() {
createCourseWithHighlightsDisabled();
expect(highlightsSetting()).not.toExist();
});
it('displays settings when enabled', function() {
createCourseWithHighlights([]);
expect(highlightsSetting()).toExist();
@@ -778,11 +767,6 @@ describe('CourseOutlinePage', function() {
expectHighlightsToBe(updatedHighlights);
};
it('does not display link when disabled', function() {
createCourseWithHighlightsDisabled();
expect(highlightsLink()).not.toExist();
});
it('displays link when no highlights exist', function() {
createCourseWithHighlights([]);
expectHighlightLinkNumberToBe(0);

View File

@@ -212,7 +212,7 @@ if (is_proctored_exam) {
</p>
</div>
<% } %>
<% if (xblockInfo.get('highlights_enabled') && xblockInfo.isChapter()) { %>
<% if (xblockInfo.isChapter()) { %>
<div class="block-highlights">
<% var number_of_highlights = (xblockInfo.get('highlights') || []).length; %>
<button class="block-highlights-value highlights-button action-button">

View File

@@ -2,12 +2,6 @@
<h3 class="modal-section-title" id="highlights_label"><%- gettext('Section Highlights') %></h3>
<div class="highlights-info">
<% if (highlights_preview_only) { %>
<p><b>
<%- gettext('This feature is currently in testing. Course teams can enter highlights, but learners will not receive email messages.') %>
</b></p>
<% } %>
<%- gettext('Enter 3-5 highlights to include in the email message that learners receive for this section (250 character limit).') %>
<p>

View File

@@ -5,8 +5,6 @@ Add and create new modes for running courses on this particular LMS
from collections import defaultdict, namedtuple
from datetime import timedelta
import inspect # lint-amnesty, pylint: disable=unused-import
import logging
import six
from config_models.models import ConfigurationModel
@@ -21,7 +19,6 @@ from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from edx_django_utils.cache import RequestCache
from opaque_keys.edx.django.models import CourseKeyField
from opaque_keys.edx.keys import CourseKey # lint-amnesty, pylint: disable=unused-import
from simple_history.models import HistoricalRecords
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview

View File

@@ -21,7 +21,7 @@ from django.utils.translation import get_language, to_locale
from django.utils.translation import ugettext as _
from django.views.generic.base import View
from edx_django_utils.monitoring.utils import increment
from ipware.ip import get_ip
from ipware.ip import get_client_ip
from opaque_keys.edx.keys import CourseKey
from six import text_type
@@ -90,7 +90,7 @@ class ChooseModeView(View):
embargo_redirect = embargo_api.redirect_if_blocked(
course_key,
user=request.user,
ip_address=get_ip(request),
ip_address=get_client_ip(request)[0],
url=request.path
)
if embargo_redirect:

View File

@@ -17,7 +17,7 @@ import logging
import six
from django.conf import settings
from django.http import Http404, HttpResponse # lint-amnesty, pylint: disable=unused-import
from django.http import HttpResponse # lint-amnesty, pylint: disable=unused-import
from django.template import engines
from django.urls import reverse, NoReverseMatch
from six.moves.urllib.parse import urljoin
@@ -26,7 +26,6 @@ from django.core.exceptions import ValidationError
from edx_django_utils.monitoring import set_custom_attribute
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.theming.helpers import is_request_in_themed_site # lint-amnesty, pylint: disable=unused-import
from xmodule.util.xmodule_django import get_current_request_hostname
from . import Engines

View File

@@ -22,7 +22,6 @@ from common.djangoapps.student.tests.factories import TEST_PASSWORD, CourseEnrol
from lms.djangoapps.courseware.models import DynamicUpgradeDeadlineConfiguration
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.models import UserOrgTag
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@@ -485,7 +484,7 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
course = CourseFactory.create(self_paced=True)
course_uuid = uuid.uuid4()
course_mode = CourseModeFactory(
CourseModeFactory(
course_id=course.id,
mode_slug=CourseMode.VERIFIED,
# This must be in the future to ensure it is returned by downstream code.
@@ -499,11 +498,9 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
# Add an audit course enrollment for user.
enrollment = CourseEnrollment.enroll(self.user, course.id, mode=CourseMode.AUDIT)
# Set an upgrade schedule so that dynamic upgrade deadlines are used
ScheduleFactory.create(
enrollment=enrollment,
upgrade_deadline=course_mode.expiration_datetime + timedelta(days=-3)
)
# Set an expired dynamic upgrade deadline
enrollment.schedule.upgrade_deadline = now() + timedelta(days=-2)
enrollment.schedule.save()
# The upgrade should complete and ignore the deadline
response = self.client.post(

View File

@@ -45,6 +45,11 @@ TRANSITION_STATES = (
DEFAULT_TRANSITION_STATE,
)
MANUAL_ENROLLMENT_ROLE_CHOICES = configuration_helpers.get_value(
'MANUAL_ENROLLMENT_ROLE_CHOICES',
settings.MANUAL_ENROLLMENT_ROLE_CHOICES
)
COURSE_DASHBOARD_PLUGIN_VIEW_NAME = "course_dashboard"
@@ -54,6 +59,7 @@ def create_manual_enrollment_audit(
transition_state,
reason,
course_run_key=None,
role=None
):
"""
Creates an audit item for a manual enrollment.
@@ -63,10 +69,13 @@ def create_manual_enrollment_audit(
transition_state: <str> state of enrollment transition state from _TRANSITIONS_STATES
reason: <str> Reason why user was manually enrolled
course_run_key: <str> Used to link the audit enrollment to the actual enrollment
role: <str> role of the enrolled user from MANUAL_ENROLLMENT_ROLE_CHOICES
Note: We purposefully *exclude* passing items like CourseEnrollment objects to prevent callers from needed to
know about model level code.
"""
if role and role not in MANUAL_ENROLLMENT_ROLE_CHOICES:
raise ValueError("Role `{}` not in allowed roles: `{}".format(role, MANUAL_ENROLLMENT_ROLE_CHOICES))
if transition_state not in TRANSITION_STATES:
raise ValueError("State `{}` not in allow states: `{}`".format(transition_state, TRANSITION_STATES))
@@ -86,7 +95,8 @@ def create_manual_enrollment_audit(
user_email,
transition_state,
reason,
enrollment
enrollment,
role
)

View File

@@ -2,17 +2,10 @@
Utility functions for validating forms
"""
import re # lint-amnesty, pylint: disable=unused-import
from importlib import import_module # lint-amnesty, pylint: disable=unused-import
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user, unused-import
from django.contrib.auth.tokens import default_token_generator
from django.core.exceptions import ValidationError # lint-amnesty, pylint: disable=unused-import
from django.urls import reverse
from django.utils.http import int_to_base36
from django.utils.translation import ugettext_lazy as _ # lint-amnesty, pylint: disable=unused-import
from edx_ace import ace
from edx_ace.recipient import Recipient
@@ -20,12 +13,9 @@ from openedx.core.djangoapps.ace_common.template_context import get_base_templat
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.theming.helpers import get_current_site
from openedx.core.djangoapps.user_api import accounts as accounts_settings # lint-amnesty, pylint: disable=unused-import
from openedx.core.djangoapps.user_api.accounts.utils import is_secondary_email_feature_enabled # lint-amnesty, pylint: disable=unused-import
from openedx.core.djangoapps.user_authn.toggles import should_redirect_to_authn_microfrontend
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
from common.djangoapps.student.message_types import AccountRecovery as AccountRecoveryMessage
from common.djangoapps.student.models import CourseEnrollmentAllowed, email_exists_or_retired # lint-amnesty, pylint: disable=unused-import
def send_account_recovery_email_for_user(user, request, email=None):

View File

@@ -4,7 +4,6 @@ Un-enroll Bulk users course wide as well as specified in csv
import logging
import unicodecsv
from django.core.exceptions import ObjectDoesNotExist # lint-amnesty, pylint: disable=unused-import
from django.core.management.base import BaseCommand
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey

View File

@@ -18,6 +18,7 @@ from edx_ace import ace
from edx_ace.recipient import Recipient
from common.djangoapps.student.models import AccountRecoveryConfiguration
from openedx.core.djangoapps.user_authn.toggles import should_redirect_to_authn_microfrontend
from openedx.core.djangoapps.ace_common.template_context import get_base_template_context
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
@@ -104,12 +105,16 @@ class Command(BaseCommand):
"""
message_context = get_base_template_context(site)
email = user.email
if should_redirect_to_authn_microfrontend():
site_url = settings.AUTHN_MICROFRONTEND_URL
else:
site_url = configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME)
message_context.update({
'email': email,
'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
'reset_link': '{protocol}://{site}{link}?track=pwreset'.format(
'reset_link': '{protocol}://{site_url}{link}?track=pwreset'.format(
protocol='http',
site=configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME),
site_url=site_url,
link=reverse('password_reset_confirm', kwargs={
'uidb36': int_to_base36(user.id),
'token': default_token_generator.make_token(user),

View File

@@ -7,17 +7,24 @@ import pytest
import six
from django.core import mail
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.management import call_command, CommandError
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, RequestFactory
from django.test import TestCase, RequestFactory, override_settings
from edx_toggles.toggles.testutils import override_waffle_flag
from testfixtures import LogCapture
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.student.models import AccountRecoveryConfiguration
from openedx.core.djangolib.testing.utils import skip_unless_lms
from openedx.core.djangoapps.user_authn.toggles import REDIRECT_TO_AUTHN_MICROFRONTEND
LOGGER_NAME = 'common.djangoapps.student.management.commands.recover_account'
FEATURES_WITH_AUTHN_MFE_ENABLED = settings.FEATURES.copy()
FEATURES_WITH_AUTHN_MFE_ENABLED['ENABLE_AUTHN_MICROFRONTEND'] = True
class RecoverAccountTests(TestCase):
@@ -65,6 +72,24 @@ class RecoverAccountTests(TestCase):
# try to login with previous password
assert not self.client.login(username=self.user.username, password='password')
@override_settings(FEATURES=FEATURES_WITH_AUTHN_MFE_ENABLED)
@override_waffle_flag(REDIRECT_TO_AUTHN_MICROFRONTEND, active=True)
@skip_unless_lms
def test_authn_mfe_url_in_reset_link(self):
"""
send password reset link to learner with authn mfe.
:return:
"""
with NamedTemporaryFile() as csv:
csv = self._write_test_csv(csv, lines=['amy,amy@edx.com,amy@newemail.com\n'])
call_command("recover_account", "--csv_file_path={}".format(csv.name))
assert len(mail.outbox) == 1
authn_mfe_url = re.findall(settings.AUTHN_MICROFRONTEND_URL, mail.outbox[0].body)[0]
self.assertEqual(authn_mfe_url, settings.AUTHN_MICROFRONTEND_URL)
def test_file_not_found_error(self):
"""
Test command error raised when csv path is invalid

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import django.db.models.deletion
import django.utils.timezone
import django_countries.fields
@@ -75,7 +72,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('recent_enrollment_time_delta', models.PositiveIntegerField(default=0, help_text=u"The number of seconds in which a new enrollment is considered 'recent'. Used to display notifications.")),
('recent_enrollment_time_delta', models.PositiveIntegerField(default=0, help_text="The number of seconds in which a new enrollment is considered 'recent'. Used to display notifications.")),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
options={
@@ -141,9 +138,9 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('company_identifier', models.TextField(help_text=u'The company identifier for the LinkedIn Add-to-Profile button e.g 0_0dPSPyS070e0HsE9HNz_13_d11_')),
('dashboard_tracking_code', models.TextField(default=u'', blank=True)),
('trk_partner_name', models.CharField(default=u'', help_text=u"Short identifier for the LinkedIn partner used in the tracking code. (Example: 'edx') If no value is provided, tracking codes will not be sent to LinkedIn.", max_length=10, blank=True)),
('company_identifier', models.TextField(help_text='The company identifier for the LinkedIn Add-to-Profile button e.g 0_0dPSPyS070e0HsE9HNz_13_d11_')),
('dashboard_tracking_code', models.TextField(default='', blank=True)),
('trk_partner_name', models.CharField(default='', help_text="Short identifier for the LinkedIn partner used in the tracking code. (Example: 'edx') If no value is provided, tracking codes will not be sent to LinkedIn.", max_length=10, blank=True)),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
options={
@@ -166,7 +163,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('enrolled_email', models.CharField(max_length=255, db_index=True)),
('time_stamp', models.DateTimeField(auto_now_add=True, null=True)),
('state_transition', models.CharField(max_length=255, choices=[(u'from unenrolled to allowed to enroll', u'from unenrolled to allowed to enroll'), (u'from allowed to enroll to enrolled', u'from allowed to enroll to enrolled'), (u'from enrolled to enrolled', u'from enrolled to enrolled'), (u'from enrolled to unenrolled', u'from enrolled to unenrolled'), (u'from unenrolled to enrolled', u'from unenrolled to enrolled'), (u'from allowed to enroll to enrolled', u'from allowed to enroll to enrolled'), (u'from unenrolled to unenrolled', u'from unenrolled to unenrolled'), (u'N/A', u'N/A')])),
('state_transition', models.CharField(max_length=255, choices=[('from unenrolled to allowed to enroll', 'from unenrolled to allowed to enroll'), ('from allowed to enroll to enrolled', 'from allowed to enroll to enrolled'), ('from enrolled to enrolled', 'from enrolled to enrolled'), ('from enrolled to unenrolled', 'from enrolled to unenrolled'), ('from unenrolled to enrolled', 'from unenrolled to enrolled'), ('from allowed to enroll to enrolled', 'from allowed to enroll to enrolled'), ('from unenrolled to unenrolled', 'from unenrolled to unenrolled'), ('N/A', 'N/A')])),
('reason', models.TextField(null=True)),
('enrolled_by', models.ForeignKey(to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)),
('enrollment', models.ForeignKey(to='student.CourseEnrollment', null=True, on_delete=models.CASCADE)),
@@ -186,7 +183,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('new_email', models.CharField(db_index=True, max_length=255, blank=True)),
('activation_key', models.CharField(unique=True, max_length=32, verbose_name=u'activation key', db_index=True)),
('activation_key', models.CharField(unique=True, max_length=32, verbose_name='activation key', db_index=True)),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
),
@@ -203,7 +200,7 @@ class Migration(migrations.Migration):
name='Registration',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('activation_key', models.CharField(unique=True, max_length=32, verbose_name=u'activation key', db_index=True)),
('activation_key', models.CharField(unique=True, max_length=32, verbose_name='activation key', db_index=True)),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
options={
@@ -216,12 +213,12 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(db_index=True, max_length=255, blank=True)),
('meta', models.TextField(blank=True)),
('courseware', models.CharField(default=u'course.xml', max_length=255, blank=True)),
('courseware', models.CharField(default='course.xml', max_length=255, blank=True)),
('language', models.CharField(db_index=True, max_length=255, blank=True)),
('location', models.CharField(db_index=True, max_length=255, blank=True)),
('year_of_birth', models.IntegerField(db_index=True, null=True, blank=True)),
('gender', models.CharField(blank=True, max_length=6, null=True, db_index=True, choices=[(u'm', u'Male'), (u'f', u'Female'), (u'o', u'Other/Prefer Not to Say')])),
('level_of_education', models.CharField(blank=True, max_length=6, null=True, db_index=True, choices=[(u'p', u'Doctorate'), (u'm', u"Master's or professional degree"), (u'b', u"Bachelor's degree"), (u'a', u'Associate degree'), (u'hs', u'Secondary/high school'), (u'jhs', u'Junior secondary/junior high/middle school'), (u'el', u'Elementary/primary school'), (u'none', u'No Formal Education'), (u'other', u'Other Education')])),
('gender', models.CharField(blank=True, max_length=6, null=True, db_index=True, choices=[('m', 'Male'), ('f', 'Female'), ('o', 'Other/Prefer Not to Say')])),
('level_of_education', models.CharField(blank=True, max_length=6, null=True, db_index=True, choices=[('p', 'Doctorate'), ('m', "Master's or professional degree"), ('b', "Bachelor's degree"), ('a', 'Associate degree'), ('hs', 'Secondary/high school'), ('jhs', 'Junior secondary/junior high/middle school'), ('el', 'Elementary/primary school'), ('none', 'No Formal Education'), ('other', 'Other Education')])),
('mailing_address', models.TextField(null=True, blank=True)),
('city', models.TextField(null=True, blank=True)),
('country', django_countries.fields.CountryField(blank=True, max_length=2, null=True)),
@@ -247,7 +244,7 @@ class Migration(migrations.Migration):
name='UserStanding',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('account_status', models.CharField(blank=True, max_length=31, choices=[(u'disabled', u'Account Disabled'), (u'enabled', u'Account Enabled')])),
('account_status', models.CharField(blank=True, max_length=31, choices=[('disabled', 'Account Disabled'), ('enabled', 'Account Enabled')])),
('standing_last_changed_at', models.DateTimeField(auto_now=True)),
('changed_by', models.ForeignKey(to=settings.AUTH_USER_MODEL, blank=True, on_delete=models.CASCADE)),
('user', models.OneToOneField(related_name='standing', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
@@ -269,22 +266,22 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='courseenrollmentallowed',
unique_together=set([('email', 'course_id')]),
unique_together={('email', 'course_id')},
),
migrations.AlterUniqueTogether(
name='languageproficiency',
unique_together=set([('code', 'user_profile')]),
unique_together={('code', 'user_profile')},
),
migrations.AlterUniqueTogether(
name='entranceexamconfiguration',
unique_together=set([('user', 'course_id')]),
unique_together={('user', 'course_id')},
),
migrations.AlterUniqueTogether(
name='courseenrollment',
unique_together=set([('user', 'course_id')]),
unique_together={('user', 'course_id')},
),
migrations.AlterUniqueTogether(
name='courseaccessrole',
unique_together=set([('user', 'org', 'course_id', 'role')]),
unique_together={('user', 'org', 'course_id', 'role')},
),
]

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-13 17:34
@@ -11,14 +10,13 @@ import opaque_keys.edx.django.models
import simple_history.models
from django.conf import settings
from django.db import migrations, models
from lms.djangoapps.experiments.models import ExperimentData
from common.djangoapps.student.models import CourseEnrollment, FBEEnrollmentExclusion
import openedx.core.djangolib.model_mixins
from common.djangoapps.course_modes import models as course_modes_models
from common.djangoapps.student.models import CourseEnrollment, FBEEnrollmentExclusion
from lms.djangoapps.experiments.models import ExperimentData
from openedx.features.course_duration_limits.config import EXPERIMENT_DATA_HOLDBACK_KEY, EXPERIMENT_ID
# These data migrations do not require changes when building from scratch.
# student.migrations.0029_add_data_researcher
# student.migrations.0011_course_key_field_to_foreign_key
@@ -287,15 +285,15 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='courseenrollmentallowed',
unique_together=set([('email', 'course_id')]),
unique_together={('email', 'course_id')},
),
migrations.AlterUniqueTogether(
name='languageproficiency',
unique_together=set([('code', 'user_profile')]),
unique_together={('code', 'user_profile')},
),
migrations.AlterUniqueTogether(
name='entranceexamconfiguration',
unique_together=set([('user', 'course_id')]),
unique_together={('user', 'course_id')},
),
migrations.AlterField(
model_name='courseenrollment',
@@ -304,11 +302,11 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='courseenrollment',
unique_together=set([('user', 'course_id')]),
unique_together={('user', 'course_id')},
),
migrations.AlterUniqueTogether(
name='courseaccessrole',
unique_together=set([('user', 'org', 'course_id', 'role')]),
unique_together={('user', 'org', 'course_id', 'role')},
),
migrations.CreateModel(
name='UserAttribute',
@@ -323,7 +321,7 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='userattribute',
unique_together=set([('user', 'name')]),
unique_together={('user', 'name')},
),
migrations.AlterField(
model_name='userattribute',
@@ -364,7 +362,7 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='courseenrollment',
unique_together=set([('user', 'course')]),
unique_together={('user', 'course')},
),
migrations.CreateModel(
name='SocialLink',

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models
@@ -14,11 +11,11 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='courseenrollment',
name='mode',
field=models.CharField(default=u'audit', max_length=100),
field=models.CharField(default='audit', max_length=100),
),
migrations.AlterField(
model_name='historicalcourseenrollment',
name='mode',
field=models.CharField(default=u'audit', max_length=100),
field=models.CharField(default='audit', max_length=100),
),
]

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import django.utils.timezone
import model_utils.fields
from django.conf import settings
@@ -28,6 +25,6 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='userattribute',
unique_together=set([('user', 'name')]),
unique_together={('user', 'name')},
),
]

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models
@@ -14,6 +11,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='userprofile',
name='level_of_education',
field=models.CharField(blank=True, max_length=6, null=True, db_index=True, choices=[(u'p', u'Doctorate'), (u'm', u"Master's or professional degree"), (u'b', u"Bachelor's degree"), (u'a', u'Associate degree'), (u'hs', u'Secondary/high school'), (u'jhs', u'Junior secondary/junior high/middle school'), (u'el', u'Elementary/primary school'), (u'none', u'No formal education'), (u'other', u'Other education')]),
field=models.CharField(blank=True, max_length=6, null=True, db_index=True, choices=[('p', 'Doctorate'), ('m', "Master's or professional degree"), ('b', "Bachelor's degree"), ('a', 'Associate degree'), ('hs', 'Secondary/high school'), ('jhs', 'Junior secondary/junior high/middle school'), ('el', 'Elementary/primary school'), ('none', 'No formal education'), ('other', 'Other education')]),
),
]

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
import django.db.models.deletion
from django.db import migrations, models
from opaque_keys.edx.django.models import CourseKeyField
@@ -71,6 +68,6 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='courseenrollment',
unique_together=set([('user', 'course')]),
unique_together={('user', 'course')},
),
]

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import migrations, models

View File

@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from django.db import migrations, models

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.14 on 2018-07-27 01:44

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-12-10 12:15

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-12-19 14:30

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.16 on 2018-12-21 10:40
@@ -22,7 +21,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('new_secondary_email', models.CharField(blank=True, db_index=True, max_length=255)),
('activation_key', models.CharField(db_index=True, max_length=32, unique=True, verbose_name=u'activation key')),
('activation_key', models.CharField(db_index=True, max_length=32, unique=True, verbose_name='activation key')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
bases=(openedx.core.djangolib.model_mixins.DeletableByUserValue, models.Model),

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-02-27 20:19

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-04-25 20:18
@@ -25,7 +24,7 @@ class Migration(migrations.Migration):
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('created', models.DateTimeField(blank=True, db_index=True, editable=False, null=True)),
('is_active', models.BooleanField(default=True)),
('mode', models.CharField(default=u'audit', max_length=100)),
('mode', models.CharField(default='audit', max_length=100)),
('history_id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_change_reason', models.CharField(max_length=100, null=True)),

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-06-24 19:01

View File

@@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.24 on 2019-09-19 19:51
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
@@ -22,7 +21,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('csv_file', models.FileField(help_text='It expect that the data will be provided in a csv file format with first row being the header and columns will be as follows: user_id, username, email, course_id, is_verified, verification_date', upload_to=u'', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=[u'csv'])])),
('csv_file', models.FileField(help_text='It expect that the data will be provided in a csv file format with first row being the header and columns will be as follows: user_id, username, email, course_id, is_verified, verification_date', upload_to='', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['csv'])])),
('changed_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Changed by')),
],
options={

View File

@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.25 on 2019-11-01 15:56
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.25 on 2019-11-01 18:46
from django.db import migrations
from common.djangoapps.student.models import CourseEnrollment, FBEEnrollmentExclusion
from lms.djangoapps.experiments.models import ExperimentData
from openedx.features.course_duration_limits.config import EXPERIMENT_DATA_HOLDBACK_KEY, EXPERIMENT_ID
from common.djangoapps.student.models import CourseEnrollment, FBEEnrollmentExclusion
def populate_fbeenrollmentexclusion(apps, schema_editor):

View File

@@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.26 on 2019-11-14 14:12
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import model_utils.fields
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.22 on 2019-07-19 13:06

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.27 on 2019-12-27 20:44

View File

@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.27 on 2020-01-27 19:02
from django.db import migrations
from common.djangoapps.student.models import CourseAccessRole

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.28 on 2020-02-18 18:36

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-03-17 11:22

View File

@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-03-25 14:28

View File

@@ -1,9 +1,9 @@
# Generated by Django 2.2.12 on 2020-06-08 15:12
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import model_utils.fields
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@@ -1,9 +1,9 @@
# Generated by Django 2.2.14 on 2020-07-16 10:33
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@@ -1,10 +1,10 @@
# Generated by Django 2.2.16 on 2020-10-15 10:19
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import model_utils.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@@ -3,8 +3,8 @@
#
# This migration does not produce any changes at the database level.
from django.db import migrations
import opaque_keys.edx.django.models
from django.db import migrations
class Migration(migrations.Migration):

View File

@@ -1,10 +1,10 @@
# Generated by Django 2.2.18 on 2021-02-18 22:49
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import model_utils.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@@ -76,7 +76,7 @@ from lms.djangoapps.courseware.models import (
OrgDynamicUpgradeDeadlineConfiguration,
)
from lms.djangoapps.courseware.toggles import (
courseware_mfe_streak_celebration_is_active,
streak_celebration_is_active,
COURSEWARE_PROCTORING_IMPROVEMENTS,
)
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
@@ -2555,9 +2555,10 @@ def log_successful_logout(sender, request, user, **kwargs): # lint-amnesty, pyl
"""Handler to log when logouts have occurred successfully."""
if hasattr(request, 'user'):
if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
AUDIT_LOG.info(u"Logout - user.id: {0}".format(request.user.id)) # pylint: disable=logging-format-interpolation
AUDIT_LOG.info('Logout - user.id: {0}'.format(request.user.id)) # pylint: disable=logging-format-interpolation
else:
AUDIT_LOG.info(u"Logout - {0}".format(request.user)) # pylint: disable=logging-format-interpolation
AUDIT_LOG.info('Logout - {0}'.format(request.user)) # pylint: disable=logging-format-interpolation
segment.track(request.user.id, 'edx.bi.user.account.logout')
@receiver(user_logged_in)
@@ -3210,9 +3211,8 @@ class UserCelebration(TimeStampedModel):
def _get_celebration(cls, user, course_key):
""" Retrieve (or create) the celebration for the provided user and course_key """
try:
# The UI for celebrations is only supported on the MFE right now, so don't turn on
# celebrations unless this enrollment's course is MFE-enabled and has milestones enabled.
if not courseware_mfe_streak_celebration_is_active(course_key):
# Only enable the streak if milestones and the streak are enabled for this course
if not streak_celebration_is_active(course_key):
return None
return user.celebration
except (cls.DoesNotExist, User.celebration.RelatedObjectDoesNotExist): # pylint: disable=no-member

View File

@@ -35,7 +35,8 @@ def create_manual_enrollment_audit( # lint-amnesty, pylint: disable=missing-fun
user_email,
state_transition,
reason,
course_enrollment
course_enrollment,
role
):
_ManualEnrollmentAudit.create_manual_enrollment_audit(
user=enrolled_by,
@@ -43,6 +44,7 @@ def create_manual_enrollment_audit( # lint-amnesty, pylint: disable=missing-fun
state_transition=state_transition,
reason=reason,
enrollment=course_enrollment,
role=role,
)

View File

@@ -9,7 +9,6 @@ import datetime
import ddt
import pytest
import six
from django.conf import settings # lint-amnesty, pylint: disable=unused-import
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.forms import ValidationError

View File

@@ -13,7 +13,7 @@ from django.test.client import Client
from milestones.tests.utils import MilestonesTestCaseMixin
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from common.djangoapps.student.models import CourseEnrollment, DashboardConfiguration # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.student.models import CourseEnrollment # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.student.roles import GlobalStaff
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.student.views import get_course_enrollments

View File

@@ -27,7 +27,6 @@ from lms.djangoapps.courseware.toggles import (
)
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.schedules.models import Schedule
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.core.djangolib.testing.utils import skip_unless_lms
from common.djangoapps.student.models import (
@@ -139,8 +138,6 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
self.assertListEqual([self.user, self.user_2], all_enrolled_users)
@skip_unless_lms
# NOTE: We mute the post_save signal to prevent Schedules from being created for new enrollments
@factory.django.mute_signals(signals.post_save)
def test_upgrade_deadline(self):
""" The property should use either the CourseMode or related Schedule to determine the deadline. """
course = CourseFactory(self_paced=True)
@@ -151,12 +148,10 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=1)
)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
assert Schedule.objects.all().count() == 0
Schedule.objects.all().delete()
assert enrollment.upgrade_deadline == course_mode.expiration_datetime
@skip_unless_lms
# NOTE: We mute the post_save signal to prevent Schedules from being created for new enrollments
@factory.django.mute_signals(signals.post_save)
def test_upgrade_deadline_with_schedule(self):
""" The property should use either the CourseMode or related Schedule to determine the deadline. """
course = CourseFactory(self_paced=True)
@@ -167,16 +162,17 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30),
)
course_overview = CourseOverview.load_from_module_store(course.id)
enrollment = CourseEnrollmentFactory(
CourseEnrollmentFactory(
course_id=course.id,
mode=CourseMode.AUDIT,
course=course_overview,
)
Schedule.objects.update(upgrade_deadline=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=5))
enrollment = CourseEnrollment.objects.first()
# The schedule's upgrade deadline should be used if a schedule exists
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
schedule = ScheduleFactory(enrollment=enrollment)
assert enrollment.upgrade_deadline == schedule.upgrade_deadline
assert enrollment.upgrade_deadline == enrollment.schedule.upgrade_deadline
@skip_unless_lms
@ddt.data(*(set(CourseMode.ALL_MODES) - set(CourseMode.AUDIT_MODES)))
@@ -197,7 +193,6 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
ScheduleFactory(enrollment=enrollment)
assert enrollment.schedule is not None
assert enrollment.upgrade_deadline == course_upgrade_deadline
@@ -215,13 +210,10 @@ class CourseEnrollmentTests(SharedModuleStoreTestCase): # lint-amnesty, pylint:
)
enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
ScheduleFactory(enrollment=enrollment)
assert enrollment.schedule is not None
assert enrollment.upgrade_deadline is None
@skip_unless_lms
# NOTE: We mute the post_save signal to prevent Schedules from being created for new enrollments
@factory.django.mute_signals(signals.post_save)
def test_enrollments_not_deleted(self):
""" Recreating a CourseOverview with an outdated version should not delete the associated enrollment. """
course = CourseFactory(self_paced=True)

View File

@@ -5,15 +5,10 @@ This test file will verify proper password policy enforcement, which is an optio
import json
from django.contrib.auth.models import AnonymousUser # lint-amnesty, pylint: disable=unused-import
from django.test import TestCase
from django.test.client import RequestFactory
from django.test.utils import override_settings
from django.urls import reverse
from mock import patch # lint-amnesty, pylint: disable=unused-import
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.util.password_policy_validators import create_validator_config

View File

@@ -15,8 +15,6 @@ from pytz import UTC
from six.moves import range, zip
from common.test.utils import XssTestMixin
from common.djangoapps.course_modes.tests.factories import CourseModeFactory # lint-amnesty, pylint: disable=unused-import
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.student.models import CourseEnrollment, DashboardConfiguration
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.student.views import get_course_enrollments

View File

@@ -7,7 +7,7 @@ import itertools
import json
import re
import unittest
from datetime import datetime, timedelta # lint-amnesty, pylint: disable=unused-import
from datetime import timedelta # lint-amnesty, pylint: disable=unused-import
import ddt
import six
@@ -17,7 +17,6 @@ from django.test import TestCase
from django.test.utils import override_settings
from django.urls import reverse
from django.utils.timezone import now
from edx_toggles.toggles.testutils import override_waffle_flag
from milestones.tests.utils import MilestonesTestCaseMixin
from mock import patch
from opaque_keys import InvalidKeyError
@@ -31,8 +30,6 @@ from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFact
from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from openedx.features.course_experience.tests.views.helpers import add_course_mode
@@ -755,7 +752,6 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
assert resume_button_html in dashboard_html
assert view_button_html not in dashboard_html
@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
def test_content_gating_course_card_changes(self):
"""
When a course is expired, the links on the course card should be removed.
@@ -775,9 +771,6 @@ class StudentDashboardTests(SharedModuleStoreTestCase, MilestonesTestCaseMixin,
enrollment.created = self.THREE_YEARS_AGO + timedelta(days=1)
enrollment.save()
# pylint: disable=unused-variable
schedule = ScheduleFactory(enrollment=enrollment)
response = self.client.get(reverse('dashboard'))
dashboard_html = self._remove_whitespace_from_response(response)
access_expired_substring = 'Accessexpired'

View File

@@ -418,7 +418,7 @@ class DashboardTest(ModuleStoreTestCase, TestVerificationBase):
Note to future developers:
If you break this test so that the "check_mongo_calls(0)" fails,
please do NOT change it to "check_mongo_calls(n>1)". Instead, change
please do NOT change it to "check_mongo_calls(n>=1)". Instead, change
your code to not load courses from the module store. This may
involve adding fields to CourseOverview so that loading a full
CourseDescriptor isn't necessary.

View File

@@ -29,7 +29,7 @@ from edx_ace import ace
from edx_ace.recipient import Recipient
from edx_django_utils import monitoring as monitoring_utils
from eventtracking import tracker
from ipware.ip import get_ip
from ipware.ip import get_client_ip
# Note that this lives in LMS, so this dependency should be refactored.
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
@@ -58,7 +58,7 @@ from common.djangoapps.student.message_types import AccountActivation, EmailChan
from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable=unused-import
AccountRecovery,
CourseEnrollment,
PendingEmailChange,
PendingEmailChange, # unimport:skip
PendingSecondaryEmailChange,
Registration,
RegistrationCookieConfiguration,
@@ -94,6 +94,7 @@ REGISTRATION_UTM_PARAMETERS = {
'utm_content': 'registration_utm_content',
}
REGISTRATION_UTM_CREATED_AT = 'registration_utm_created_at'
USER_ACCOUNT_ACTIVATED = 'edx.user.account.activated'
def csrf_token(context):
@@ -340,7 +341,7 @@ def change_enrollment(request, check_access=True):
# or if the user is enrolling in a country in which the course
# is not available.
redirect_url = embargo_api.redirect_if_blocked(
course_id, user=user, ip_address=get_ip(request),
course_id, user=user, ip_address=get_client_ip(request)[0],
url=request.path
)
if redirect_url:
@@ -529,6 +530,13 @@ def activate_account(request, key):
# Success message for logged in users.
message = _('{html_start}Success{html_end} You have activated your account.')
tracker.emit(
USER_ACCOUNT_ACTIVATED,
{
"user_id": registration.user.id,
}
)
if not request.user.is_authenticated:
# Success message for logged out users
message = _(

View File

@@ -10,7 +10,6 @@ from django.conf import settings
from django.test import RequestFactory, TestCase
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.jwt.tests.utils import generate_jwt
from mock import patch # lint-amnesty, pylint: disable=unused-import
from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response
from rest_framework.views import APIView

View File

@@ -6,12 +6,10 @@ Decorators that can be used to interact with third_party_auth.
from functools import wraps
from django.conf import settings
from django.shortcuts import redirect # lint-amnesty, pylint: disable=unused-import
from django.utils.decorators import available_attrs
from six.moves.urllib.parse import urlencode, urlparse # lint-amnesty, pylint: disable=unused-import
from six.moves.urllib.parse import urlparse # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.third_party_auth.models import LTIProviderConfig
from common.djangoapps.third_party_auth.provider import Registry # lint-amnesty, pylint: disable=unused-import
def xframe_allow_whitelisted(view_func):

View File

@@ -1,8 +1,6 @@
"""
Tests for SAMLConfiguration endpoints
"""
import unittest # lint-amnesty, pylint: disable=unused-import
from django.urls import reverse
from django.contrib.sites.models import Site
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
@@ -10,7 +8,6 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imp
from rest_framework import status
from rest_framework.test import APITestCase
from common.djangoapps.third_party_auth.models import SAMLConfiguration
from common.djangoapps.third_party_auth.tests import testutil # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.third_party_auth.tests.utils import skip_unless_thirdpartyauth
SAML_CONFIGURATIONS = [
{

View File

@@ -1,8 +1,6 @@
"""
Tests for SAMLProviderConfig endpoints
"""
import unittest
import copy
from uuid import uuid4
from django.urls import reverse
@@ -16,7 +14,6 @@ from enterprise.models import EnterpriseCustomerIdentityProvider, EnterpriseCust
from enterprise.constants import ENTERPRISE_ADMIN_ROLE, ENTERPRISE_LEARNER_ROLE
from common.djangoapps.third_party_auth.tests.samlutils import set_jwt_cookie
from common.djangoapps.third_party_auth.models import SAMLProviderConfig, SAMLConfiguration
from common.djangoapps.third_party_auth.tests import testutil
from common.djangoapps.third_party_auth.tests.utils import skip_unless_thirdpartyauth
from common.djangoapps.third_party_auth.utils import convert_saml_slug_provider_id

View File

@@ -1,4 +1,3 @@
import unittest
import copy
import pytz
from uuid import uuid4
@@ -12,8 +11,6 @@ from rest_framework.test import APITestCase
from enterprise.models import EnterpriseCustomer, EnterpriseCustomerIdentityProvider
from enterprise.constants import ENTERPRISE_ADMIN_ROLE, ENTERPRISE_LEARNER_ROLE
from common.djangoapps.third_party_auth.tests import testutil
from common.djangoapps.third_party_auth.models import SAMLProviderData, SAMLProviderConfig
from common.djangoapps.third_party_auth.tests.samlutils import set_jwt_cookie
from common.djangoapps.third_party_auth.tests.utils import skip_unless_thirdpartyauth

View File

@@ -1,9 +1,6 @@
"""
Use the 'Dummy' auth provider for generic integration tests of third_party_auth.
"""
import unittest # lint-amnesty, pylint: disable=unused-import
from common.djangoapps.third_party_auth.tests import testutil
from common.djangoapps.third_party_auth.tests.utils import skip_unless_thirdpartyauth
from .base import IntegrationTestMixin

View File

@@ -7,7 +7,6 @@ import datetime
import json
import logging
import os
import unittest # lint-amnesty, pylint: disable=unused-import
from unittest import skip
import ddt

View File

@@ -2,9 +2,6 @@
Tests third_party_auth admin views
"""
import unittest # lint-amnesty, pylint: disable=unused-import
from django.contrib.admin.sites import AdminSite
from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import models

View File

@@ -2,11 +2,7 @@
Tests for third_party_auth decorators.
"""
import unittest # lint-amnesty, pylint: disable=unused-import
import ddt
from django.conf import settings # lint-amnesty, pylint: disable=unused-import
from django.http import HttpResponse
from django.test import RequestFactory

View File

@@ -3,8 +3,6 @@ Unit tests for the IdentityServer3 OAuth2 Backend
"""
import json
import ddt
import pytest # pylint: disable=unused-import
from common.djangoapps.third_party_auth.identityserver3 import IdentityServer3
from common.djangoapps.third_party_auth.tests import testutil
from common.djangoapps.third_party_auth.tests.utils import skip_unless_thirdpartyauth

View File

@@ -2,7 +2,6 @@
import json
import unittest # lint-amnesty, pylint: disable=unused-import
import ddt
import mock

View File

@@ -1,8 +1,5 @@
"""Unit tests for settings.py."""
import unittest # lint-amnesty, pylint: disable=unused-import
from mock import patch
from common.djangoapps.third_party_auth import provider, settings
from common.djangoapps.third_party_auth.tests import testutil

View File

@@ -36,7 +36,7 @@ class ThirdPartyOAuthTestMixin(ThirdPartyAuthTestMixin):
def setUp(self): # lint-amnesty, pylint: disable=arguments-differ
super(ThirdPartyOAuthTestMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
if self.CREATE_USER:
self.user = UserFactory()
self.user = UserFactory.create(password='secret')
UserSocialAuth.objects.create(user=self.user, provider=self.BACKEND, uid=self.social_uid)
self.oauth_client = self._create_client()
if self.BACKEND == 'google-oauth2':

View File

@@ -16,7 +16,7 @@ import six
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
from eventtracking import tracker
from ipware.ip import get_ip
from ipware.ip import get_client_ip
from common.djangoapps.track import contexts, views
@@ -229,7 +229,7 @@ class TrackMiddleware(MiddlewareMixin):
def get_request_ip_address(self, request):
"""Gets the IP address of the request"""
ip_address = get_ip(request)
ip_address = get_client_ip(request)[0]
if ip_address is not None:
return ip_address
else:

View File

@@ -1,9 +1,6 @@
"""
URLs for track app
"""
from django.conf import settings # lint-amnesty, pylint: disable=unused-import
from django.conf.urls import url
from . import views

View File

@@ -6,7 +6,7 @@ import six
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.http import HttpResponse
from eventtracking import tracker as eventtracker
from ipware.ip import get_ip
from ipware.ip import get_client_ip
from common.djangoapps.track import contexts, shim, tracker
@@ -22,7 +22,7 @@ def _get_request_header(request, header_name, default=''):
def _get_request_ip(request, default=''):
"""Helper method to get IP from a request's META dict, if present."""
if request is not None and hasattr(request, 'META'):
return get_ip(request)
return get_client_ip(request)[0]
else:
return default

View File

@@ -10,7 +10,6 @@ not migrating so as not to inconvenience users by logging them all out.
from functools import wraps
import six
from django.conf import settings # lint-amnesty, pylint: disable=unused-import
from django.core import cache
# If we can't find a 'general' CACHE defined in settings.py, we simply fall back
# to returning the default cache. This will happen with dev machines.

View File

@@ -8,9 +8,8 @@ import random
# TransactionManagementError used below actually *does* derive from the standard "Exception" class.
# lint-amnesty, pylint: disable=bad-option-value, nonstandard-exception
from contextlib import contextmanager
from functools import wraps # lint-amnesty, pylint: disable=unused-import
from django.db import DEFAULT_DB_ALIAS, DatabaseError, Error, transaction # lint-amnesty, pylint: disable=unused-import
from django.db import DEFAULT_DB_ALIAS, transaction # lint-amnesty, pylint: disable=unused-import
from openedx.core.lib.cache_utils import get_cache

View File

@@ -5,10 +5,7 @@ so that we can cache any keys, not just ones that memcache would ordinarily acce
import hashlib
import six.moves.urllib.error
import six.moves.urllib.parse
import six.moves.urllib.request
from django.utils.encoding import smart_str

View File

@@ -10,9 +10,6 @@ import unittest
import ddt
import mock
from lxml import etree
# Changes formatting of empty elements; import here to avoid test order dependence
import xmodule.modulestore.xml # pylint: disable=unused-import
from capa.tests.helpers import new_loncapa_problem, test_capa_system
from openedx.core.djangolib.markup import HTML

View File

@@ -6,9 +6,6 @@ i.e. those with the <multiplechoiceresponse> element
import textwrap
import unittest
# Changes formatting of empty elements; import here to avoid test order dependence
import xmodule.modulestore.xml # pylint: disable=unused-import
from capa.tests.helpers import load_fixture, new_loncapa_problem, test_capa_system

View File

@@ -7,8 +7,6 @@ import pytest
from safe_lxml import defuse_xml_libs
from openedx.core.pytest_hooks import pytest_configure # pylint: disable=unused-import
defuse_xml_libs()

View File

@@ -21,7 +21,6 @@ import unicodedata
#import subprocess
from copy import deepcopy
from functools import reduce
from xml.sax.saxutils import unescape # lint-amnesty, pylint: disable=unused-import
import six
import sympy

View File

@@ -6,7 +6,6 @@ Common code shared by course and library fixtures.
import json
import requests
import six # lint-amnesty, pylint: disable=unused-import
from lazy import lazy
from common.test.acceptance.fixtures import STUDIO_BASE_URL

View File

@@ -7,7 +7,7 @@ import logging
from bok_choy.javascript import js_defined, wait_for_js
from bok_choy.page_object import PageObject
from bok_choy.promise import EmptyPromise, Promise # lint-amnesty, pylint: disable=unused-import
from bok_choy.promise import EmptyPromise # lint-amnesty, pylint: disable=unused-import
log = logging.getLogger('VideoPage')

View File

@@ -3,7 +3,7 @@ Course Outline page in Studio.
"""
from bok_choy.javascript import js_defined, wait_for_js # lint-amnesty, pylint: disable=unused-import
from bok_choy.javascript import js_defined # lint-amnesty, pylint: disable=unused-import
from bok_choy.page_object import PageObject
from bok_choy.promise import EmptyPromise
from selenium.webdriver.support.ui import Select

View File

@@ -7,17 +7,19 @@ from uuid import uuid4
import pytest
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc # lint-amnesty, pylint: disable=unused-import
from common.test.acceptance.fixtures.course import CourseFixture # lint-amnesty, pylint: disable=unused-import
from common.test.acceptance.fixtures.discussion import (
Comment,
Response,
SingleThreadViewFixture,
Thread,
)
from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
from common.test.acceptance.pages.lms.discussion import (
DiscussionTabHomePage,
DiscussionTabSingleThreadPage,
)
from common.test.acceptance.tests.discussion.helpers import BaseDiscussionMixin, BaseDiscussionTestCase
from common.test.acceptance.tests.helpers import UniqueCourseTest

View File

@@ -19,12 +19,12 @@ from bok_choy.promise import EmptyPromise, Promise
from bok_choy.web_app_test import WebAppTest
from opaque_keys.edx.locator import CourseLocator
from path import Path as path
from pymongo import ASCENDING, MongoClient # lint-amnesty, pylint: disable=unused-import
from pymongo import MongoClient # lint-amnesty, pylint: disable=unused-import
from selenium.common.exceptions import StaleElementReferenceException
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait
from six.moves import range, zip # lint-amnesty, pylint: disable=unused-import
from six.moves import range # lint-amnesty, pylint: disable=unused-import
from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory
from common.test.acceptance.fixtures.course import XBlockFixtureDesc

View File

@@ -1,9 +1,4 @@
"""Code run by pylint before running any tests."""
# Patch the xml libs before anything else.
from openedx.core.pytest_hooks import pytest_configure # pylint: disable=unused-import
from safe_lxml import defuse_xml_libs
defuse_xml_libs()

View File

@@ -1 +1 @@
521f88f382acecc86fb7c9b5128d858a14313b07
c4a7790413ee6ca1e3eabb9de0024a0b5c2a3ac7

Some files were not shown because too many files have changed in this diff Show More