Add commerce AppConfig and use to register signals. Move imports.

Add explicit 'lms.djangoapps' prefix to all commerce imports, as another
commerce Django app exists at openedx.core.djangoapps.commerce
This commit is contained in:
John Eskew
2017-11-03 14:36:56 -04:00
parent ac3642ad05
commit 6672f64188
41 changed files with 94 additions and 81 deletions

View File

@@ -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):

View File

@@ -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'),

View File

@@ -1,3 +0,0 @@
""" Commerce app. """
# this is here to support registering the signals in signals.py
from commerce import signals

View File

@@ -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)

View File

@@ -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')),
]

View File

@@ -14,12 +14,13 @@ 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 ..views import SAILTHRU_CAMPAIGN_COOKIE
from ....constants import Messages
from ....tests.mocks import mock_basket_order
from ....tests.test_views import UserMixin
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
@@ -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):

View File

@@ -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'),

View File

@@ -9,12 +9,12 @@ 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
from enrollment.views import EnrollmentCrossDomainSessionAuth
from ...constants import Messages
from ...http import DetailResponse
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
from openedx.core.djangoapps.embargo import api as embargo_api
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in

View File

@@ -3,7 +3,7 @@ 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 ...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

View File

@@ -7,8 +7,8 @@ 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 .models import Course
from xmodule.modulestore.django import modulestore

View File

@@ -2,8 +2,8 @@
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

View File

@@ -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):

View File

@@ -14,9 +14,9 @@ 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 ....tests.mocks import mock_order_endpoint
from ....tests.test_views import UserMixin
from lms.djangoapps.verify_student.models import VerificationDeadline
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase

View File

@@ -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'),

View File

@@ -10,11 +10,11 @@ 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 .models import Course
from .permissions import ApiKeyOrModelPermission, IsAuthenticatedOrActivationOverridden
from .serializers import CourseSerializer
from ...utils import is_account_activation_requirement_disabled
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
from openedx.core.lib.api.mixins import PutAsCreateMixin
from util.json_request import JsonResponse

View 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

View File

@@ -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

View File

@@ -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):

View File

@@ -10,9 +10,12 @@ 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 .models import CommerceConfiguration
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
@@ -28,10 +31,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 +83,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

View File

@@ -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

View File

@@ -18,11 +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 ..models import CommerceConfiguration
from ..signals import (
create_zendesk_ticket,
generate_refund_notification_body,
send_refund_notification
)
from . import JSON
from .mocks import mock_create_refund, mock_process_refund
from student.signals import REFUND_ORDER
from student.tests.factories import CourseEnrollmentFactory, UserFactory
@@ -76,11 +80,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 +104,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 +135,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 +145,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 +160,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 +174,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 +189,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 +199,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 +220,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

View File

@@ -9,8 +9,8 @@ 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 ..models import CommerceConfiguration
from ..utils import EcommerceService
from openedx.core.lib.log_utils import audit_log
from student.tests.factories import UserFactory

View File

@@ -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

View File

@@ -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'),

View File

@@ -7,7 +7,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from student.models import CourseEnrollment
from commerce.models import CommerceConfiguration
from .models import CommerceConfiguration
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers

View File

@@ -10,9 +10,9 @@ 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 .models import CommerceConfiguration
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.theming.helpers import is_request_in_themed_site

View File

@@ -12,7 +12,7 @@ from mock import patch
from nose.plugins.attrib import attr
from pytz import utc
from commerce.models import CommerceConfiguration
from lms.djangoapps.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

View File

@@ -35,7 +35,7 @@ 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 lms.djangoapps.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

View File

@@ -12,7 +12,7 @@ import shoppingcart
import survey.views
from certificates import api as certs_api
from certificates.models import CertificateStatuses
from commerce.utils import EcommerceService
from lms.djangoapps.commerce.utils import EcommerceService
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

View File

@@ -10,7 +10,7 @@ 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 lms.djangoapps.commerce.signals import create_zendesk_ticket
from courseware.models import StudentModule
from lms.djangoapps.instructor.views.tools import get_student_from_identifier
from student import auth

View File

@@ -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

View File

@@ -27,9 +27,9 @@ 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 lms.djangoapps.commerce.models import CommerceConfiguration
from lms.djangoapps.commerce.tests import factories
from lms.djangoapps.commerce.tests.mocks import mock_get_orders
from course_modes.models import CourseMode
from http.cookies import SimpleCookie
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories

View File

@@ -18,7 +18,7 @@ from django.views.decorators.http import require_http_methods
from django_countries import countries
import third_party_auth
from commerce.models import CommerceConfiguration
from lms.djangoapps.commerce.models import CommerceConfiguration
from edxmako.shortcuts import render_to_response
from lms.djangoapps.commerce.utils import EcommerceService
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client

View File

@@ -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 lms.djangoapps.commerce.models import CommerceConfiguration
from lms.djangoapps.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.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 lms.djangoapps.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

View File

@@ -30,7 +30,7 @@ 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 lms.djangoapps.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

View File

@@ -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',

View File

@@ -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

View File

@@ -10,7 +10,7 @@ from freezegun import freeze_time
from mock import Mock, patch
import pytz
from commerce.models import CommerceConfiguration
from lms.djangoapps.commerce.models import CommerceConfiguration
from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory
from courseware.models import DynamicUpgradeDeadlineConfiguration

View File

@@ -14,8 +14,8 @@ 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.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 course_modes.models import CourseMode
from courseware.tests.factories import StaffFactory

View File

@@ -4,7 +4,7 @@ Tests for course verification sock
import ddt
from commerce.models import CommerceConfiguration
from lms.djangoapps.commerce.models import CommerceConfiguration
from course_modes.models import CourseMode
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG

View File

@@ -9,7 +9,7 @@ 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 commerce.utils import EcommerceService
from lms.djangoapps.commerce.utils import EcommerceService
from course_modes.models import get_cosmetic_verified_display_price
from courseware.access import has_access
from courseware.courses import (