コード例 #1
0
ファイル: views.py プロジェクト: mitodl/micromasters
    def post(self, request):
        """
        Audit enrolls the user in a course in edx
        """
        course_id = request.data.get('course_id')
        if course_id is None:
            raise ValidationError('course id missing in the request')
        # get the credentials for the current user for edX
        user_social = get_social_auth(request.user)
        try:
            utils.refresh_user_token(user_social)
        except utils.InvalidCredentialStored as exc:
            log.error(
                "Error while refreshing credentials for user %s",
                get_social_username(request.user),
            )
            return Response(
                status=exc.http_status_code,
                data={'error': str(exc)}
            )

        # create an instance of the client to query edX
        edx_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL)

        try:
            enrollment = edx_client.enrollments.create_audit_student_enrollment(course_id)
        except HTTPError as exc:
            if exc.response.status_code == status.HTTP_400_BAD_REQUEST:
                raise PossiblyImproperlyConfigured(
                    'Got a 400 status code from edX server while trying to create '
                    'audit enrollment. This might happen if the course is improperly '
                    'configured on MicroMasters. Course key '
                    '{course_key}, edX user "{edX_user}"'.format(
                        edX_user=get_social_username(request.user),
                        course_key=course_id,
                    )
                )
            log.error(
                "Http error from edX while creating audit enrollment for course key %s for edX user %s",
                course_id,
                get_social_username(request.user),
            )
            return Response(
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                data={'error': str(exc)}
            )
        except Exception as exc:  # pylint: disable=broad-except
            log.exception(
                "Error creating audit enrollment for course key %s for edX user %s",
                course_id,
                get_social_username(request.user),
            )
            return Response(
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                data={'error': str(exc)}
            )
        CachedEdxDataApi.update_cached_enrollment(request.user, enrollment, enrollment.course_id, index_user=True)
        return Response(
            data=enrollment.json
        )
コード例 #2
0
    def post(self, request):
        """
        Audit enrolls the user in a course in edx
        """
        course_id = request.data.get('course_id')
        if course_id is None:
            raise ValidationError('course id missing in the request')
        # get the credentials for the current user for edX
        user_social = get_social_auth(request.user)
        try:
            utils.refresh_user_token(user_social)
        except utils.InvalidCredentialStored as exc:
            log.error(
                "Error while refreshing credentials for user %s",
                get_social_username(request.user),
            )
            return Response(
                status=exc.http_status_code,
                data={'error': str(exc)}
            )

        # create an instance of the client to query edX
        edx_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL)

        try:
            enrollment = edx_client.enrollments.create_audit_student_enrollment(course_id)
        except HTTPError as exc:
            if exc.response.status_code == status.HTTP_400_BAD_REQUEST:
                raise PossiblyImproperlyConfigured(
                    'Got a 400 status code from edX server while trying to create '
                    'audit enrollment. This might happen if the course is improperly '
                    'configured on MicroMasters. Course key '
                    '{course_key}, edX user "{edX_user}"'.format(
                        edX_user=get_social_username(request.user),
                        course_key=course_id,
                    )
                )
            log.error(
                "Http error from edX while creating audit enrollment for course key %s for edX user %s",
                course_id,
                get_social_username(request.user),
            )
            return Response(
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                data={'error': str(exc)}
            )
        except Exception as exc:  # pylint: disable=broad-except
            log.exception(
                "Error creating audit enrollment for course key %s for edX user %s",
                course_id,
                get_social_username(request.user),
            )
            return Response(
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                data={'error': str(exc)}
            )
        CachedEdxDataApi.update_cached_enrollment(request.user, enrollment, enrollment.course_id, index_user=True)
        return Response(
            data=enrollment.json
        )
コード例 #3
0
    def test_check_edx_verified_email(self, is_active, mocked_get_json):
        """
        User should be directed to error page if user is unverified on edX
        """
        mock_strategy = mock.Mock()
        backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy)
        mocked_content = dict(self.mocked_edx_profile)
        mocked_content['is_active'] = is_active
        mocked_get_json.return_value = mocked_content
        result = pipeline_api.check_edx_verified_email(
            backend,
            {'access_token': 'foo_token'},
            {'username': get_social_username(self.user)}
        )

        mocked_get_json.assert_called_once_with(
            urljoin(
                edxorg.EdxOrgOAuth2.EDXORG_BASE_URL,
                '/api/user/v1/accounts/{0}'.format(get_social_username(self.user))
            ),
            headers={'Authorization': 'Bearer foo_token'}
        )
        if is_active:
            assert 'edx_profile' in result
        else:
            self.assertEqual(result.status_code, 302)
コード例 #4
0
ファイル: models.py プロジェクト: mitodl/micromasters
    def get_context(self, request, *args, **kwargs):
        programs = Program.objects.filter(live=True).select_related('programpage').order_by("id")
        js_settings = {
            "gaTrackingID": settings.GA_TRACKING_ID,
            "host": webpack_dev_server_host(request),
            "environment": settings.ENVIRONMENT,
            "sentry_dsn": sentry.get_public_dsn(),
            "release_version": settings.VERSION
        }

        username = get_social_username(request.user)
        context = super(HomePage, self).get_context(request)

        def get_program_page(program):
            """Return a None if ProgramPage does not exist, to avoid template errors"""
            try:
                return program.programpage
            except ProgramPage.DoesNotExist:
                return None

        program_pairs = [(program, get_program_page(program)) for program in programs]
        context["programs"] = program_pairs
        context["is_public"] = True
        context["has_zendesk_widget"] = True
        context["google_maps_api"] = False
        context["authenticated"] = not request.user.is_anonymous
        context["is_staff"] = has_role(request.user, [Staff.ROLE_ID, Instructor.ROLE_ID])
        context["username"] = username
        context["js_settings_json"] = json.dumps(js_settings)
        context["title"] = self.title
        context["ga_tracking_id"] = ""
        context["coupon_code"] = get_coupon_code(request)

        return context
コード例 #5
0
ファイル: models.py プロジェクト: mitodl/micromasters
def get_program_page_context(programpage, request):
    """ Get context for the program page"""
    from cms.serializers import ProgramPageSerializer

    courses_query = (
        programpage.program.course_set.all()
    )
    js_settings = {
        "gaTrackingID": settings.GA_TRACKING_ID,
        "host": webpack_dev_server_host(request),
        "environment": settings.ENVIRONMENT,
        "sentry_dsn": sentry.get_public_dsn(),
        "release_version": settings.VERSION,
        "user": serialize_maybe_user(request.user),
        "program": ProgramPageSerializer(programpage).data,
    }
    username = get_social_username(request.user)
    context = super(ProgramPage, programpage).get_context(request)

    context["is_staff"] = has_role(request.user, [Staff.ROLE_ID, Instructor.ROLE_ID])
    context["is_public"] = True
    context["has_zendesk_widget"] = True
    context["google_maps_api"] = False
    context["authenticated"] = not request.user.is_anonymous
    context["username"] = username
    context["js_settings_json"] = json.dumps(js_settings)
    context["title"] = programpage.title
    context["courses"] = courses_query
    context["ga_tracking_id"] = programpage.program.ga_tracking_id

    return context
コード例 #6
0
 def get_username(self, obj):
     """
     Look up the user's username on edX.
     We do *not* use the `user.username` field, because the Javascript
     doesn't need to know anything about that.
     """
     return get_social_username(obj)
