Merge pull request #16449 from edx/jeskew/commerce_to_appconfig
Add commerce AppConfig and use to register signals.
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
""" Commerce app. """
|
||||
# this is here to support registering the signals in signals.py
|
||||
from commerce import signals
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')),
|
||||
]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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. """
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
|
||||
17
lms/djangoapps/commerce/apps.py
Normal file
17
lms/djangoapps/commerce/apps.py
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 """
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user