diff --git a/common/djangoapps/student/tests/test_refunds.py b/common/djangoapps/student/tests/test_refunds.py index 38d089bf93..ae521f3fb4 100644 --- a/common/djangoapps/student/tests/test_refunds.py +++ b/common/djangoapps/student/tests/test_refunds.py @@ -18,11 +18,11 @@ from edx_rest_api_client.exceptions import SlumberBaseException from mock import patch from slumber.exceptions import HttpClientError, HttpServerError +from certificates.models import CertificateStatuses, GeneratedCertificate # pylint: disable=import-error +from certificates.tests.factories import GeneratedCertificateFactory # pylint: disable=import-error # These imports refer to lms djangoapps. # Their testcases are only run under lms. from course_modes.tests.factories import CourseModeFactory -from certificates.models import CertificateStatuses, GeneratedCertificate # pylint: disable=import-error -from certificates.tests.factories import GeneratedCertificateFactory # pylint: disable=import-error from openedx.core.djangoapps.commerce.utils import ECOMMERCE_DATE_FORMAT from student.models import CourseEnrollment, CourseEnrollmentAttribute from student.tests.factories import UserFactory @@ -203,7 +203,7 @@ class RefundableTest(SharedModuleStoreTestCase): raised while getting order detail for ecommerce. """ # importing this after overriding value of ECOMMERCE_API_URL - from commerce.tests.mocks import mock_order_endpoint + from lms.djangoapps.commerce.tests.mocks import mock_order_endpoint self.client.login(username=self.user.username, password=self.USER_PASSWORD) with mock_order_endpoint(order_number=self.ORDER_NUMBER, exception=exception, reset_on_exit=False): diff --git a/common/djangoapps/student/tests/test_views.py b/common/djangoapps/student/tests/test_views.py index 790cafd809..e74e58fa97 100644 --- a/common/djangoapps/student/tests/test_views.py +++ b/common/djangoapps/student/tests/test_views.py @@ -14,17 +14,17 @@ from django.core.urlresolvers import reverse from django.test import RequestFactory, TestCase from edx_oauth2_provider.constants import AUTHORIZED_CLIENTS_SESSION_KEY from edx_oauth2_provider.tests.factories import ClientFactory, TrustedClientFactory -from mock import patch -from pyquery import PyQuery as pq -from opaque_keys import InvalidKeyError - from milestones.tests.utils import MilestonesTestCaseMixin +from mock import patch +from opaque_keys import InvalidKeyError +from pyquery import PyQuery as pq + from student.cookies import get_user_info_cookie_data from student.helpers import DISABLE_UNENROLL_CERT_STATES from student.models import CourseEnrollment, UserProfile from student.signals import REFUND_ORDER from student.tests.factories import CourseEnrollmentFactory, UserFactory -from util.milestones_helpers import set_prerequisite_courses, remove_prerequisite_course, get_course_milestones +from util.milestones_helpers import get_course_milestones, remove_prerequisite_course, set_prerequisite_courses from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -95,7 +95,7 @@ class TestStudentDashboardUnenrollments(SharedModuleStoreTestCase): self.cert_status = cert_status with patch('student.views.cert_info', side_effect=self.mock_cert): - with patch('commerce.signals.handle_refund_order') as mock_refund_handler: + with patch('lms.djangoapps.commerce.signals.handle_refund_order') as mock_refund_handler: REFUND_ORDER.connect(mock_refund_handler) response = self.client.post( reverse('change_enrollment'), diff --git a/lms/djangoapps/commerce/__init__.py b/lms/djangoapps/commerce/__init__.py index a02aabc656..e69de29bb2 100644 --- a/lms/djangoapps/commerce/__init__.py +++ b/lms/djangoapps/commerce/__init__.py @@ -1,3 +0,0 @@ -""" Commerce app. """ -# this is here to support registering the signals in signals.py -from commerce import signals diff --git a/lms/djangoapps/commerce/admin.py b/lms/djangoapps/commerce/admin.py index c5c178183a..420e174900 100644 --- a/lms/djangoapps/commerce/admin.py +++ b/lms/djangoapps/commerce/admin.py @@ -3,6 +3,6 @@ from config_models.admin import ConfigurationModelAdmin from django.contrib import admin -from commerce.models import CommerceConfiguration +from .models import CommerceConfiguration admin.site.register(CommerceConfiguration, ConfigurationModelAdmin) diff --git a/lms/djangoapps/commerce/api/urls.py b/lms/djangoapps/commerce/api/urls.py index a8715a738d..af1a1dde5b 100644 --- a/lms/djangoapps/commerce/api/urls.py +++ b/lms/djangoapps/commerce/api/urls.py @@ -4,6 +4,6 @@ API URLs. from django.conf.urls import include, url urlpatterns = [ - url(r'^v0/', include('commerce.api.v0.urls', namespace='v0')), - url(r'^v1/', include('commerce.api.v1.urls', namespace='v1')), + url(r'^v0/', include('lms.djangoapps.commerce.api.v0.urls', namespace='v0')), + url(r'^v1/', include('lms.djangoapps.commerce.api.v1.urls', namespace='v1')), ] diff --git a/lms/djangoapps/commerce/api/v0/tests/test_views.py b/lms/djangoapps/commerce/api/v0/tests/test_views.py index 2e2a864130..558db9b500 100644 --- a/lms/djangoapps/commerce/api/v0/tests/test_views.py +++ b/lms/djangoapps/commerce/api/v0/tests/test_views.py @@ -14,21 +14,22 @@ from django.test.utils import override_settings from edx_rest_api_client import exceptions from nose.plugins.attrib import attr -from commerce.api.v0.views import SAILTHRU_CAMPAIGN_COOKIE -from commerce.constants import Messages -from commerce.tests.mocks import mock_basket_order -from commerce.tests.test_views import UserMixin from course_modes.models import CourseMode +from course_modes.tests.factories import CourseModeFactory from enrollment.api import get_enrollment from openedx.core.djangoapps.embargo.test_utils import restrict_course from openedx.core.lib.django_test_client_utils import get_absolute_url from student.models import CourseEnrollment -from course_modes.tests.factories import CourseModeFactory from student.tests.tests import EnrollmentEventTestMixin from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory +from ....constants import Messages +from ....tests.mocks import mock_basket_order +from ....tests.test_views import UserMixin +from ..views import SAILTHRU_CAMPAIGN_COOKIE + UTM_COOKIE_NAME = 'edx.test.utm' UTM_COOKIE_CONTENTS = { 'utm_source': 'test-source' @@ -250,7 +251,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) self.assertIsNotNone(get_enrollment(self.user.username, unicode(self.course.id))) - @mock.patch('commerce.api.v0.views.update_email_opt_in') + @mock.patch('lms.djangoapps.commerce.api.v0.views.update_email_opt_in') @ddt.data(*itertools.product((False, True), (False, True), (False, True))) @ddt.unpack def test_marketing_email_opt_in(self, is_opt_in, has_sku, is_exception, mock_update): diff --git a/lms/djangoapps/commerce/api/v0/urls.py b/lms/djangoapps/commerce/api/v0/urls.py index 601df846e8..b6df78845c 100644 --- a/lms/djangoapps/commerce/api/v0/urls.py +++ b/lms/djangoapps/commerce/api/v0/urls.py @@ -3,7 +3,7 @@ API v0 URLs. """ from django.conf.urls import include, url -from commerce.api.v0 import views +from . import views BASKET_URLS = [ url(r'^$', views.BasketsView.as_view(), name='create'), diff --git a/lms/djangoapps/commerce/api/v0/views.py b/lms/djangoapps/commerce/api/v0/views.py index 181cf81ed4..1d2db22b2f 100644 --- a/lms/djangoapps/commerce/api/v0/views.py +++ b/lms/djangoapps/commerce/api/v0/views.py @@ -9,8 +9,6 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.status import HTTP_406_NOT_ACCEPTABLE, HTTP_409_CONFLICT from rest_framework.views import APIView -from commerce.constants import Messages -from commerce.http import DetailResponse from course_modes.models import CourseMode from courseware import courses from enrollment.api import add_enrollment @@ -22,6 +20,9 @@ from openedx.core.lib.api.authentication import OAuth2AuthenticationAllowInactiv from student.models import CourseEnrollment from util.json_request import JsonResponse +from ...constants import Messages +from ...http import DetailResponse + log = logging.getLogger(__name__) SAILTHRU_CAMPAIGN_COOKIE = 'sailthru_bid' diff --git a/lms/djangoapps/commerce/api/v1/permissions.py b/lms/djangoapps/commerce/api/v1/permissions.py index 629f5835dc..5ff2b9842d 100644 --- a/lms/djangoapps/commerce/api/v1/permissions.py +++ b/lms/djangoapps/commerce/api/v1/permissions.py @@ -3,10 +3,11 @@ from django.conf import settings from django.contrib.auth.models import User from rest_framework.permissions import BasePermission, DjangoModelPermissions -from commerce.utils import is_account_activation_requirement_disabled from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.lib.api.permissions import ApiKeyHeaderPermission +from ...utils import is_account_activation_requirement_disabled + class ApiKeyOrModelPermission(BasePermission): """ Access granted for requests with API key in header, diff --git a/lms/djangoapps/commerce/api/v1/serializers.py b/lms/djangoapps/commerce/api/v1/serializers.py index c1a4645c6c..b0b526371e 100644 --- a/lms/djangoapps/commerce/api/v1/serializers.py +++ b/lms/djangoapps/commerce/api/v1/serializers.py @@ -7,10 +7,11 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from rest_framework import serializers -from commerce.api.v1.models import Course from course_modes.models import CourseMode from xmodule.modulestore.django import modulestore +from .models import Course + class CourseModeSerializer(serializers.ModelSerializer): """ CourseMode serializer. """ diff --git a/lms/djangoapps/commerce/api/v1/tests/test_models.py b/lms/djangoapps/commerce/api/v1/tests/test_models.py index 16c40acaaf..982c0ea481 100644 --- a/lms/djangoapps/commerce/api/v1/tests/test_models.py +++ b/lms/djangoapps/commerce/api/v1/tests/test_models.py @@ -2,9 +2,10 @@ import ddt from django.test import TestCase -from commerce.api.v1.models import Course from course_modes.models import CourseMode +from ..models import Course + @ddt.ddt class CourseTests(TestCase): diff --git a/lms/djangoapps/commerce/api/v1/tests/test_serializers.py b/lms/djangoapps/commerce/api/v1/tests/test_serializers.py index 93bcb9aee3..c17b069122 100644 --- a/lms/djangoapps/commerce/api/v1/tests/test_serializers.py +++ b/lms/djangoapps/commerce/api/v1/tests/test_serializers.py @@ -1,7 +1,7 @@ """ Commerce API v1 serializer tests. """ from django.test import TestCase -from commerce.api.v1.serializers import serializers, validate_course_id +from ..serializers import serializers, validate_course_id class CourseValidatorTests(TestCase): diff --git a/lms/djangoapps/commerce/api/v1/tests/test_views.py b/lms/djangoapps/commerce/api/v1/tests/test_views.py index 177d1ea9f3..f8a32ce962 100644 --- a/lms/djangoapps/commerce/api/v1/tests/test_views.py +++ b/lms/djangoapps/commerce/api/v1/tests/test_views.py @@ -14,14 +14,15 @@ from edx_rest_api_client import exceptions from nose.plugins.attrib import attr from rest_framework.utils.encoders import JSONEncoder -from commerce.tests.mocks import mock_order_endpoint -from commerce.tests.test_views import UserMixin from course_modes.models import CourseMode from lms.djangoapps.verify_student.models import VerificationDeadline from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory +from ....tests.mocks import mock_order_endpoint +from ....tests.test_views import UserMixin + PASSWORD = 'test' JSON_CONTENT_TYPE = 'application/json' diff --git a/lms/djangoapps/commerce/api/v1/urls.py b/lms/djangoapps/commerce/api/v1/urls.py index e448701ea8..88a4de47b5 100644 --- a/lms/djangoapps/commerce/api/v1/urls.py +++ b/lms/djangoapps/commerce/api/v1/urls.py @@ -1,8 +1,7 @@ -""" API v1 URLs. """ from django.conf import settings from django.conf.urls import include, url -from commerce.api.v1 import views +from . import views COURSE_URLS = [ url(r'^$', views.CourseListView.as_view(), name='list'), diff --git a/lms/djangoapps/commerce/api/v1/views.py b/lms/djangoapps/commerce/api/v1/views.py index 5c25d991c4..5a0ee6d9a2 100644 --- a/lms/djangoapps/commerce/api/v1/views.py +++ b/lms/djangoapps/commerce/api/v1/views.py @@ -10,15 +10,16 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView from rest_framework_oauth.authentication import OAuth2Authentication -from commerce.api.v1.models import Course -from commerce.api.v1.permissions import ApiKeyOrModelPermission, IsAuthenticatedOrActivationOverridden -from commerce.api.v1.serializers import CourseSerializer -from commerce.utils import is_account_activation_requirement_disabled from course_modes.models import CourseMode from openedx.core.djangoapps.commerce.utils import ecommerce_api_client from openedx.core.lib.api.mixins import PutAsCreateMixin from util.json_request import JsonResponse +from ...utils import is_account_activation_requirement_disabled +from .models import Course +from .permissions import ApiKeyOrModelPermission, IsAuthenticatedOrActivationOverridden +from .serializers import CourseSerializer + log = logging.getLogger(__name__) diff --git a/lms/djangoapps/commerce/apps.py b/lms/djangoapps/commerce/apps.py new file mode 100644 index 0000000000..d0bddaa323 --- /dev/null +++ b/lms/djangoapps/commerce/apps.py @@ -0,0 +1,17 @@ +""" +Commerce Application Configuration +""" +from django.apps import AppConfig + + +class CommerceConfig(AppConfig): + """ + Application Configuration for Commerce. + """ + name = 'lms.djangoapps.commerce' + + def ready(self): + """ + Connect handlers to signals. + """ + from . import signals # pylint: disable=unused-variable diff --git a/lms/djangoapps/commerce/management/commands/configure_commerce.py b/lms/djangoapps/commerce/management/commands/configure_commerce.py index 6b47a8814d..df27a5d3db 100644 --- a/lms/djangoapps/commerce/management/commands/configure_commerce.py +++ b/lms/djangoapps/commerce/management/commands/configure_commerce.py @@ -9,7 +9,7 @@ import logging from django.core.management import BaseCommand -from commerce.models import CommerceConfiguration +from ...models import CommerceConfiguration logger = logging.getLogger(__name__) # pylint: disable=invalid-name diff --git a/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py b/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py index 1e7791e5d6..a52e1d7d5e 100644 --- a/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py +++ b/lms/djangoapps/commerce/management/commands/tests/test_configure_commerce.py @@ -4,7 +4,7 @@ Tests for management command for enabling commerce configuration. from django.core.management import call_command from django.test import TestCase -from commerce.models import CommerceConfiguration +from ....models import CommerceConfiguration class TestCommerceConfigurationCommand(TestCase): diff --git a/lms/djangoapps/commerce/signals.py b/lms/djangoapps/commerce/signals.py index 25cb10ebc1..01b4e9146d 100644 --- a/lms/djangoapps/commerce/signals.py +++ b/lms/djangoapps/commerce/signals.py @@ -10,14 +10,18 @@ from urlparse import urljoin import requests from django.conf import settings from django.contrib.auth import get_user_model +from django.contrib.auth.models import AnonymousUser from django.dispatch import receiver from django.utils.translation import ugettext as _ +from openedx.core.djangoapps.commerce.utils import ecommerce_api_client, is_commerce_service_configured from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.theming import helpers as theming_helpers from request_cache.middleware import RequestCache from student.signals import REFUND_ORDER +from .models import CommerceConfiguration + log = logging.getLogger(__name__) @@ -28,10 +32,6 @@ def handle_refund_order(sender, course_enrollment=None, **kwargs): Signal receiver for unenrollments, used to automatically initiate refunds when applicable. """ - # Import is placed here to avoid model import at project startup. - from django.contrib.auth.models import AnonymousUser - from openedx.core.djangoapps.commerce.utils import is_commerce_service_configured - if not is_commerce_service_configured(): return @@ -84,10 +84,6 @@ def refund_seat(course_enrollment): exceptions.SlumberBaseException: for any unhandled HTTP error during communication with the E-Commerce Service. exceptions.Timeout: if the attempt to reach the commerce service timed out. """ - # Import is placed here to avoid model import at project startup. - from commerce.models import CommerceConfiguration - from openedx.core.djangoapps.commerce.utils import ecommerce_api_client - User = get_user_model() # pylint:disable=invalid-name course_key_str = unicode(course_enrollment.course_id) enrollee = course_enrollment.user diff --git a/lms/djangoapps/commerce/tests/mocks.py b/lms/djangoapps/commerce/tests/mocks.py index 1ce677842a..a42be0021d 100644 --- a/lms/djangoapps/commerce/tests/mocks.py +++ b/lms/djangoapps/commerce/tests/mocks.py @@ -4,7 +4,7 @@ import json import httpretty from django.conf import settings -from commerce.tests import factories +from . import factories # pylint: disable=invalid-name diff --git a/lms/djangoapps/commerce/tests/test_signals.py b/lms/djangoapps/commerce/tests/test_signals.py index f74f1f2500..55b1d15d53 100644 --- a/lms/djangoapps/commerce/tests/test_signals.py +++ b/lms/djangoapps/commerce/tests/test_signals.py @@ -18,14 +18,15 @@ from django.test.utils import override_settings from opaque_keys.edx.keys import CourseKey from requests import Timeout -from commerce.models import CommerceConfiguration -from commerce.signals import create_zendesk_ticket, generate_refund_notification_body, send_refund_notification -from commerce.tests import JSON -from commerce.tests.mocks import mock_create_refund, mock_process_refund from course_modes.models import CourseMode from student.signals import REFUND_ORDER from student.tests.factories import CourseEnrollmentFactory, UserFactory +from . import JSON +from ..models import CommerceConfiguration +from ..signals import create_zendesk_ticket, generate_refund_notification_body, send_refund_notification +from .mocks import mock_create_refund, mock_process_refund + ZENDESK_URL = 'http://zendesk.example.com/' ZENDESK_USER = 'test@example.com' ZENDESK_API_KEY = 'abc123' @@ -76,11 +77,11 @@ class TestRefundSignal(TestCase): Ensure that the receiver quietly bypasses attempts to initiate refunds when there is no external service configured. """ - with mock.patch('commerce.signals.refund_seat') as mock_refund_seat: + with mock.patch('lms.djangoapps.commerce.signals.refund_seat') as mock_refund_seat: self.send_signal() self.assertFalse(mock_refund_seat.called) - @mock.patch('commerce.signals.refund_seat') + @mock.patch('lms.djangoapps.commerce.signals.refund_seat') def test_receiver(self, mock_refund_seat): """ Ensure that the REFUND_ORDER signal triggers correct calls to @@ -100,8 +101,8 @@ class TestRefundSignal(TestCase): self.send_signal() self.assertFalse(mock_refund_seat.called) - @mock.patch('commerce.signals.refund_seat') - @mock.patch('commerce.signals.get_request_user', return_value=None) + @mock.patch('lms.djangoapps.commerce.signals.refund_seat') + @mock.patch('lms.djangoapps.commerce.signals.get_request_user', return_value=None) def test_requester(self, mock_get_request_user, mock_refund_seat): """ Ensure the right requester is specified when initiating refunds. @@ -131,7 +132,7 @@ class TestRefundSignal(TestCase): self.send_signal() self.assertFalse(mock_refund_seat.called) - @mock.patch('commerce.signals.log.exception') + @mock.patch('lms.djangoapps.commerce.signals.log.exception') def test_error_logging(self, mock_log_exception): """ Ensure that unexpected Exceptions are logged as errors (but do not @@ -141,7 +142,7 @@ class TestRefundSignal(TestCase): self.send_signal() self.assertTrue(mock_log_exception.called) - @mock.patch('commerce.signals.send_refund_notification') + @mock.patch('lms.djangoapps.commerce.signals.send_refund_notification') def test_notification_when_approval_fails(self, mock_send_notification): """ Ensure the notification function is triggered when refunds are initiated, and cannot be automatically approved. @@ -156,7 +157,7 @@ class TestRefundSignal(TestCase): self.assertTrue(mock_send_notification.called) mock_send_notification.assert_called_with(self.course_enrollment, [failed_refund_id]) - @mock.patch('commerce.signals.send_refund_notification') + @mock.patch('lms.djangoapps.commerce.signals.send_refund_notification') def test_notification_if_automatic_approval_disabled(self, mock_send_notification): """ Ensure the notification is always sent if the automatic approval functionality is disabled. @@ -170,7 +171,7 @@ class TestRefundSignal(TestCase): self.assertTrue(mock_send_notification.called) mock_send_notification.assert_called_with(self.course_enrollment, [refund_id]) - @mock.patch('commerce.signals.send_refund_notification') + @mock.patch('lms.djangoapps.commerce.signals.send_refund_notification') def test_no_notification_after_approval(self, mock_send_notification): """ Ensure the notification function is triggered when refunds are initiated, and cannot be automatically approved. @@ -185,7 +186,7 @@ class TestRefundSignal(TestCase): last_request = httpretty.last_request() self.assertDictEqual(json.loads(last_request.body), {'action': 'approve_payment_only'}) - @mock.patch('commerce.signals.send_refund_notification') + @mock.patch('lms.djangoapps.commerce.signals.send_refund_notification') def test_notification_no_refund(self, mock_send_notification): """ Ensure the notification function is NOT triggered when no refunds are @@ -195,7 +196,7 @@ class TestRefundSignal(TestCase): self.send_signal() self.assertFalse(mock_send_notification.called) - @mock.patch('commerce.signals.send_refund_notification') + @mock.patch('lms.djangoapps.commerce.signals.send_refund_notification') @ddt.data( CourseMode.HONOR, CourseMode.PROFESSIONAL, @@ -216,8 +217,8 @@ class TestRefundSignal(TestCase): self.send_signal() self.assertFalse(mock_send_notification.called) - @mock.patch('commerce.signals.send_refund_notification', side_effect=Exception("Splat!")) - @mock.patch('commerce.signals.log.warning') + @mock.patch('lms.djangoapps.commerce.signals.send_refund_notification', side_effect=Exception("Splat!")) + @mock.patch('lms.djangoapps.commerce.signals.log.warning') def test_notification_error(self, mock_log_warning, mock_send_notification): """ Ensure an error occuring during notification does not break program diff --git a/lms/djangoapps/commerce/tests/test_utils.py b/lms/djangoapps/commerce/tests/test_utils.py index 145332fa80..fc2c08297b 100644 --- a/lms/djangoapps/commerce/tests/test_utils.py +++ b/lms/djangoapps/commerce/tests/test_utils.py @@ -9,11 +9,12 @@ from django.test.utils import override_settings from mock import patch from waffle.testutils import override_switch -from commerce.models import CommerceConfiguration -from commerce.utils import EcommerceService from openedx.core.lib.log_utils import audit_log from student.tests.factories import UserFactory +from ..models import CommerceConfiguration +from ..utils import EcommerceService + def update_commerce_config(enabled=False, checkout_page='/test_basket/'): """ Enable / Disable CommerceConfiguration model """ diff --git a/lms/djangoapps/commerce/tests/test_views.py b/lms/djangoapps/commerce/tests/test_views.py index a2ec722840..601c13a743 100644 --- a/lms/djangoapps/commerce/tests/test_views.py +++ b/lms/djangoapps/commerce/tests/test_views.py @@ -109,7 +109,7 @@ class ReceiptViewTests(UserMixin, ModuleStoreTestCase): self.assertRegexpMatches(response.content, expected_pattern) @ddt.data(True, False) - @mock.patch('commerce.views.is_user_payment_error') + @mock.patch('lms.djangoapps.commerce.views.is_user_payment_error') def test_cybersource_message(self, is_user_message_expected, mock_is_user_payment_error): """ Ensure that the page displays the right message for the reason_code (it diff --git a/lms/djangoapps/commerce/urls.py b/lms/djangoapps/commerce/urls.py index b8c3774f82..eb0ded876a 100644 --- a/lms/djangoapps/commerce/urls.py +++ b/lms/djangoapps/commerce/urls.py @@ -3,7 +3,7 @@ Defines the URL routes for this app. """ from django.conf.urls import url -from commerce import views +from . import views urlpatterns = [ url(r'^checkout/cancel/$', views.checkout_cancel, name='checkout_cancel'), diff --git a/lms/djangoapps/commerce/utils.py b/lms/djangoapps/commerce/utils.py index daf83e4d0b..30eb070fd5 100644 --- a/lms/djangoapps/commerce/utils.py +++ b/lms/djangoapps/commerce/utils.py @@ -5,10 +5,11 @@ from urlparse import urljoin import waffle from django.conf import settings from django.core.urlresolvers import reverse + +from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from student.models import CourseEnrollment -from commerce.models import CommerceConfiguration -from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers +from .models import CommerceConfiguration def is_account_activation_requirement_disabled(): diff --git a/lms/djangoapps/commerce/views.py b/lms/djangoapps/commerce/views.py index 5f41736f07..a9047e6083 100644 --- a/lms/djangoapps/commerce/views.py +++ b/lms/djangoapps/commerce/views.py @@ -10,7 +10,6 @@ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from opaque_keys.edx.locator import CourseLocator -from commerce.models import CommerceConfiguration from course_modes.models import CourseMode from edxmako.shortcuts import render_to_response from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification @@ -20,6 +19,8 @@ from shoppingcart.processors.CyberSource2 import is_user_payment_error from student.models import CourseEnrollment from util.json_request import JsonResponse +from .models import CommerceConfiguration + log = logging.getLogger(__name__) diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py index 052cea4f83..ce6f575dd8 100644 --- a/lms/djangoapps/courseware/tests/test_date_summary.py +++ b/lms/djangoapps/courseware/tests/test_date_summary.py @@ -12,23 +12,23 @@ from mock import patch from nose.plugins.attrib import attr from pytz import utc -from commerce.models import CommerceConfiguration from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.courses import get_course_date_blocks from courseware.date_summary import ( + CertificateAvailableDate, CourseEndDate, CourseStartDate, TodaysDate, VerificationDeadlineDate, - VerifiedUpgradeDeadlineDate, - CertificateAvailableDate + VerifiedUpgradeDeadlineDate ) from courseware.models import ( CourseDynamicUpgradeDeadlineConfiguration, DynamicUpgradeDeadlineConfiguration, OrgDynamicUpgradeDeadlineConfiguration ) +from lms.djangoapps.commerce.models import CommerceConfiguration from lms.djangoapps.verify_student.models import VerificationDeadline from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory from openedx.core.djangoapps.content.course_overviews.models import CourseOverview @@ -38,8 +38,8 @@ from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory from openedx.core.djangoapps.user_api.preferences.api import set_user_preference from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag -from openedx.features.course_experience import CourseHomeMessages, UNIFIED_COURSE_TAB_FLAG, UPGRADE_DEADLINE_MESSAGE -from student.tests.factories import CourseEnrollmentFactory, UserFactory, TEST_PASSWORD +from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, UPGRADE_DEADLINE_MESSAGE, CourseHomeMessages +from student.tests.factories import TEST_PASSWORD, CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 0056b6cfc5..eca079f730 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -35,7 +35,6 @@ from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory from certificates import api as certs_api from certificates.models import CertificateGenerationConfiguration, CertificateStatuses from certificates.tests.factories import CertificateInvalidationFactory, GeneratedCertificateFactory -from commerce.models import CommerceConfiguration from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.access_utils import check_course_open_for_learner @@ -45,6 +44,7 @@ from courseware.tests.factories import GlobalStaffFactory, StudentModuleFactory from courseware.testutils import RenderXBlockTestMixin from courseware.url_helpers import get_redirect_url from courseware.user_state_client import DjangoXBlockUserStateClient +from lms.djangoapps.commerce.models import CommerceConfiguration from lms.djangoapps.commerce.utils import EcommerceService # pylint: disable=import-error from lms.djangoapps.grades.config.waffle import waffle as grades_waffle from lms.djangoapps.grades.config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 1c1ee58e75..92587bf02d 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -8,12 +8,38 @@ from collections import OrderedDict, namedtuple from datetime import datetime import analytics +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import AnonymousUser, User +from django.core.exceptions import PermissionDenied +from django.core.urlresolvers import reverse +from django.db import transaction +from django.db.models import Q +from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, QueryDict +from django.shortcuts import redirect +from django.template.context_processors import csrf +from django.utils.decorators import method_decorator +from django.utils.http import urlquote_plus +from django.utils.text import slugify +from django.utils.translation import ugettext as _ +from django.views.decorators.cache import cache_control +from django.views.decorators.csrf import ensure_csrf_cookie +from django.views.decorators.http import require_GET, require_http_methods, require_POST +from django.views.generic import View +from eventtracking import tracker +from ipware.ip import get_ip +from markupsafe import escape +from opaque_keys import InvalidKeyError +from opaque_keys.edx.keys import CourseKey, UsageKey +from pytz import UTC +from rest_framework import status +from web_fragments.fragment import Fragment + import shoppingcart import survey.views from certificates import api as certs_api from certificates.models import CertificateStatuses -from commerce.utils import EcommerceService -from course_modes.models import (CourseMode, get_course_prices) +from course_modes.models import CourseMode, get_course_prices from courseware.access import has_access, has_ccx_coach_role from courseware.access_utils import check_course_open_for_learner from courseware.courses import ( @@ -34,47 +60,24 @@ from courseware.model_data import FieldDataCache from courseware.models import BaseStudentModuleHistory, StudentModule from courseware.url_helpers import get_redirect_url from courseware.user_state_client import DjangoXBlockUserStateClient -from django.conf import settings -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import AnonymousUser, User -from django.template.context_processors import csrf -from django.core.exceptions import PermissionDenied -from django.core.urlresolvers import reverse -from django.db import transaction -from django.db.models import Q -from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, QueryDict -from django.shortcuts import redirect -from django.utils.decorators import method_decorator -from django.utils.http import urlquote_plus -from django.utils.text import slugify -from pytz import UTC -from django.utils.translation import ugettext as _ -from django.views.decorators.cache import cache_control -from django.views.decorators.csrf import ensure_csrf_cookie -from django.views.decorators.http import require_GET, require_http_methods, require_POST -from django.views.generic import View from edxmako.shortcuts import marketing_link, render_to_response, render_to_string from enrollment.api import add_enrollment -from eventtracking import tracker -from ipware.ip import get_ip from lms.djangoapps.ccx.custom_exception import CCXLocatorValidationException +from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.courseware.exceptions import CourseAccessRedirect, Redirect from lms.djangoapps.experiments.utils import get_experiment_user_metadata_context from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory from lms.djangoapps.instructor.enrollment import uses_shib from lms.djangoapps.instructor.views.api import require_global_staff from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification -from markupsafe import escape -from opaque_keys import InvalidKeyError -from opaque_keys.edx.keys import CourseKey, UsageKey from openedx.core.djangoapps.catalog.utils import get_programs, get_programs_with_type +from openedx.core.djangoapps.certificates import api as auto_certs_api from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.credit.api import ( get_credit_requirement_status, is_credit_course, is_user_eligible_for_credit ) -from openedx.core.djangoapps.certificates import api as auto_certs_api from openedx.core.djangoapps.models.course_details import CourseDetails from openedx.core.djangoapps.monitoring_utils import set_custom_metrics_for_course_key from openedx.core.djangoapps.plugin_api.views import EdxFragmentView @@ -87,14 +90,12 @@ from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, course_h from openedx.features.course_experience.course_tools import CourseToolsPluginManager from openedx.features.course_experience.views.course_dates import CourseDatesFragmentView from openedx.features.enterprise_support.api import data_sharing_consent_required -from rest_framework import status from shoppingcart.utils import is_shopping_cart_enabled from student.models import CourseEnrollment, UserTestGroup from util.cache import cache, cache_if_anonymous from util.db import outer_atomic from util.milestones_helpers import get_prerequisite_courses_display from util.views import _record_feedback_in_zendesk, ensure_valid_course_key, ensure_valid_usage_key -from web_fragments.fragment import Fragment from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem from xmodule.tabs import CourseTabList diff --git a/lms/djangoapps/instructor/services.py b/lms/djangoapps/instructor/services.py index 5e7e4fadf6..55442edf11 100644 --- a/lms/djangoapps/instructor/services.py +++ b/lms/djangoapps/instructor/services.py @@ -10,8 +10,8 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey import lms.djangoapps.instructor.enrollment as enrollment -from commerce.signals import create_zendesk_ticket from courseware.models import StudentModule +from lms.djangoapps.commerce.signals import create_zendesk_ticket from lms.djangoapps.instructor.views.tools import get_student_from_identifier from student import auth from student.roles import CourseStaffRole diff --git a/lms/djangoapps/learner_dashboard/views.py b/lms/djangoapps/learner_dashboard/views.py index cb58623f9a..34065d2d52 100644 --- a/lms/djangoapps/learner_dashboard/views.py +++ b/lms/djangoapps/learner_dashboard/views.py @@ -5,7 +5,7 @@ from django.http import Http404 from django.views.decorators.http import require_GET from edxmako.shortcuts import render_to_response -from commerce.utils import EcommerceService +from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.learner_dashboard.programs import ProgramsFragmentView from lms.djangoapps.learner_dashboard.utils import FAKE_COURSE_KEY, strip_course_id diff --git a/lms/djangoapps/student_account/test/test_views.py b/lms/djangoapps/student_account/test/test_views.py index 767ec1242b..c119475b5c 100644 --- a/lms/djangoapps/student_account/test/test_views.py +++ b/lms/djangoapps/student_account/test/test_views.py @@ -20,6 +20,7 @@ from django.test import TestCase from django.test.utils import override_settings from edx_oauth2_provider.tests.factories import AccessTokenFactory, ClientFactory, RefreshTokenFactory from edx_rest_api_client import exceptions +from http.cookies import SimpleCookie from nose.plugins.attrib import attr from oauth2_provider.models import AccessToken as dot_access_token from oauth2_provider.models import RefreshToken as dot_refresh_token @@ -27,11 +28,10 @@ from provider.oauth2.models import AccessToken as dop_access_token from provider.oauth2.models import RefreshToken as dop_refresh_token from testfixtures import LogCapture -from commerce.models import CommerceConfiguration -from commerce.tests import factories -from commerce.tests.mocks import mock_get_orders from course_modes.models import CourseMode -from http.cookies import SimpleCookie +from lms.djangoapps.commerce.models import CommerceConfiguration +from lms.djangoapps.commerce.tests import factories +from lms.djangoapps.commerce.tests.mocks import mock_get_orders from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin diff --git a/lms/djangoapps/student_account/views.py b/lms/djangoapps/student_account/views.py index e454cc12df..08d724e514 100644 --- a/lms/djangoapps/student_account/views.py +++ b/lms/djangoapps/student_account/views.py @@ -18,8 +18,8 @@ from django.views.decorators.http import require_http_methods from django_countries import countries import third_party_auth -from commerce.models import CommerceConfiguration from edxmako.shortcuts import render_to_response +from lms.djangoapps.commerce.models import CommerceConfiguration from lms.djangoapps.commerce.utils import EcommerceService from openedx.core.djangoapps.commerce.utils import ecommerce_api_client from openedx.core.djangoapps.external_auth.login_and_register import login as external_auth_login diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 3ff2b7b458..6186a3bd87 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -28,14 +28,14 @@ from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import CourseLocator from waffle.testutils import override_switch -from commerce.models import CommerceConfiguration -from commerce.tests import TEST_API_URL, TEST_PAYMENT_DATA, TEST_PUBLIC_URL_ROOT from common.test.utils import XssTestMixin from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory +from lms.djangoapps.commerce.models import CommerceConfiguration +from lms.djangoapps.commerce.tests import TEST_API_URL, TEST_PAYMENT_DATA, TEST_PUBLIC_URL_ROOT +from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline from lms.djangoapps.verify_student.views import PayAndVerifyView, checkout_with_ecommerce_service, render_to_response -from commerce.utils import EcommerceService from openedx.core.djangoapps.embargo.test_utils import restrict_course from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme from openedx.core.djangoapps.user_api.accounts.api import get_account_settings diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index 5c305873b1..6d3f5183c1 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -25,15 +25,15 @@ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from django.views.generic.base import View from edx_rest_api_client.exceptions import SlumberBaseException +from eventtracking import tracker from ipware.ip import get_ip from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from pytz import UTC -from commerce.utils import EcommerceService, is_account_activation_requirement_disabled from course_modes.models import CourseMode from edxmako.shortcuts import render_to_response, render_to_string -from eventtracking import tracker +from lms.djangoapps.commerce.utils import EcommerceService, is_account_activation_requirement_disabled from lms.djangoapps.verify_student.image import InvalidImageData, decode_image_data from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline from lms.djangoapps.verify_student.ssencrypt import has_valid_signature diff --git a/lms/envs/common.py b/lms/envs/common.py index a6f284e3ad..058aecaec4 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2243,7 +2243,7 @@ INSTALLED_APPS = [ 'corsheaders', 'openedx.core.djangoapps.cors_csrf', - 'commerce', + 'lms.djangoapps.commerce.apps.CommerceConfig', # Credit courses 'openedx.core.djangoapps.credit.apps.CreditConfig', diff --git a/lms/urls.py b/lms/urls.py index e501ef68a3..55c262eb77 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -847,7 +847,7 @@ if configuration_helpers.get_value('ENABLE_BULK_ENROLLMENT_VIEW', settings.FEATU # Shopping cart urlpatterns += [ url(r'^shoppingcart/', include('shoppingcart.urls')), - url(r'^commerce/', include('commerce.urls', namespace='commerce')), + url(r'^commerce/', include('lms.djangoapps.commerce.urls', namespace='commerce')), ] # Course goals diff --git a/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py index b98f65c7d6..7ab9d5b405 100644 --- a/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py +++ b/openedx/core/djangoapps/schedules/management/commands/tests/send_email_base.py @@ -1,33 +1,32 @@ -from collections import namedtuple, defaultdict -from copy import deepcopy import datetime -import ddt import logging +from collections import defaultdict, namedtuple +from copy import deepcopy import attr +import ddt +import pytz from django.conf import settings +from edx_ace.channel import ChannelType +from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies +from edx_ace.utils.date import serialize from freezegun import freeze_time from mock import Mock, patch -import pytz +from opaque_keys.edx.keys import CourseKey -from commerce.models import CommerceConfiguration from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.models import DynamicUpgradeDeadlineConfiguration -from edx_ace.channel import ChannelType -from edx_ace.utils.date import serialize -from edx_ace.test_utils import StubPolicy, patch_channels, patch_policies -from opaque_keys.edx.keys import CourseKey -from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory +from lms.djangoapps.commerce.models import CommerceConfiguration from openedx.core.djangoapps.schedules import resolvers, tasks from openedx.core.djangoapps.schedules.resolvers import _get_datetime_beginning_of_day from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory, ScheduleFactory +from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES -from openedx.core.djangolib.testing.utils import FilteredQueryCountMixin, CacheIsolationTestCase +from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, FilteredQueryCountMixin from student.models import CourseEnrollment from student.tests.factories import UserFactory - SITE_QUERY = 1 # django_site SITE_CONFIG_QUERY = 1 # site_configuration_siteconfiguration diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py index 87c62e1c86..e1cc09caff 100644 --- a/openedx/features/course_experience/tests/views/test_course_home.py +++ b/openedx/features/course_experience/tests/views/test_course_home.py @@ -14,11 +14,11 @@ from pytz import UTC from waffle.models import Flag from waffle.testutils import override_flag -from commerce.models import CommerceConfiguration -from commerce.utils import EcommerceService -from lms.djangoapps.course_goals.api import add_course_goal, remove_course_goal from course_modes.models import CourseMode from courseware.tests.factories import StaffFactory +from lms.djangoapps.commerce.models import CommerceConfiguration +from lms.djangoapps.commerce.utils import EcommerceService +from lms.djangoapps.course_goals.api import add_course_goal, remove_course_goal from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES, override_waffle_flag from openedx.features.course_experience import ( SHOW_REVIEWS_TOOL_FLAG, @@ -26,14 +26,15 @@ from openedx.features.course_experience import ( UNIFIED_COURSE_TAB_FLAG ) from student.models import CourseEnrollment -from student.tests.factories import UserFactory, CourseEnrollmentFactory +from student.tests.factories import CourseEnrollmentFactory, UserFactory from util.date_utils import strftime_localized from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import CourseUserType, ModuleStoreTestCase, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls + +from ... import COURSE_PRE_START_ACCESS_FLAG, ENABLE_COURSE_GOALS from .helpers import add_course_mode from .test_course_updates import create_course_update, remove_course_updates -from ... import COURSE_PRE_START_ACCESS_FLAG, ENABLE_COURSE_GOALS TEST_PASSWORD = 'test' TEST_CHAPTER_NAME = 'Test Chapter' diff --git a/openedx/features/course_experience/tests/views/test_course_sock.py b/openedx/features/course_experience/tests/views/test_course_sock.py index cc31250d45..a2992cf638 100644 --- a/openedx/features/course_experience/tests/views/test_course_sock.py +++ b/openedx/features/course_experience/tests/views/test_course_sock.py @@ -4,11 +4,11 @@ Tests for course verification sock import ddt -from commerce.models import CommerceConfiguration from course_modes.models import CourseMode +from lms.djangoapps.commerce.models import CommerceConfiguration from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG -from student.tests.factories import UserFactory, CourseEnrollmentFactory +from student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory diff --git a/openedx/features/course_experience/views/course_home.py b/openedx/features/course_experience/views/course_home.py index dff93ed7f4..b7c9232706 100644 --- a/openedx/features/course_experience/views/course_home.py +++ b/openedx/features/course_experience/views/course_home.py @@ -2,30 +2,31 @@ Views for the course home page. """ -from django.template.context_processors import csrf from django.core.urlresolvers import reverse +from django.template.context_processors import csrf from django.template.loader import render_to_string from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_control from django.views.decorators.csrf import ensure_csrf_cookie +from opaque_keys.edx.keys import CourseKey +from web_fragments.fragment import Fragment -from commerce.utils import EcommerceService from course_modes.models import get_cosmetic_verified_display_price from courseware.access import has_access -from courseware.courses import ( - can_self_enroll_in_course, - get_course_info_section, - get_course_with_access, +from courseware.courses import can_self_enroll_in_course, get_course_info_section, get_course_with_access +from lms.djangoapps.commerce.utils import EcommerceService +from lms.djangoapps.course_goals.api import ( + get_course_goal, + get_course_goal_options, + get_goal_api_url, + has_course_goal_permission ) -from lms.djangoapps.course_goals.api import get_course_goal, has_course_goal_permission, get_course_goal_options, get_goal_api_url from lms.djangoapps.courseware.exceptions import CourseAccessRedirect from lms.djangoapps.courseware.views.views import CourseTabView -from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.plugin_api.views import EdxFragmentView from openedx.features.course_experience.course_tools import CourseToolsPluginManager from student.models import CourseEnrollment from util.views import ensure_valid_course_key -from web_fragments.fragment import Fragment from .. import LATEST_UPDATE_FLAG, SHOW_UPGRADE_MSG_ON_COURSE_HOME, USE_BOOTSTRAP_FLAG from ..utils import get_course_outline_block_tree