コード例 #7
0
 def test_limited(self):
     """
     Test limited serializer
     """
     profile = self.create_profile()
     data = ProfileLimitedSerializer(profile).data
     assert data == {
         'username': get_social_username(profile.user),
         'first_name': profile.first_name,
         'last_name': profile.last_name,
         'full_name': profile.full_name,
         'preferred_name': profile.preferred_name,
         'gender': profile.gender,
         'account_privacy': profile.account_privacy,
         'country': profile.country,
         'state_or_territory': profile.state_or_territory,
         'city': profile.city,
         'birth_country': profile.birth_country,
         'preferred_language': profile.preferred_language,
         'edx_level_of_education': profile.edx_level_of_education,
         'education': EducationSerializer(profile.education.all(), many=True).data,
         'work_history': (
             EmploymentSerializer(profile.work_history.all(), many=True).data
         ),
         'image_medium': profile.image_medium.url,
         'about_me': profile.about_me,
         'romanized_first_name': profile.romanized_first_name,
         'romanized_last_name': profile.romanized_last_name,
     }
コード例 #8
0
ファイル: models.py プロジェクト: guaranta/micromasters
def get_program_page_context(programpage, request):
    """ Get context for the program page"""
    from cms.serializers import ProgramPageSerializer

    courses_query = (programpage.program.course_set.all())
    js_settings = {
        "gaTrackingID": settings.GA_TRACKING_ID,
        "host": webpack_dev_server_host(request),
        "environment": settings.ENVIRONMENT,
        "sentry_dsn": settings.SENTRY_DSN,
        "release_version": settings.VERSION,
        "user": serialize_maybe_user(request.user),
        "program": ProgramPageSerializer(programpage).data,
    }
    username = get_social_username(request.user)
    context = super(ProgramPage, programpage).get_context(request)

    context["is_staff"] = has_role(request.user,
                                   [Staff.ROLE_ID, Instructor.ROLE_ID])
    context["is_public"] = True
    context["has_zendesk_widget"] = True
    context["google_maps_api"] = False
    context["authenticated"] = not request.user.is_anonymous
    context["username"] = username
    context["js_settings_json"] = json.dumps(js_settings)
    context["title"] = programpage.title
    context["courses"] = courses_query
    context["ga_tracking_id"] = programpage.program.ga_tracking_id

    return context
コード例 #9
0
def generate_cybersource_sa_payload(order):
    """
    Generates a payload dict to send to CyberSource for Secure Acceptance

    Args:
        order (Order): An order
    Returns:
        dict: the payload to send to CyberSource via Secure Acceptance
    """
    # http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_WM/Secure_Acceptance_WM.pdf
    # Section: API Fields
    payload = {
        'access_key': settings.CYBERSOURCE_ACCESS_KEY,
        'amount': str(order.total_price_paid),
        'consumer_id': get_social_username(order.user),
        'currency': 'USD',
        'locale': 'en-us',
        # TODO
        'override_custom_cancel_page': 'https://micromasters.mit.edu?cancel',
        'override_custom_receipt_page': "https://micromasters.mit.edu?receipt",
        'reference_number': make_reference_id(order),
        'profile_id': settings.CYBERSOURCE_PROFILE_ID,
        'signed_date_time': datetime.utcnow().strftime(ISO_8601_FORMAT),
        'transaction_type': 'sale',
        'transaction_uuid': uuid.uuid4().hex,
        'unsigned_field_names': '',
    }

    field_names = sorted(list(payload.keys()) + ['signed_field_names'])
    payload['signed_field_names'] = ','.join(field_names)
    payload['signature'] = generate_cybersource_sa_signature(payload)

    return payload
コード例 #10
0
ファイル: views_test.py プロジェクト: mitodl/micromasters
    def test_index_context_logged_in_social_auth(self):
        """
        Assert context values when logged in as social auth user
        """
        profile = self.create_and_login_user()
        user = profile.user
        ga_tracking_id = FuzzyText().fuzz()
        with self.settings(
            GA_TRACKING_ID=ga_tracking_id,
        ), patch('ui.templatetags.render_bundle._get_bundle') as get_bundle:
            response = self.client.get('/')

            bundles = [bundle[0][1] for bundle in get_bundle.call_args_list]
            assert set(bundles) == {
                'common',
                'public',
                'sentry_client',
                'style',
                'style_public',
                'zendesk_widget',
            }

            assert response.context['authenticated'] is True
            assert response.context['username'] == get_social_username(user)
            assert response.context['title'] == HomePage.objects.first().title
            assert response.context['is_public'] is True
            assert response.context['has_zendesk_widget'] is True
            assert response.context['is_staff'] is False
            assert response.context['programs'] == []
            self.assertContains(response, 'Share this page')
            js_settings = json.loads(response.context['js_settings_json'])
            assert js_settings['gaTrackingID'] == ga_tracking_id
コード例 #11
0
ファイル: views.py プロジェクト: guaranta/micromasters
def standard_error_page(request, status_code, template_filename):
    """
    Returns an error page with a given template filename and provides necessary context variables
    """
    name = request.user.profile.preferred_name if not request.user.is_anonymous else ""
    authenticated = not request.user.is_anonymous
    username = get_social_username(request.user)
    response = render(
        request,
        template_filename,
        context={
            "has_zendesk_widget": True,
            "is_public": True,
            "js_settings_json": json.dumps({
                "release_version": settings.VERSION,
                "environment": settings.ENVIRONMENT,
                "sentry_dsn": settings.SENTRY_DSN,
                "user": serialize_maybe_user(request.user),
            }),
            "authenticated": authenticated,
            "name": name,
            "username": username,
            "is_staff": has_role(request.user, [Staff.ROLE_ID, Instructor.ROLE_ID]),
            "support_email": settings.EMAIL_SUPPORT,
            "sentry_dsn": settings.SENTRY_DSN,
        }
    )
    response.status_code = status_code
    return response
コード例 #12
0
ファイル: views.py プロジェクト: mitodl/micromasters
def standard_error_page(request, status_code, template_filename):
    """
    Returns an error page with a given template filename and provides necessary context variables
    """
    name = request.user.profile.preferred_name if not request.user.is_anonymous else ""
    authenticated = not request.user.is_anonymous
    username = get_social_username(request.user)
    response = render(
        request,
        template_filename,
        context={
            "has_zendesk_widget": True,
            "is_public": True,
            "js_settings_json": json.dumps({
                "release_version": settings.VERSION,
                "environment": settings.ENVIRONMENT,
                "sentry_dsn": sentry.get_public_dsn(),
                "user": serialize_maybe_user(request.user),
            }),
            "authenticated": authenticated,
            "name": name,
            "username": username,
            "is_staff": has_role(request.user, [Staff.ROLE_ID, Instructor.ROLE_ID]),
            "support_email": settings.EMAIL_SUPPORT,
            "sentry_dsn": sentry.get_public_dsn(),
        }
    )
    response.status_code = status_code
    return response
コード例 #13
0
    def test_dashboard_settings(self):
        """
        Assert settings we pass to dashboard
        """
        profile = self.create_and_login_user()
        user = profile.user

        ga_tracking_id = FuzzyText().fuzz()
        react_ga_debug = FuzzyText().fuzz()
        edx_base_url = FuzzyText().fuzz()
        host = FuzzyText().fuzz()
        with self.settings(
                GA_TRACKING_ID=ga_tracking_id,
                REACT_GA_DEBUG=react_ga_debug,
                EDXORG_BASE_URL=edx_base_url,
                WEBPACK_DEV_SERVER_HOST=host,
        ):
            resp = self.client.get(DASHBOARD_URL)
            js_settings = json.loads(resp.context['js_settings_json'])
            assert js_settings == {
                'gaTrackingID': ga_tracking_id,
                'reactGaDebug': react_ga_debug,
                'authenticated': True,
                'name': user.profile.preferred_name,
                'username': get_social_username(user),
                'host': host,
                'edx_base_url': edx_base_url,
                'roles': [],
                'search_url': reverse('search_api', kwargs={"elastic_url":
                                                            ""}),
            }
コード例 #14
0
ファイル: serializers.py プロジェクト: mitodl/micromasters
 def get_username(self, obj):
     """
     Look up the user's username on edX.
     We do *not* use the `user.username` field, because the Javascript
     doesn't need to know anything about that.
     """
     return get_social_username(obj)
コード例 #15
0
    def test_index_context_logged_in_social_auth(self):
        """
        Assert context values when logged in as social auth user
        """
        profile = self.create_and_login_user()
        user = profile.user
        ga_tracking_id = FuzzyText().fuzz()
        with self.settings(GA_TRACKING_ID=ga_tracking_id, ), patch(
                'ui.templatetags.render_bundle._get_bundle') as get_bundle:
            response = self.client.get('/')

            bundles = [bundle[0][1] for bundle in get_bundle.call_args_list]
            assert set(bundles) == {
                'common',
                'public',
                'sentry_client',
                'style',
                'style_public',
                'zendesk_widget',
            }

            assert response.context['authenticated'] is True
            assert response.context['username'] == get_social_username(user)
            assert response.context['title'] == HomePage.objects.first().title
            assert response.context['is_public'] is True
            assert response.context['has_zendesk_widget'] is True
            assert response.context['is_staff'] is False
            assert response.context['programs'] == []
            self.assertContains(response, 'Share this page')
            js_settings = json.loads(response.context['js_settings_json'])
            assert js_settings['gaTrackingID'] == ga_tracking_id
コード例 #16
0
    def get_context(self, request, *args, **kwargs):
        js_settings = {
            "gaTrackingID": settings.GA_TRACKING_ID,
            "host": webpack_dev_server_host(request),
            "environment": settings.ENVIRONMENT,
            "sentry_dsn": settings.SENTRY_DSN,
            "release_version": settings.VERSION
        }

        username = get_social_username(request.user)
        context = super().get_context(request)

        context["is_public"] = True
        context["has_zendesk_widget"] = True
        context["google_maps_api"] = False
        context["authenticated"] = not request.user.is_anonymous
        context["is_staff"] = has_role(request.user,
                                       [Staff.ROLE_ID, Instructor.ROLE_ID])
        context["username"] = username
        context["js_settings_json"] = json.dumps(js_settings)
        context["title"] = self.title
        context["ga_tracking_id"] = ""
        context["hubspot_portal_id"] = settings.HUBSPOT_CONFIG.get(
            "HUBSPOT_PORTAL_ID")
        context[
            "hubspot_ogranizations_form_guid"] = settings.HUBSPOT_CONFIG.get(
                "HUBSPOT_ORGANIZATIONS_FORM_GUID")

        return context
コード例 #17
0
ファイル: views_test.py プロジェクト: mitodl/micromasters
 def test_no_coupon(self):
     """
     A 404 should be returned if no coupon exists
     """
     resp = self.client.post(reverse('coupon-user-create', kwargs={'code': "missing"}), data={
         "username": get_social_username(self.user)
     }, format='json')
     assert resp.status_code == status.HTTP_404_NOT_FOUND
コード例 #18
0
ファイル: permissions.py プロジェクト: mitodl/micromasters
 def has_permission(self, request, view):
     """
     Returns true if the username in the request body matches the logged in user.
     """
     try:
         return request.data['username'] == get_social_username(request.user)
     except KeyError:
         return False
コード例 #19
0
 def test_anonymous_user(self):  # pylint: disable=no-self-use
     """
     get_social_username should return None for anonymous users
     """
     is_anonymous = Mock(return_value=True)
     user = Mock(is_anonymous=is_anonymous)
     assert get_social_username(user) is None
     assert is_anonymous.called
コード例 #20
0
 def has_permission(self, request, view):
     """
     Returns true if the username in the request body matches the logged in user.
     """
     try:
         return request.data['username'] == get_social_username(
             request.user)
     except KeyError:
         return False
コード例 #21
0
 def test_no_coupon(self):
     """
     A 404 should be returned if no coupon exists
     """
     resp = self.client.post(
         reverse('coupon-user-create', kwargs={'code': "missing"}),
         data={"username": get_social_username(self.user)},
         format='json')
     assert resp.status_code == status.HTTP_404_NOT_FOUND
コード例 #22
0
def generate_cybersource_sa_payload(order, dashboard_url):
    """
    Generates a payload dict to send to CyberSource for Secure Acceptance

    Args:
        order (Order): An order
        dashboard_url: (str): The absolute url for the dashboard
    Returns:
        dict: the payload to send to CyberSource via Secure Acceptance
    """
    # http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_WM/Secure_Acceptance_WM.pdf
    # Section: API Fields

    # Course key is used only to show the confirmation message to the user
    course_key = ""
    line = order.line_set.first()
    if line is not None:
        course_key = line.course_key
    course_run = CourseRun.objects.get(edx_course_key=course_key)

    # NOTE: be careful about max length here, many (all?) string fields have a max
    # length of 255. At the moment none of these fields should go over that, due to database
    # constraints or other reasons

    payload = {
        'access_key': settings.CYBERSOURCE_ACCESS_KEY,
        'amount': str(order.total_price_paid),
        'consumer_id': get_social_username(order.user),
        'currency': 'USD',
        'locale': 'en-us',
        'item_0_code': 'course',
        'item_0_name': '{}'.format(course_run.title),
        'item_0_quantity': 1,
        'item_0_sku': '{}'.format(course_key),
        'item_0_tax_amount': '0',
        'item_0_unit_price': str(order.total_price_paid),
        'line_item_count': 1,
        'override_custom_cancel_page': make_dashboard_receipt_url(dashboard_url, course_key, 'cancel'),
        'override_custom_receipt_page': make_dashboard_receipt_url(dashboard_url, course_key, 'receipt'),
        'reference_number': make_reference_id(order),
        'profile_id': settings.CYBERSOURCE_PROFILE_ID,
        'signed_date_time': now_in_utc().strftime(ISO_8601_FORMAT),
        'transaction_type': 'sale',
        'transaction_uuid': uuid.uuid4().hex,
        'unsigned_field_names': '',
        'merchant_defined_data1': 'course',
        'merchant_defined_data2': '{}'.format(course_run.title),
        'merchant_defined_data3': '{}'.format(course_key),
    }

    field_names = sorted(list(payload.keys()) + ['signed_field_names'])
    payload['signed_field_names'] = ','.join(field_names)
    payload['signature'] = generate_cybersource_sa_signature(payload)

    return payload
コード例 #23
0
ファイル: seed_db.py プロジェクト: mitodl/micromasters
def deserialize_dashboard_data(user, user_data, programs):
    """
    Deserializes enrollment/grade data for a user
    """
    fake_course_runs = CourseRun.objects.filter(
        course__program__in=programs
    ).select_related('course__program').all()
    social_username = get_social_username(user)
    enrollment_list = user_data.get('_enrollments', [])
    grade_list = user_data.get('_grades', [])
    deserialize_enrollment_data(user, social_username, fake_course_runs, enrollment_list)
    deserialize_grade_data(user, fake_course_runs, grade_list)
コード例 #24
0
def deserialize_dashboard_data(user, user_data, programs):
    """
    Deserializes enrollment/grade data for a user
    """
    fake_course_runs = CourseRun.objects.filter(
        course__program__in=programs).select_related('course__program').all()
    social_username = get_social_username(user)
    enrollment_list = user_data.get('_enrollments', [])
    grade_list = user_data.get('_grades', [])
    deserialize_enrollment_data(user, social_username, fake_course_runs,
                                enrollment_list)
    deserialize_grade_data(user, fake_course_runs, grade_list)
コード例 #25
0
    def test_two_social(self):
        """
        get_social_username should return None if there are two social edX accounts for a user
        """
        UserSocialAuthFactory.create(user=self.user, uid='other name')

        with LogCapture() as log_capture:
            assert get_social_username(self.user) is None
            log_capture.check((
                'profiles.api', 'ERROR',
                'Unexpected error retrieving social auth username: get() returned more than '
                'one UserSocialAuth -- it returned 2!'))
コード例 #26
0
def get_purchasable_course_run(course_key, user):
    """
    Gets a course run, or raises Http404 if not purchasable. To be purchasable a course run
    must not already be purchased, must be part of a live program, must be part of a program
    with financial aid enabled, with a financial aid object, and must have a valid price.

    Args:
        course_key (str):
            An edX course key
        user (User):
            The purchaser of the course run
    Returns:
        CourseRun: A course run
    """
    # Make sure it's connected to a live program, it has a valid price, and the user is enrolled in the program already
    try:
        course_run = get_object_or_404(
            CourseRun,
            edx_course_key=course_key,
            course__program__live=True,
            course__program__financial_aid_availability=True,
        )
    except Http404:
        log.warning("Course run %s is not purchasable", course_key)
        raise

    if not FinancialAid.objects.filter(
            tier_program__current=True,
            tier_program__program__course__courserun=course_run,
            user=user,
            status__in=FinancialAidStatus.TERMINAL_STATUSES,
    ).exists():
        log.warning("Course run %s has no attached financial aid for user %s",
                    course_key, get_social_username(user))
        raise ValidationError(
            "Course run {} does not have a current attached financial aid application"
            .format(course_key))

    # Make sure it's not already purchased
    if Line.objects.filter(
            order__status__in=Order.FULFILLED_STATUSES,
            order__user=user,
            course_key=course_run.edx_course_key,
    ).exists():
        mmtrack = get_mmtrack(user, course_run.course.program)
        if not has_to_pay_for_exam(mmtrack, course_run.course):
            log.warning("Course run %s is already purchased by user %s",
                        course_key, user)
            raise ValidationError(
                "Course run {} is already purchased".format(course_key))

    return course_run
コード例 #27
0
    def setUp(self):
        """
        Set up class
        """
        super(EdxPipelineApiTest, self).setUp()
        self.user = UserFactory(username="******")
        self.user.social_auth.create(
            provider='not_edx',
        )
        self.user.social_auth.create(
            provider=edxorg.EdxOrgOAuth2.name,
            uid="{}_edx".format(self.user.username),
        )
        self.user_profile = Profile.objects.get(user=self.user)

        self.mocked_edx_profile = {
            'account_privacy': 'all_users',
            'bio': 'this is my personal profile text',
            'country': 'IT',
            'date_joined': '2016-03-17T20:37:51Z',
            'email': '*****@*****.**',
            'gender': 'f',
            'goals': None,
            'is_active': True,
            'language_proficiencies': [{'code': 'it'}],
            'level_of_education': 'p',
            'mailing_address': None,
            'name': 'dummy user',
            'profile_image': {
                'has_image': True,
                'image_url_full': 'https://edx.org/full.jpg',
                'image_url_large': 'https://edx.org/large.jpg',
                'image_url_medium': 'https://edx.org/medium.jpg',
                'image_url_small': 'https://edx.org/small.jpg'
            },
            'requires_parental_consent': False,
            'username': get_social_username(self.user),
            'year_of_birth': 1986,
            "work_history": [
                {
                    "id": 1,
                    "city": "NY",
                    "state_or_territory": "NY",
                    "country": "USA",
                    "company_name": "XYZ-ABC",
                    "position": "SSE",
                    "industry": "IT",
                    "end_date": "2016-05-17T17:14:00Z",
                    "start_date": "2016-05-28T17:14:06Z"
                }
            ]
        }
コード例 #28
0
    def setUp(self):
        """
        Set up class
        """
        super(EdxPipelineApiTest, self).setUp()
        self.user = UserFactory()
        self.user.social_auth.create(
            provider='not_edx',
        )
        self.user.social_auth.create(
            provider=edxorg.EdxOrgOAuth2.name,
            uid="{}_edx".format(self.user.username),
        )
        self.user_profile = Profile.objects.get(user=self.user)

        self.mocked_edx_profile = {
            'account_privacy': 'all_users',
            'bio': 'this is my personal profile text',
            'country': 'IT',
            'date_joined': '2016-03-17T20:37:51Z',
            'email': '*****@*****.**',
            'gender': 'f',
            'goals': None,
            'is_active': True,
            'language_proficiencies': [{'code': 'it'}],
            'level_of_education': 'p',
            'mailing_address': None,
            'name': 'dummy user',
            'profile_image': {
                'has_image': True,
                'image_url_full': 'https://edx.org/full.jpg',
                'image_url_large': 'https://edx.org/large.jpg',
                'image_url_medium': 'https://edx.org/medium.jpg',
                'image_url_small': 'https://edx.org/small.jpg'
            },
            'requires_parental_consent': False,
            'username': get_social_username(self.user),
            'year_of_birth': 1986,
            "work_history": [
                {
                    "id": 1,
                    "city": "NY",
                    "state_or_territory": "NY",
                    "country": "USA",
                    "company_name": "XYZ-ABC",
                    "position": "SSE",
                    "industry": "IT",
                    "end_date": "2016-05-17T17:14:00Z",
                    "start_date": "2016-05-28T17:14:06Z"
                }
            ]
        }
コード例 #29
0
 def _fill_in_missing_data(cls, data, user, course_run):
     data['user'] = get_social_username(user)
     data['course_details'].update({
         'course_start':
         course_run.start_date.isoformat(),
         'course_end':
         course_run.end_date.isoformat(),
         'enrollment_start':
         course_run.enrollment_start.isoformat(),
         'enrollment_end':
         course_run.enrollment_end.isoformat()
     })
     return data
コード例 #30
0
 def test_index_context_logged_in_social_auth(self):
     """
     Assert context values when logged in as social auth user
     """
     profile = self.create_and_login_user()
     user = profile.user
     ga_tracking_id = FuzzyText().fuzz()
     with self.settings(GA_TRACKING_ID=ga_tracking_id, ):
         response = self.client.get('/')
         assert response.context['authenticated'] is True
         assert response.context['username'] == get_social_username(user)
         assert response.context['title'] == HomePage.objects.first().title
         js_settings = json.loads(response.context['js_settings_json'])
         assert js_settings['gaTrackingID'] == ga_tracking_id
コード例 #31
0
    def test_check_edx_verified_email(self, is_active, mocked_get_json):
        """
        User should be directed to error page if user is unverified on edX
        """
        mock_strategy = mock.Mock()
        backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy)
        mocked_content = dict(self.mocked_edx_profile)
        mocked_content['is_active'] = is_active
        mocked_get_json.return_value = mocked_content
        result = pipeline_api.check_edx_verified_email(
            backend, {'access_token': 'foo_token'},
            {'username': get_social_username(self.user)})

        mocked_get_json.assert_called_once_with(
            urljoin(
                edxorg.EdxOrgOAuth2.EDXORG_BASE_URL,
                '/api/user/v1/accounts/{0}'.format(
                    get_social_username(self.user))),
            headers={'Authorization': 'Bearer foo_token'})
        if is_active:
            assert 'edx_profile' in result
        else:
            self.assertEqual(result.status_code, 302)
コード例 #32
0
def test_dashboard_states(browser, override_allowed_hosts, seeded_database_loader, django_db_blocker, test_data):
    """Iterate through all possible dashboard states and save screenshots/API results of each one"""
    output_directory = DASHBOARD_STATES_OPTIONS.get('output_directory')
    use_learner_page = DASHBOARD_STATES_OPTIONS.get('learner')
    os.makedirs(output_directory, exist_ok=True)
    use_mobile = DASHBOARD_STATES_OPTIONS.get('mobile')
    if use_mobile:
        browser.driver.set_window_size(480, 854)

    dashboard_states = DashboardStates(test_data['user'])
    dashboard_state_iter = enumerate(dashboard_states)
    match = DASHBOARD_STATES_OPTIONS.get('match')
    if match is not None:
        dashboard_state_iter = filter(
            lambda scenario: match in make_filename(scenario[0], scenario[1][1]),
            dashboard_state_iter
        )

    LoginPage(browser).log_in_via_admin(dashboard_states.user, DEFAULT_PASSWORD)
    for num, (run_scenario, name) in dashboard_state_iter:
        skip_screenshot = False
        with django_db_blocker.unblock():
            dashboard_states.user.refresh_from_db()
            if use_learner_page:
                for program in Program.objects.all():
                    Role.objects.create(role=Staff.ROLE_ID, user=dashboard_states.user, program=program)
            filename = make_filename(num, name, output_directory=output_directory, use_mobile=use_mobile)
            new_url = run_scenario()
            if new_url is None:
                if use_learner_page:
                    new_url = '/learner'
                else:
                    new_url = '/dashboard'
            elif use_learner_page:
                # the new_url is only for the dashboard page, skip
                skip_screenshot = True
            if not skip_screenshot:
                browser.get(new_url)
                browser.store_api_results(
                    get_social_username(dashboard_states.user),
                    filename=filename
                )
                if use_learner_page:
                    browser.wait_until_loaded(By.CLASS_NAME, 'user-page')
                else:
                    browser.wait_until_loaded(By.CLASS_NAME, 'course-list')
                browser.take_screenshot(filename=filename)
        with django_db_blocker.unblock():
            terminate_db_connections()
        seeded_database_loader.load_backup()
コード例 #33
0
 def test_limited(self):  # pylint: disable=no-self-use
     """
     Test limited serializer
     """
     profile = self.create_profile()
     assert ProfileLimitedSerializer().to_representation(profile) == {
         'username':
         get_social_username(profile.user),
         'first_name':
         profile.first_name,
         'last_name':
         profile.last_name,
         'preferred_name':
         profile.preferred_name,
         'gender':
         profile.gender,
         'account_privacy':
         profile.account_privacy,
         'has_profile_image':
         profile.has_profile_image,
         'profile_url_full':
         format_gravatar_url(profile.user.email, GravatarImgSize.FULL),
         'profile_url_large':
         format_gravatar_url(profile.user.email, GravatarImgSize.LARGE),
         'profile_url_medium':
         format_gravatar_url(profile.user.email, GravatarImgSize.MEDIUM),
         'profile_url_small':
         format_gravatar_url(profile.user.email, GravatarImgSize.SMALL),
         'country':
         profile.country,
         'state_or_territory':
         profile.state_or_territory,
         'city':
         profile.city,
         'birth_country':
         profile.birth_country,
         'preferred_language':
         profile.preferred_language,
         'edx_level_of_education':
         profile.edx_level_of_education,
         'education': [
             EducationSerializer().to_representation(education)
             for education in profile.education.all()
         ],
         'work_history': [
             EmploymentSerializer().to_representation(work_history)
             for work_history in profile.work_history.all()
         ]
     }
コード例 #34
0
 def test_coupon_not_redeemable(self):
     """
     A 404 should be returned if coupon is not redeemable
     """
     with patch('ecommerce.views.is_coupon_redeemable',
                autospec=True) as _is_redeemable_mock:
         _is_redeemable_mock.return_value = False
         resp = self.client.post(
             reverse('coupon-user-create',
                     kwargs={'code': self.coupon.coupon_code}),
             data={"username": get_social_username(self.user)},
             format='json',
         )
         assert resp.status_code == status.HTTP_404_NOT_FOUND
     _is_redeemable_mock.assert_called_with(self.coupon, self.user)
コード例 #35
0
ファイル: api_test.py プロジェクト: mitodl/micromasters
    def test_two_social(self):
        """
        get_social_username should return None if there are two social edX accounts for a user
        """
        UserSocialAuthFactory.create(user=self.user, uid='other name')

        with LogCapture() as log_capture:
            assert get_social_username(self.user) is None
            log_capture.check(
                (
                    'profiles.api',
                    'ERROR',
                    'Unexpected error retrieving social auth username: get() returned more than '
                    'one UserSocialAuth -- it returned 2!'
                )
            )
コード例 #36
0
    def test_users_logged_in(self):
        """
        Assert settings we pass to dashboard
        """
        profile = self.create_and_login_user()
        user = profile.user
        username = get_social_username(user)

        ga_tracking_id = FuzzyText().fuzz()
        react_ga_debug = FuzzyText().fuzz()
        edx_base_url = FuzzyText().fuzz()
        host = FuzzyText().fuzz()
        with self.settings(
                GA_TRACKING_ID=ga_tracking_id,
                REACT_GA_DEBUG=react_ga_debug,
                EDXORG_BASE_URL=edx_base_url,
                WEBPACK_DEV_SERVER_HOST=host,
        ):
            # Mock has_permission so we don't worry about testing permissions here
            has_permission = Mock(return_value=True)
            with patch(
                    'profiles.permissions.CanSeeIfNotPrivate.has_permission',
                    has_permission):
                resp = self.client.get(
                    reverse('ui-users', kwargs={'user': username}))
                assert resp.status_code == 200
                js_settings = json.loads(resp.context['js_settings_json'])
                assert js_settings == {
                    'gaTrackingID':
                    ga_tracking_id,
                    'reactGaDebug':
                    react_ga_debug,
                    'authenticated':
                    True,
                    'name':
                    user.profile.preferred_name,
                    'username':
                    username,
                    'host':
                    host,
                    'edx_base_url':
                    edx_base_url,
                    'roles': [],
                    'search_url':
                    reverse('search_api', kwargs={"elastic_url": ""}),
                }
                assert has_permission.called
コード例 #37
0
 def test_limited(self):
     """
     Test limited serializer
     """
     profile = self.create_profile()
     data = ProfileLimitedSerializer(profile).data
     assert data == {
         'username':
         get_social_username(profile.user),
         'first_name':
         profile.first_name,
         'last_name':
         profile.last_name,
         'full_name':
         profile.full_name,
         'preferred_name':
         profile.preferred_name,
         'gender':
         profile.gender,
         'account_privacy':
         profile.account_privacy,
         'country':
         profile.country,
         'state_or_territory':
         profile.state_or_territory,
         'city':
         profile.city,
         'birth_country':
         profile.birth_country,
         'preferred_language':
         profile.preferred_language,
         'edx_level_of_education':
         profile.edx_level_of_education,
         'education':
         EducationSerializer(profile.education.all(), many=True).data,
         'work_history': (EmploymentSerializer(profile.work_history.all(),
                                               many=True).data),
         'image_medium':
         profile.image_medium.url,
         'about_me':
         profile.about_me,
         'romanized_first_name':
         profile.romanized_first_name,
         'romanized_last_name':
         profile.romanized_last_name,
     }
コード例 #38
0
ファイル: views_test.py プロジェクト: mitodl/micromasters
 def test_coupon_not_redeemable(self):
     """
     A 404 should be returned if coupon is not redeemable
     """
     with patch(
         'ecommerce.views.is_coupon_redeemable', autospec=True
     ) as _is_redeemable_mock:
         _is_redeemable_mock.return_value = False
         resp = self.client.post(
             reverse('coupon-user-create', kwargs={'code': self.coupon.coupon_code}),
             data={
                 "username": get_social_username(self.user)
             },
             format='json',
         )
         assert resp.status_code == status.HTTP_404_NOT_FOUND
     _is_redeemable_mock.assert_called_with(self.coupon, self.user)
コード例 #39
0
    def test_create_user_coupon(self, already_exists):
        """
        Test happy case for creating a UserCoupon
        """
        previous_modified = self.coupon.user_coupon_qset(
            self.user).first().updated_on
        if not already_exists:
            # Won't change anything if it already exists
            UserCoupon.objects.all().delete()
        data = {
            'username': get_social_username(self.user),
        }
        with patch('ecommerce.views.is_coupon_redeemable',
                   autospec=True) as _is_redeemable_mock:
            _is_redeemable_mock.return_value = True
            resp = self.client.post(
                reverse('coupon-user-create',
                        kwargs={'code': self.coupon.coupon_code}),
                data=data,
                format='json',
            )
        _is_redeemable_mock.assert_called_with(self.coupon, self.user)
        assert resp.status_code == status.HTTP_200_OK
        assert UserCoupon.objects.count() == 1
        user_coupon = UserCoupon.objects.get(user=self.user,
                                             coupon=self.coupon)
        assert user_coupon.updated_on > previous_modified
        assert resp.json() == {
            'message': 'Attached user to coupon successfully.',
            'coupon': {
                'amount': str(self.coupon.amount),
                'amount_type': self.coupon.amount_type,
                'content_type': self.coupon.content_type.model,
                'coupon_type': self.coupon.coupon_type,
                'coupon_code': self.coupon.coupon_code,
                'object_id': self.coupon.object_id,
                'program_id': self.coupon.program.id,
            }
        }

        assert UserCouponAudit.objects.count() == 1
        audit = UserCouponAudit.objects.first()
        assert audit.user_coupon == user_coupon
        assert audit.data_after == serialize_model_object(user_coupon)
コード例 #40
0
 def test_full(self):
     """
     Test full serializer
     """
     birthdate = date(1980, 1, 2)
     profile = self.create_profile(date_of_birth=birthdate)
     data = ProfileSerializer(profile).data
     assert data == {
         'username': get_social_username(profile.user),
         'first_name': profile.first_name,
         'full_name': profile.full_name,
         'filled_out': profile.filled_out,
         'agreed_to_terms_of_service': profile.agreed_to_terms_of_service,
         'last_name': profile.last_name,
         'preferred_name': profile.preferred_name,
         'email_optin': profile.email_optin,
         'email': profile.email,
         'gender': profile.gender,
         'date_of_birth': "1980-01-02",
         'account_privacy': profile.account_privacy,
         'country': profile.country,
         'state_or_territory': profile.state_or_territory,
         'city': profile.city,
         'address': profile.address,
         'postal_code': profile.postal_code,
         'birth_country': profile.birth_country,
         'nationality': profile.nationality,
         'preferred_language': profile.preferred_language,
         'pretty_printed_student_id': profile.pretty_printed_student_id,
         'edx_level_of_education': profile.edx_level_of_education,
         'education': EducationSerializer(profile.education.all(), many=True).data,
         'work_history': (
             EmploymentSerializer(profile.work_history.all(), many=True).data
         ),
         'image': profile.image.url,
         'image_small': profile.image_small.url,
         'image_medium': profile.image_medium.url,
         'about_me': profile.about_me,
         'romanized_first_name': profile.romanized_first_name,
         'romanized_last_name': profile.romanized_last_name,
         'phone_number': profile.phone_number,
         'student_id': profile.student_id,
     }
コード例 #41
0
ファイル: views_test.py プロジェクト: mitodl/micromasters
    def test_create_user_coupon(self, already_exists):
        """
        Test happy case for creating a UserCoupon
        """
        previous_modified = self.coupon.user_coupon_qset(self.user).first().updated_on
        if not already_exists:
            # Won't change anything if it already exists
            UserCoupon.objects.all().delete()
        data = {
            'username': get_social_username(self.user),
        }
        with patch(
            'ecommerce.views.is_coupon_redeemable', autospec=True
        ) as _is_redeemable_mock:
            _is_redeemable_mock.return_value = True
            resp = self.client.post(
                reverse('coupon-user-create', kwargs={'code': self.coupon.coupon_code}),
                data=data,
                format='json',
            )
        _is_redeemable_mock.assert_called_with(self.coupon, self.user)
        assert resp.status_code == status.HTTP_200_OK
        assert UserCoupon.objects.count() == 1
        user_coupon = UserCoupon.objects.get(user=self.user, coupon=self.coupon)
        assert user_coupon.updated_on > previous_modified
        assert resp.json() == {
            'message': 'Attached user to coupon successfully.',
            'coupon': {
                'amount': str(self.coupon.amount),
                'amount_type': self.coupon.amount_type,
                'content_type': self.coupon.content_type.model,
                'coupon_type': self.coupon.coupon_type,
                'coupon_code': self.coupon.coupon_code,
                'object_id': self.coupon.object_id,
                'program_id': self.coupon.program.id,
            }
        }

        assert UserCouponAudit.objects.count() == 1
        audit = UserCouponAudit.objects.first()
        assert audit.user_coupon == user_coupon
        assert audit.data_after == serialize_model_object(user_coupon)
コード例 #42
0
    def get(self, request, *args, **kwargs):
        """
        Handle GET requests to templates using React
        """
        user = request.user
        username = get_social_username(user)
        name = ""
        roles = []
        if not user.is_anonymous():
            name = user.profile.preferred_name
            roles = [{
                'program':
                role.program.id,
                'role':
                role.role,
                'permissions': [
                    perm
                    for perm, value in available_perm_status(user).items()
                    if value is True
                ]
            } for role in user.role_set.all()]

        js_settings = {
            "gaTrackingID": settings.GA_TRACKING_ID,
            "reactGaDebug": settings.REACT_GA_DEBUG,
            "authenticated": not user.is_anonymous(),
            "name": name,
            "username": username,
            "host": webpack_dev_server_host(request),
            "edx_base_url": settings.EDXORG_BASE_URL,
            "roles": roles,
            "search_url": reverse('search_api', kwargs={"elastic_url": ""}),
        }

        return render(request,
                      "dashboard.html",
                      context={
                          "style_src": get_bundle_url(request, "style.js"),
                          "dashboard_src":
                          get_bundle_url(request, "dashboard.js"),
                          "js_settings_json": json.dumps(js_settings),
                      })
コード例 #43
0
def enroll_user_on_success(order):
    """
    Enroll user after they made a successful purchase.

    Args:
        order (Order): An order to be fulfilled

    Returns:
         None
    """
    user_social = get_social_auth(order.user)
    enrollments_client = EdxApi(user_social.extra_data,
                                settings.EDXORG_BASE_URL).enrollments
    existing_enrollments = enrollments_client.get_student_enrollments()

    exceptions = []
    enrollments = []
    for line in order.line_set.all():
        course_key = line.course_key
        try:
            if not existing_enrollments.is_enrolled_in(course_key):
                enrollments.append(
                    enrollments_client.create_audit_student_enrollment(
                        course_key))
        except Exception as ex:  # pylint: disable=broad-except
            log.exception(
                "Error creating audit enrollment for course key %s for user %s",
                course_key,
                get_social_username(order.user),
            )
            exceptions.append(ex)

    for enrollment in enrollments:
        CachedEdxDataApi.update_cached_enrollment(
            order.user,
            enrollment,
            enrollment.course_id,
            index_user=True,
        )

    if exceptions:
        raise EcommerceEdxApiException(exceptions)
コード例 #44
0
def standard_error_page(request, status_code, template_filename):
    """
    Returns an error page with a given template filename and provides necessary context variables
    """
    name = request.user.profile.preferred_name if not request.user.is_anonymous(
    ) else ""
    authenticated = not request.user.is_anonymous()
    username = get_social_username(request.user)
    response = render(request,
                      template_filename,
                      context={
                          "style_src": get_bundle_url(request, "style.js"),
                          "dashboard_src":
                          get_bundle_url(request, "dashboard.js"),
                          "js_settings_json": "{}",
                          "authenticated": authenticated,
                          "name": name,
                          "username": username
                      })
    response.status_code = status_code
    return response
コード例 #45
0
    def test_update_profile(self, mocked_get_json):
        """
        Happy path
        """
        mocked_content = self.mocked_edx_profile
        mocked_get_json.return_value = mocked_content
        pipeline_api.update_profile_from_edx(
            edxorg.EdxOrgOAuth2, self.user, {'access_token': 'foo_token'}, True)
        mocked_get_json.assert_called_once_with(
            urljoin(
                edxorg.EdxOrgOAuth2.EDXORG_BASE_URL,
                '/api/user/v1/accounts/{0}'.format(get_social_username(self.user))
            ),
            headers={'Authorization': 'Bearer foo_token'}
        )

        first_name, last_name = split_name(mocked_content['name'])

        all_fields = [
            ('account_privacy', Profile.PUBLIC_TO_MM),
            ('edx_name', mocked_content['name']),
            ('first_name', first_name),
            ('last_name', last_name),
            ('preferred_name', None),
            ('edx_bio', mocked_content['bio']),
            ('country', mocked_content['country']),
            ('has_profile_image', mocked_content['profile_image']['has_image']),
            ('edx_requires_parental_consent', mocked_content['requires_parental_consent']),
            ('edx_level_of_education', mocked_content['level_of_education']),
            ('edx_goals', mocked_content['goals']),
            ('edx_language_proficiencies', mocked_content['language_proficiencies']),
            ('preferred_language', mocked_content['language_proficiencies'][0]['code']),
            ('gender', mocked_content['gender']),
            ('edx_mailing_address', mocked_content['mailing_address']),
        ]

        self.check_profile_fields(self.user_profile, all_fields)

        # We do not set the date_of_birth using year_of_birth
        assert self.user_profile.date_of_birth is None
コード例 #46
0
ファイル: models.py プロジェクト: guaranta/micromasters
    def get_context(self, request, *args, **kwargs):
        programs = Program.objects.filter(
            live=True).select_related('programpage').order_by("id")
        benefits_page = BenefitsPage.objects.filter(live=True).first()
        js_settings = {
            "gaTrackingID": settings.GA_TRACKING_ID,
            "host": webpack_dev_server_host(request),
            "environment": settings.ENVIRONMENT,
            "sentry_dsn": settings.SENTRY_DSN,
            "release_version": settings.VERSION
        }

        username = get_social_username(request.user)
        context = super(HomePage, self).get_context(request)

        def get_program_page(program):
            """Return a None if ProgramPage does not exist, to avoid template errors"""
            try:
                return program.programpage
            except ProgramPage.DoesNotExist:
                return None

        program_pairs = [(program, get_program_page(program))
                         for program in programs]
        context["programs"] = program_pairs
        context["is_public"] = True
        context["has_zendesk_widget"] = True
        context["google_maps_api"] = False
        context["authenticated"] = not request.user.is_anonymous
        context["is_staff"] = has_role(request.user,
                                       [Staff.ROLE_ID, Instructor.ROLE_ID])
        context["username"] = username
        context["js_settings_json"] = json.dumps(js_settings)
        context["title"] = self.title
        context["ga_tracking_id"] = ""
        context["coupon_code"] = get_coupon_code(request)
        context["benefits_url"] = benefits_page.get_url(
        ) if benefits_page else ""

        return context
コード例 #47
0
ファイル: api.py プロジェクト: mitodl/micromasters
def create_unfulfilled_order(course_key, user):
    """
    Create a new Order which is not fulfilled for a purchasable course run. If course run is not purchasable,
    it raises an Http404

    Args:
        course_key (str):
            A course key
        user (User):
            The purchaser of the course run
    Returns:
        Order: A newly created Order for the CourseRun with the given course_id
    """
    course_run = get_purchasable_course_run(course_key, user)
    price, coupon = calculate_run_price(course_run, user)
    if price < 0:
        log.error(
            "Price to be charged for course run %s for user %s is less than zero: %s",
            course_key,
            get_social_username(user),
            price,
        )
        raise ImproperlyConfigured("Price to be charged is less than zero")

    order = Order.objects.create(
        status=Order.CREATED,
        total_price_paid=price,
        user=user,
    )
    Line.objects.create(
        order=order,
        course_key=course_key,
        description='Seat for {}'.format(course_run.title),
        price=price,
    )
    if coupon is not None:
        redeemed_coupon = RedeemedCoupon(order=order, coupon=coupon)
        redeemed_coupon.save_and_log(user)
    order.save_and_log(user)
    return order
コード例 #48
0
ファイル: api.py プロジェクト: mitodl/micromasters
def enroll_user_on_success(order):
    """
    Enroll user after they made a successful purchase.

    Args:
        order (Order): An order to be fulfilled

    Returns:
         None
    """
    user_social = get_social_auth(order.user)
    enrollments_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL).enrollments
    existing_enrollments = enrollments_client.get_student_enrollments()

    exceptions = []
    enrollments = []
    for line in order.line_set.all():
        course_key = line.course_key
        try:
            if not existing_enrollments.is_enrolled_in(course_key):
                enrollments.append(enrollments_client.create_audit_student_enrollment(course_key))
        except Exception as ex:  # pylint: disable=broad-except
            log.exception(
                "Error creating audit enrollment for course key %s for user %s",
                course_key,
                get_social_username(order.user),
            )
            exceptions.append(ex)

    for enrollment in enrollments:
        CachedEdxDataApi.update_cached_enrollment(
            order.user,
            enrollment,
            enrollment.course_id,
            index_user=True,
        )

    if exceptions:
        raise EcommerceEdxApiException(exceptions)
コード例 #49
0
ファイル: api_edx_cache.py プロジェクト: mitodl/micromasters
    def update_cached_current_grades(cls, user, edx_client):
        """
        Updates cached current grade data.

        Args:
            user (django.contrib.auth.models.User): A user
            edx_client (EdxApi): EdX client to retrieve enrollments
        Returns:
            None
        """

        course_ids = models.CachedEnrollment.active_course_ids(user)

        # Current Grades are out of date, so fetch new data from edX.
        current_grades = edx_client.current_grades.get_student_current_grades(
            get_social_username(user), course_ids)

        # the update must be done atomically
        with transaction.atomic():
            all_grade_course_ids = current_grades.all_course_ids
            for course_run in CourseRun.objects.filter(edx_course_key__in=all_grade_course_ids):
                current_grade = current_grades.get_current_grade(course_run.edx_course_key)
                updated_values = {
                    'user': user,
                    'course_run': course_run,
                    'data': current_grade.json,
                }
                models.CachedCurrentGrade.objects.update_or_create(
                    user=user,
                    course_run=course_run,
                    defaults=updated_values
                )
            # delete anything is not in the current grades
            models.CachedCurrentGrade.delete_all_but(user, all_grade_course_ids)
            # update the last refresh timestamp
            cls.update_cache_last_access(user, cls.CURRENT_GRADE)
        # submit a celery task to reindex the user
        tasks.index_users.delay([user.id], check_if_changed=True)
コード例 #50
0
ファイル: api_test.py プロジェクト: mitodl/micromasters
 def test_anonymous_user(self):
     """
     get_social_username should return None for anonymous users
     """
     user = Mock(is_anonymous=True)
     assert get_social_username(user) is None
コード例 #51
0
ファイル: serializers.py プロジェクト: mitodl/micromasters
 def get_username(self, obj):
     """Getter for the username field"""
     return get_social_username(obj.user)
コード例 #52
0
ファイル: api_test.py プロジェクト: mitodl/micromasters
 def test_zero_social(self):
     """
     get_social_username should return None if there is no edX account associated yet
     """
     self.user.social_auth.all().delete()
     assert get_social_username(self.user) is None
コード例 #53
0
ファイル: api_test.py プロジェクト: mitodl/micromasters
 def test_one_social(self):
     """
     get_social_username should return the social username, not the Django username
     """
     assert get_social_username(self.user) == self.user.social_auth.first().uid
コード例 #54
0
ファイル: views_test.py プロジェクト: mitodl/micromasters
    def test_users_anonymous(self):
        """
        Assert settings we pass to dashboard
        """
        profile = self.create_and_login_user()
        user = profile.user
        self.client.logout()
        username = get_social_username(user)

        ga_tracking_id = FuzzyText().fuzz()
        react_ga_debug = FuzzyText().fuzz()
        edx_base_url = FuzzyText().fuzz()
        host = FuzzyText().fuzz()
        email_support = FuzzyText().fuzz()
        open_discussions_redirect_url = FuzzyText().fuzz()
        with self.settings(
            GA_TRACKING_ID=ga_tracking_id,
            REACT_GA_DEBUG=react_ga_debug,
            EDXORG_BASE_URL=edx_base_url,
            WEBPACK_DEV_SERVER_HOST=host,
            EMAIL_SUPPORT=email_support,
            VERSION='0.0.1',
            RAVEN_CONFIG={'dsn': ''},
            ELASTICSEARCH_DEFAULT_PAGE_SIZE=10,
            EXAMS_SSO_CLIENT_CODE='itsacode',
            EXAMS_SSO_URL='url',
            OPEN_DISCUSSIONS_REDIRECT_URL=open_discussions_redirect_url
        ):
            # Mock has_permission so we don't worry about testing permissions here
            has_permission = Mock(return_value=True)
            with patch(
                'profiles.permissions.CanSeeIfNotPrivate.has_permission',
                has_permission,
            ), patch('ui.templatetags.render_bundle._get_bundle') as get_bundle:
                resp = self.client.get(reverse('ui-users', kwargs={'user': username}))
                assert resp.status_code == 200
                assert resp.context['is_public'] is False
                assert resp.context['has_zendesk_widget'] is True
                self.assertNotContains(resp, 'Share this page')
                js_settings = json.loads(resp.context['js_settings_json'])
                assert js_settings == {
                    'gaTrackingID': ga_tracking_id,
                    'reactGaDebug': react_ga_debug,
                    'user': None,
                    'host': host,
                    'edx_base_url': edx_base_url,
                    'roles': [],
                    'search_url': reverse('search_api', kwargs={"elastic_url": ""}),
                    'support_email': email_support,
                    'environment': 'dev',
                    'release_version': '0.0.1',
                    'sentry_dsn': None,
                    'es_page_size': 10,
                    'public_path': '/static/bundles/',
                    'EXAMS_SSO_CLIENT_CODE': 'itsacode',
                    'EXAMS_SSO_URL': 'url',
                    'FEATURES': {
                        'PROGRAM_LEARNERS': False,
                        'DISCUSSIONS_POST_UI': False,
                        'DISCUSSIONS_CREATE_CHANNEL_UI': False,
                        'PROGRAM_RECORD_LINK': False,
                        'ENABLE_PROGRAM_LETTER': False,
                    },
                    'open_discussions_redirect_url': open_discussions_redirect_url
                }
                assert has_permission.called

                bundles = [bundle[0][1] for bundle in get_bundle.call_args_list]
                assert set(bundles) == {
                    'common',
                    'dashboard',
                    'sentry_client',
                    'style',
                    'zendesk_widget',
                }
コード例 #55
0
ファイル: api.py プロジェクト: mitodl/micromasters
def get_purchasable_course_run(course_key, user):
    """
    Gets a course run, or raises Http404 if not purchasable. To be purchasable a course run
    must not already be purchased, must be part of a live program, must be part of a program
    with financial aid enabled, with a financial aid object, and must have a valid price.

    Args:
        course_key (str):
            An edX course key
        user (User):
            The purchaser of the course run
    Returns:
        CourseRun: A course run
    """
    # Make sure it's connected to a live program, it has a valid price, and the user is enrolled in the program already
    try:
        course_run = get_object_or_404(
            CourseRun,
            edx_course_key=course_key,
            course__program__live=True,
            course__program__financial_aid_availability=True,
        )
    except Http404:
        log.warning("Course run %s is not purchasable", course_key)
        raise

    if not FinancialAid.objects.filter(
            tier_program__current=True,
            tier_program__program__course__courserun=course_run,
            user=user,
            status__in=FinancialAidStatus.TERMINAL_STATUSES,
    ).exists():
        log.warning("Course run %s has no attached financial aid for user %s", course_key, get_social_username(user))
        raise ValidationError(
            "Course run {} does not have a current attached financial aid application".format(course_key)
        )

    # Make sure it's not already purchased
    if Line.objects.filter(
            order__status__in=Order.FULFILLED_STATUSES,
            order__user=user,
            course_key=course_run.edx_course_key,
    ).exists():
        mmtrack = get_mmtrack(user, course_run.course.program)
        if not has_to_pay_for_exam(mmtrack, course_run.course):
            log.warning("Course run %s is already purchased by user %s", course_key, user)
            raise ValidationError("Course run {} is already purchased".format(course_key))

    return course_run