Ejemplo n.º 1
0
    def render_to_fragment(self, request, **kwargs):
        """
        Render the program listing fragment.
        """
        user = request.user
        try:
            mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
        except ValueError:
            mobile_only = False

        programs_config = kwargs.get('programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not user.is_authenticated:
            raise Http404

        meter = ProgramProgressMeter(request.site, user, mobile_only=mobile_only)

        context = {
            'marketing_url': get_program_marketing_url(programs_config),
            'programs': meter.engaged_programs,
            'progress': meter.progress()
        }
        html = render_to_string('learner_dashboard/programs_fragment.html', context)
        programs_fragment = Fragment(html)
        self.add_fragment_resource_urls(programs_fragment)

        return programs_fragment
Ejemplo n.º 2
0
    def test_course_grade_results(self, mock_get_programs):
        grade_percent = .8
        with mock_passing_grade(percent=grade_percent):
            course_run_key = generate_course_run_key()
            data = [
                ProgramFactory(
                    courses=[
                        CourseFactory(course_runs=[
                            CourseRunFactory(key=course_run_key),
                        ]),
                    ]
                )
            ]
            mock_get_programs.return_value = data

            self._create_enrollments(course_run_key)

            meter = ProgramProgressMeter(self.site, self.user)

            program = data[0]
            expected = [
                ProgressFactory(
                    uuid=program['uuid'],
                    completed=[],
                    in_progress=[program['courses'][0]],
                    not_started=[],
                    grades={course_run_key: grade_percent},
                )
            ]

            self.assertEqual(meter.progress(count_only=False), expected)
Ejemplo n.º 3
0
    def test_course_progress(self, mock_get_programs):
        """
        Verify that the progress meter can represent progress in terms of
        serialized courses.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)

        meter = ProgramProgressMeter(self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[]
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Ejemplo n.º 4
0
    def related_programs(self):
        """
        Returns related program data if the effective_user is enrolled.
        Note: related programs can be None depending on the course.
        """
        if self.effective_user.is_anonymous:
            return

        meter = ProgramProgressMeter(self.request.site, self.effective_user)
        inverted_programs = meter.invert_programs()
        related_programs = inverted_programs.get(str(self.course_key))

        if related_programs is None:
            return

        related_progress = meter.progress(programs=related_programs)
        progress_by_program = {
            progress['uuid']: progress
            for progress in related_progress
        }

        programs = [{
            'progress': progress_by_program[program['uuid']],
            'title': program['title'],
            'slug': program['type_attrs']['slug'],
            'url': program['detail_url'],
            'uuid': program['uuid']
        } for program in related_programs]

        return programs
Ejemplo n.º 5
0
    def test_no_id_professional_in_progress(self, mock_get_programs):
        """
        Verify that the progress meter treats no-id-professional enrollments
        as professional.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type=CourseMode.PROFESSIONAL),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(
            user=self.user, course_id=course_run_key,
            mode=CourseMode.NO_ID_PROFESSIONAL_MODE
        )

        meter = ProgramProgressMeter(self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[]
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Ejemplo n.º 6
0
    def handle(self, *args, **options):
        """
        Command's entry point.
        """
        should_commit = not options['no_commit']

        email_sent_records = []
        site = Site.objects.get_current()
        course_to_users_maps = self.get_passed_course_to_users_maps()

        for completed_course_id, users in course_to_users_maps.items():
            course_linked_programs = get_programs(course=completed_course_id)
            course_linked_programs = self.sort_programs(course_linked_programs)
            if course_linked_programs:
                for user in users:
                    meter = ProgramProgressMeter(site=site, user=user, include_course_entitlements=False)
                    programs_progress = meter.progress(programs=course_linked_programs, count_only=False)
                    suggested_program_progress, suggested_course_run = self.get_course_run_to_suggest(
                        programs_progress, completed_course_id
                    )
                    if suggested_course_run:
                        suggested_program = self.get_program(course_linked_programs, suggested_program_progress)
                        completed_course_run = self.get_course_run(suggested_program, completed_course_id)
                        if should_commit:
                            self.emit_event(user, suggested_program, suggested_course_run, completed_course_run)
                        email_sent_records.append(
                            f'User: {user.username}, Completed Course: {completed_course_id}, '
                            f'Suggested Course: {suggested_course_run["key"]}'
                        )

        LOGGER.info(
            '[Program Course Nudge Email] %s Emails sent. Records: %s',
            len(email_sent_records),
            email_sent_records,
        )
Ejemplo n.º 7
0
    def test_course_progress(self, mock_get_programs):
        """
        Verify that the progress meter can represent progress in terms of
        serialized courses.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[],
                grades={course_run_key: 0.0},
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Ejemplo n.º 8
0
    def test_course_grade_results(self, mock_get_programs):
        grade_percent = .8
        with mock_passing_grade(percent=grade_percent):
            course_run_key = generate_course_run_key()
            data = [
                ProgramFactory(
                    courses=[
                        CourseFactory(course_runs=[
                            CourseRunFactory(key=course_run_key),
                        ]),
                    ]
                )
            ]
            mock_get_programs.return_value = data

            self._create_enrollments(course_run_key)

            meter = ProgramProgressMeter(self.site, self.user)

            program = data[0]
            expected = [
                ProgressFactory(
                    uuid=program['uuid'],
                    completed=[],
                    in_progress=[program['courses'][0]],
                    not_started=[],
                    grades={course_run_key: grade_percent},
                )
            ]

            self.assertEqual(meter.progress(count_only=False), expected)
Ejemplo n.º 9
0
    def test_no_id_professional_in_progress(self, mock_get_programs):
        """
        Verify that the progress meter treats no-id-professional enrollments
        as professional.
        """
        course_run_key = generate_course_run_key()
        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type=CourseMode.PROFESSIONAL),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(
            user=self.user, course_id=course_run_key,
            mode=CourseMode.NO_ID_PROFESSIONAL_MODE
        )

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=[],
                in_progress=[program['courses'][0]],
                not_started=[]
            )
        ]

        self.assertEqual(meter.progress(count_only=False), expected)
Ejemplo n.º 10
0
    def render_to_fragment(self, request, **kwargs):
        """
        Render the program listing fragment.
        """
        user = request.user
        try:
            mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
        except ValueError:
            mobile_only = False

        programs_config = kwargs.get(
            'programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not user.is_authenticated:
            raise Http404

        meter = ProgramProgressMeter(request.site,
                                     user,
                                     mobile_only=mobile_only)

        context = {
            'marketing_url':
            get_program_marketing_url(programs_config, mobile_only),
            'programs':
            meter.engaged_programs,
            'progress':
            meter.progress()
        }
        html = render_to_string('learner_dashboard/programs_fragment.html',
                                context)
        programs_fragment = Fragment(html)
        self.add_fragment_resource_urls(programs_fragment)

        return programs_fragment
Ejemplo n.º 11
0
    def render_to_fragment(self, request, program_uuid, **kwargs):
        """View details about a specific program."""
        programs_config = kwargs.get(
            'programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not request.user.is_authenticated():
            raise Http404

        meter = ProgramProgressMeter(request.site,
                                     request.user,
                                     uuid=program_uuid)
        program_data = meter.programs[0]

        if not program_data:
            raise Http404

        try:
            mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
        except ValueError:
            mobile_only = False

        program_data = ProgramDataExtender(program_data,
                                           request.user,
                                           mobile_only=mobile_only).extend()
        course_data = meter.progress(programs=[program_data],
                                     count_only=False)[0]
        certificate_data = get_certificates(request.user, program_data)

        program_data.pop('courses')
        skus = program_data.get('skus')
        ecommerce_service = EcommerceService()

        urls = {
            'program_listing_url':
            reverse('program_listing_view'),
            'track_selection_url':
            strip_course_id(
                reverse('course_modes_choose',
                        kwargs={'course_id': FAKE_COURSE_KEY})),
            'commerce_api_url':
            reverse('commerce_api:v0:baskets:create'),
            'buy_button_url':
            ecommerce_service.get_checkout_page_url(*skus)
        }

        context = {
            'urls': urls,
            'user_preferences': get_user_preferences(request.user),
            'program_data': program_data,
            'course_data': course_data,
            'certificate_data': certificate_data
        }

        html = render_to_string(
            'learner_dashboard/program_details_fragment.html', context)
        program_details_fragment = Fragment(html)
        self.add_fragment_resource_urls(program_details_fragment)
        return program_details_fragment
Ejemplo n.º 12
0
    def render_to_fragment(self, request, program_uuid, **kwargs):
        """View details about a specific program."""
        programs_config = kwargs.get('programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not request.user.is_authenticated:
            raise Http404

        meter = ProgramProgressMeter(request.site, request.user, uuid=program_uuid)
        program_data = meter.programs[0]

        if not program_data:
            raise Http404

        try:
            mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
        except ValueError:
            mobile_only = False

        program_data = ProgramDataExtender(program_data, request.user, mobile_only=mobile_only).extend()
        course_data = meter.progress(programs=[program_data], count_only=False)[0]
        certificate_data = get_certificates(request.user, program_data)

        program_data.pop('courses')
        skus = program_data.get('skus')
        ecommerce_service = EcommerceService()

        # TODO: Don't have business logic of course-certificate==record-available here in LMS.
        # Eventually, the UI should ask Credentials if there is a record available and get a URL from it.
        # But this is here for now so that we can gate this URL behind both this business logic and
        # a waffle flag. This feature is in active developoment.
        program_record_url = get_credentials_records_url(program_uuid=program_uuid)
        if not certificate_data:
            program_record_url = None

        urls = {
            'program_listing_url': reverse('program_listing_view'),
            'track_selection_url': strip_course_id(
                reverse('course_modes_choose', kwargs={'course_id': FAKE_COURSE_KEY})
            ),
            'commerce_api_url': reverse('commerce_api:v0:baskets:create'),
            'buy_button_url': ecommerce_service.get_checkout_page_url(*skus),
            'program_record_url': program_record_url,
        }

        context = {
            'urls': urls,
            'user_preferences': get_user_preferences(request.user),
            'program_data': program_data,
            'course_data': course_data,
            'certificate_data': certificate_data
        }

        html = render_to_string('learner_dashboard/program_details_fragment.html', context)
        program_details_fragment = Fragment(html)
        self.add_fragment_resource_urls(program_details_fragment)
        return program_details_fragment
Ejemplo n.º 13
0
    def render_to_fragment(self, request, program_uuid, **kwargs):
        """View details about a specific program."""
        programs_config = kwargs.get('programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not request.user.is_authenticated:
            raise Http404

        meter = ProgramProgressMeter(request.site, request.user, uuid=program_uuid)
        program_data = meter.programs[0]

        if not program_data:
            raise Http404

        try:
            mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
        except ValueError:
            mobile_only = False

        program_data = ProgramDataExtender(program_data, request.user, mobile_only=mobile_only).extend()
        course_data = meter.progress(programs=[program_data], count_only=False)[0]
        certificate_data = get_certificates(request.user, program_data)

        program_data.pop('courses')
        skus = program_data.get('skus')
        ecommerce_service = EcommerceService()

        # TODO: Don't have business logic of course-certificate==record-available here in LMS.
        # Eventually, the UI should ask Credentials if there is a record available and get a URL from it.
        # But this is here for now so that we can gate this URL behind both this business logic and
        # a waffle flag. This feature is in active developoment.
        program_record_url = get_credentials_records_url(program_uuid=program_uuid)
        if not certificate_data:
            program_record_url = None

        urls = {
            'program_listing_url': reverse('program_listing_view'),
            'track_selection_url': strip_course_id(
                reverse('course_modes_choose', kwargs={'course_id': FAKE_COURSE_KEY})
            ),
            'commerce_api_url': reverse('commerce_api:v0:baskets:create'),
            'buy_button_url': ecommerce_service.get_checkout_page_url(*skus),
            'program_record_url': program_record_url,
        }

        context = {
            'urls': urls,
            'user_preferences': get_user_preferences(request.user),
            'program_data': program_data,
            'course_data': course_data,
            'certificate_data': certificate_data
        }

        html = render_to_string('learner_dashboard/program_details_fragment.html', context)
        program_details_fragment = Fragment(html)
        self.add_fragment_resource_urls(program_details_fragment)
        return program_details_fragment
Ejemplo n.º 14
0
def program_details(request, program_uuid):
    """View details about a specific program."""
    programs_config = ProgramsApiConfig.current()
    if not programs_config.enabled:
        raise Http404

    meter = ProgramProgressMeter(request.user, uuid=program_uuid)
    program_data = meter.programs[0]

    if not program_data:
        raise Http404

    program_data = ProgramDataExtender(program_data, request.user).extend()

    urls = {
        'program_listing_url':
        reverse('program_listing_view'),
        'track_selection_url':
        strip_course_id(
            reverse('course_modes_choose',
                    kwargs={'course_id': FAKE_COURSE_KEY})),
        'commerce_api_url':
        reverse('commerce_api:v0:baskets:create'),
    }

    context = {
        'urls': urls,
        'show_program_listing': programs_config.enabled,
        'nav_hidden': True,
        'disable_courseware_js': True,
        'uses_pattern_library': True,
        'user_preferences': get_user_preferences(request.user)
    }

    if waffle.switch_is_active('new_program_progress'):
        course_data = meter.progress(programs=[program_data],
                                     count_only=False)[0]
        certificate_data = get_certificates(request.user, program_data)

        program_data.pop('courses')

        context.update({
            'program_data': program_data,
            'course_data': course_data,
            'certificate_data': certificate_data,
        })

        return render_to_response(
            'learner_dashboard/program_details_2017.html', context)
    else:
        context.update({'program_data': program_data})

        return render_to_response('learner_dashboard/program_details.html',
                                  context)
Ejemplo n.º 15
0
def program_details(request, program_uuid):
    """View details about a specific program."""
    programs_config = ProgramsApiConfig.current()
    if not programs_config.enabled:
        raise Http404

    meter = ProgramProgressMeter(request.user, uuid=program_uuid)
    program_data = meter.programs[0]

    if not program_data:
        raise Http404

    program_data = ProgramDataExtender(program_data, request.user).extend()

    urls = {
        'program_listing_url': reverse('program_listing_view'),
        'track_selection_url': strip_course_id(
            reverse('course_modes_choose', kwargs={'course_id': FAKE_COURSE_KEY})
        ),
        'commerce_api_url': reverse('commerce_api:v0:baskets:create'),
    }

    context = {
        'urls': urls,
        'show_program_listing': programs_config.enabled,
        'nav_hidden': True,
        'disable_courseware_js': True,
        'uses_pattern_library': True,
        'user_preferences': get_user_preferences(request.user)
    }

    if waffle.switch_is_active('new_program_progress'):
        course_data = meter.progress(programs=[program_data], count_only=False)[0]
        certificate_data = get_certificates(request.user, program_data)

        program_data.pop('courses')

        context.update({
            'program_data': program_data,
            'course_data': course_data,
            'certificate_data': certificate_data,
        })

        return render_to_response('learner_dashboard/program_details_2017.html', context)
    else:
        context.update({'program_data': program_data})

        return render_to_response('learner_dashboard/program_details.html', context)
Ejemplo n.º 16
0
def program_details(request, program_uuid):
    """View details about a specific program."""
    programs_config = ProgramsApiConfig.current()
    if not programs_config.enabled:
        raise Http404

    meter = ProgramProgressMeter(request.site, request.user, uuid=program_uuid)
    program_data = meter.programs[0]

    if not program_data:
        raise Http404

    program_data = ProgramDataExtender(program_data, request.user).extend()
    course_data = meter.progress(programs=[program_data], count_only=False)[0]
    certificate_data = get_certificates(request.user, program_data)

    program_data.pop('courses')
    skus = program_data.get('skus')
    ecommerce_service = EcommerceService()

    urls = {
        'program_listing_url':
        reverse('program_listing_view'),
        'track_selection_url':
        strip_course_id(
            reverse('course_modes_choose',
                    kwargs={'course_id': FAKE_COURSE_KEY})),
        'commerce_api_url':
        reverse('commerce_api:v0:baskets:create'),
        'buy_button_url':
        ecommerce_service.get_checkout_page_url(*skus)
    }

    context = {
        'urls': urls,
        'show_program_listing': programs_config.enabled,
        'show_dashboard_tabs': True,
        'nav_hidden': True,
        'disable_courseware_js': True,
        'uses_pattern_library': True,
        'user_preferences': get_user_preferences(request.user),
        'program_data': program_data,
        'course_data': course_data,
        'certificate_data': certificate_data
    }

    return render_to_response('learner_dashboard/program_details.html',
                              context)
Ejemplo n.º 17
0
def program_listing(request):
    """View a list of programs in which the user is engaged."""
    programs_config = ProgramsApiConfig.current()
    if not programs_config.enabled:
        raise Http404

    meter = ProgramProgressMeter(request.user)

    context = {
        'disable_courseware_js': True,
        'marketing_url': get_program_marketing_url(programs_config),
        'nav_hidden': True,
        'programs': meter.engaged_programs,
        'progress': meter.progress(),
        'show_program_listing': programs_config.enabled,
        'uses_pattern_library': True,
    }

    return render_to_response('learner_dashboard/programs.html', context)
Ejemplo n.º 18
0
def program_details(request, program_uuid):
    """View details about a specific program."""
    programs_config = ProgramsApiConfig.current()
    if not programs_config.enabled:
        raise Http404

    meter = ProgramProgressMeter(request.site, request.user, uuid=program_uuid)
    program_data = meter.programs[0]

    if not program_data:
        raise Http404

    program_data = ProgramDataExtender(program_data, request.user).extend()
    course_data = meter.progress(programs=[program_data], count_only=False)[0]
    certificate_data = get_certificates(request.user, program_data)

    program_data.pop('courses')
    skus = program_data.get('skus')
    ecommerce_service = EcommerceService()

    urls = {
        'program_listing_url': reverse('program_listing_view'),
        'track_selection_url': strip_course_id(
            reverse('course_modes_choose', kwargs={'course_id': FAKE_COURSE_KEY})
        ),
        'commerce_api_url': reverse('commerce_api:v0:baskets:create'),
        'buy_button_url': ecommerce_service.get_checkout_page_url(*skus)
    }

    context = {
        'urls': urls,
        'show_program_listing': programs_config.enabled,
        'show_dashboard_tabs': True,
        'nav_hidden': True,
        'disable_courseware_js': True,
        'uses_pattern_library': True,
        'user_preferences': get_user_preferences(request.user),
        'program_data': program_data,
        'course_data': course_data,
        'certificate_data': certificate_data
    }

    return render_to_response('learner_dashboard/program_details.html', context)
Ejemplo n.º 19
0
    def test_in_progress_course_upgrade_deadline_check(self, offset,
                                                       mock_get_programs):
        """
        Verify that if the user's enrollment is not of the same type as the course run,
        the course will only count as in progress if there is another available seat with
        the right type for which the upgrade deadline has not passed.
        """
        course_run_key = generate_course_run_key()
        now = datetime.datetime.now(utc)
        upgrade_deadline = None if not offset else str(now +
                                                       datetime.timedelta(
                                                           days=offset))
        required_seat = SeatFactory(type='verified',
                                    upgrade_deadline=upgrade_deadline)
        enrolled_seat = SeatFactory(type='audit')
        seats = [required_seat, enrolled_seat]

        data = [
            ProgramFactory(courses=[
                CourseFactory(course_runs=[
                    CourseRunFactory(
                        key=course_run_key, type='verified', seats=seats),
                ]),
            ])
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(user=self.user,
                                course_id=course_run_key,
                                mode='audit')

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(uuid=program['uuid'],
                            completed=0,
                            in_progress=1 if offset in [None, 1] else 0,
                            not_started=1 if offset in [-1] else 0)
        ]

        self.assertEqual(meter.progress(count_only=True), expected)
Ejemplo n.º 20
0
    def render_to_fragment(self, request, program_uuid, **kwargs):
        """View details about a specific program."""
        programs_config = kwargs.get('programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not request.user.is_authenticated():
            raise Http404

        meter = ProgramProgressMeter(request.site, request.user, uuid=program_uuid)
        program_data = meter.programs[0]

        if not program_data:
            raise Http404

        program_data = ProgramDataExtender(program_data, request.user).extend()
        course_data = meter.progress(programs=[program_data], count_only=False)[0]
        certificate_data = get_certificates(request.user, program_data)

        program_data.pop('courses')
        skus = program_data.get('skus')
        ecommerce_service = EcommerceService()

        urls = {
            'program_listing_url': reverse('program_listing_view'),
            'track_selection_url': strip_course_id(
                reverse('course_modes_choose', kwargs={'course_id': FAKE_COURSE_KEY})
            ),
            'commerce_api_url': reverse('commerce_api:v0:baskets:create'),
            'buy_button_url': ecommerce_service.get_checkout_page_url(*skus)
        }

        context = {
            'urls': urls,
            'user_preferences': get_user_preferences(request.user),
            'program_data': program_data,
            'course_data': course_data,
            'certificate_data': certificate_data
        }

        html = render_to_string('learner_dashboard/program_details_fragment.html', context)
        program_details_fragment = Fragment(html)
        self.add_fragment_resource_urls(program_details_fragment)
        return program_details_fragment
Ejemplo n.º 21
0
    def render_to_fragment(self, request, **kwargs):
        """
        Render the program listing fragment.
        """
        user = request.user
        programs_config = kwargs.get('programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not user.is_authenticated():
            raise Http404

        meter = ProgramProgressMeter(request.site, user)

        context = {
            'marketing_url': get_program_marketing_url(programs_config),
            'programs': meter.engaged_programs,
            'progress': meter.progress()
        }
        html = render_to_string('learner_dashboard/programs_fragment.html', context)
        programs_fragment = Fragment(html)
        self.add_fragment_resource_urls(programs_fragment)

        return programs_fragment
Ejemplo n.º 22
0
    def test_in_progress_course_upgrade_deadline_check(self, offset, mock_get_programs):
        """
        Verify that if the user's enrollment is not of the same type as the course run,
        the course will only count as in progress if there is another available seat with
        the right type for which the upgrade deadline has not passed.
        """
        course_run_key = generate_course_run_key()
        now = datetime.datetime.now(utc)
        upgrade_deadline = None if not offset else str(now + datetime.timedelta(days=offset))
        required_seat = SeatFactory(type='verified', upgrade_deadline=upgrade_deadline)
        enrolled_seat = SeatFactory(type='audit')
        seats = [required_seat, enrolled_seat]

        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type='verified', seats=seats),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        CourseEnrollmentFactory(user=self.user, course_id=course_run_key, mode='audit')

        meter = ProgramProgressMeter(self.site, self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=0,
                in_progress=1 if offset in [None, 1] else 0,
                not_started=1 if offset in [-1] else 0,
                grades={course_run_key: 0.0},
            )
        ]

        self.assertEqual(meter.progress(count_only=True), expected)
Ejemplo n.º 23
0
    def test_in_progress_course_upgrade_deadline_check(self, modifier, mock_get_programs):
        """
        Verify that if the user's enrollment is not of the same type as the course run,
        the course will only count as in progress if there is another available seat with
        the right type, where the upgrade deadline has not expired.
        """
        course_run_key = generate_course_run_key()
        now = datetime.datetime.now(utc)
        date_modifier = modifier * datetime.timedelta(days=1)
        seat_with_upgrade_deadline = SeatFactory(type='test', upgrade_deadline=str(now + date_modifier))
        enrolled_seat = SeatFactory(type='verified')
        seats = [seat_with_upgrade_deadline, enrolled_seat]

        data = [
            ProgramFactory(
                courses=[
                    CourseFactory(course_runs=[
                        CourseRunFactory(key=course_run_key, type='test', seats=seats),
                    ]),
                ]
            )
        ]
        mock_get_programs.return_value = data

        self._create_enrollments(course_run_key)

        meter = ProgramProgressMeter(self.user)

        program = data[0]
        expected = [
            ProgressFactory(
                uuid=program['uuid'],
                completed=0,
                in_progress=1 if modifier == 1 else 0,
                not_started=1 if modifier == -1 else 0
            )
        ]
        self.assertEqual(meter.progress(count_only=True), expected)
Ejemplo n.º 24
0
    def render_to_fragment(self, request, **kwargs):
        """
        Render the program listing fragment.
        """
        user = request.user
        programs_config = kwargs.get(
            'programs_config') or ProgramsApiConfig.current()
        if not programs_config.enabled or not user.is_authenticated():
            raise Http404

        meter = ProgramProgressMeter(request.site, user)

        context = {
            'marketing_url': get_program_marketing_url(programs_config),
            'programs': meter.engaged_programs,
            'progress': meter.progress()
        }
        html = render_to_string('learner_dashboard/programs_fragment.html',
                                context)
        programs_fragment = Fragment(html)
        self.add_fragment_resource_urls(programs_fragment)

        return programs_fragment
Ejemplo n.º 25
0
    def get(self, request, enterprise_uuid):
        """
        Return a list of a enterprise learner's all enrolled programs with their progress.

        Args:
            request (Request): DRF request object.
            enterprise_uuid (string): UUID of an enterprise customer.
        """
        user = request.user

        enrollments = self._get_enterprise_course_enrollments(
            enterprise_uuid, user)
        meter = ProgramProgressMeter(request.site,
                                     user,
                                     enrollments=enrollments,
                                     mobile_only=False,
                                     include_course_entitlements=False)
        engaged_programs = meter.engaged_programs
        progress = meter.progress(programs=engaged_programs)
        programs = self._extract_minimal_required_programs_data(
            engaged_programs)
        programs = self._combine_programs_data_and_progress(programs, progress)

        return Response(programs)
Ejemplo n.º 26
0
    def render_to_fragment(self, request, program_uuid, **kwargs):  # lint-amnesty, pylint: disable=arguments-differ
        """View details about a specific program."""
        programs_config = kwargs.get(
            'programs_config') or ProgramsApiConfig.current()
        user = request.user
        if not programs_config.enabled or not request.user.is_authenticated:
            raise Http404

        meter = ProgramProgressMeter(request.site, user, uuid=program_uuid)
        program_data = meter.programs[0]

        if not program_data:
            raise Http404

        try:
            mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
        except ValueError:
            mobile_only = False

        program_data = ProgramDataExtender(program_data,
                                           user,
                                           mobile_only=mobile_only).extend()
        course_data = meter.progress(programs=[program_data],
                                     count_only=False)[0]
        certificate_data = get_certificates(user, program_data)

        program_data.pop('courses')
        skus = program_data.get('skus')
        ecommerce_service = EcommerceService()

        # TODO: Don't have business logic of course-certificate==record-available here in LMS.
        # Eventually, the UI should ask Credentials if there is a record available and get a URL from it.
        # But this is here for now so that we can gate this URL behind both this business logic and
        # a waffle flag. This feature is in active developoment.
        program_record_url = get_credentials_records_url(
            program_uuid=program_uuid)
        if not certificate_data:
            program_record_url = None

        industry_pathways = []
        credit_pathways = []
        try:
            for pathway_id in program_data['pathway_ids']:
                pathway = get_pathways(request.site, pathway_id)
                if pathway and pathway['email']:
                    if pathway['pathway_type'] == PathwayType.CREDIT.value:
                        credit_pathways.append(pathway)
                    elif pathway['pathway_type'] == PathwayType.INDUSTRY.value:
                        industry_pathways.append(pathway)
        # if pathway caching did not complete fully (no pathway_ids)
        except KeyError:
            pass

        urls = {
            'program_listing_url':
            reverse('program_listing_view'),
            'track_selection_url':
            strip_course_id(
                reverse('course_modes_choose',
                        kwargs={'course_id': FAKE_COURSE_KEY})),
            'commerce_api_url':
            reverse('commerce_api:v0:baskets:create'),
            'buy_button_url':
            ecommerce_service.get_checkout_page_url(*skus),
            'program_record_url':
            program_record_url,
        }

        context = {
            'urls':
            urls,
            'user_preferences':
            get_user_preferences(user),
            'program_data':
            program_data,
            'course_data':
            course_data,
            'certificate_data':
            certificate_data,
            'industry_pathways':
            industry_pathways,
            'credit_pathways':
            credit_pathways,
            'program_discussions_enabled':
            program_discussions_is_enabled(),
            'discussion_fragment':
            self.render_discussions_fragment(program_uuid, request)
        }

        html = render_to_string(
            'learner_dashboard/program_details_fragment.html', context)
        program_details_fragment = Fragment(html)
        self.add_fragment_resource_urls(program_details_fragment)
        return program_details_fragment
Ejemplo n.º 27
0
def get_experiment_user_metadata_context(course, user):
    """
    Return a context dictionary with the keys used by the user_metadata.html.
    """
    enrollment_mode = None
    enrollment_time = None
    enrollment = None
    # TODO: clean up as part of REVO-28 (START)
    has_non_audit_enrollments = None
    # TODO: clean up as part of REVO-28 (END)
    # TODO: clean up as part of REVEM-106 (START)
    program_key = None
    # TODO: clean up as part of REVEM-106 (END)
    try:
        # TODO: clean up as part of REVO-28 (START)
        user_enrollments = CourseEnrollment.objects.select_related(
            'course').filter(user_id=user.id)
        audit_enrollments = user_enrollments.filter(mode='audit')
        has_non_audit_enrollments = (len(audit_enrollments) !=
                                     len(user_enrollments))
        # TODO: clean up as part of REVO-28 (END)
        enrollment = CourseEnrollment.objects.select_related('course').get(
            user_id=user.id, course_id=course.id)
        if enrollment.is_active:
            enrollment_mode = enrollment.mode
            enrollment_time = enrollment.created

            # TODO: clean up as part of REVEM-106 (START)
            # get program data for this course
            request = get_current_request()
            if request:
                enrollment_list = [enrollment]
                meter = ProgramProgressMeter(request.site,
                                             user,
                                             enrollments=enrollment_list)
                if meter.engaged_programs and meter.engaged_programs[0]:
                    org_name = None
                    courses_not_started = 0
                    courses_in_progress = 0
                    courses_completed = 0
                    program_data = meter.engaged_programs[0]
                    program_data = ProgramDataExtender(
                        program_data, user, mobile_only=False).extend()
                    program_orgs = program_data.get(
                        'credit_backing_organizations')
                    if program_orgs and program_orgs[0]:
                        org = program_orgs[0]
                        org_name = org.get('name')
                    if meter.progress() and meter.progress()[0]:
                        progress = meter.progress()[0]
                        courses_not_started = progress.get('not_started')
                        courses_in_progress = progress.get('in_progress')
                        courses_completed = progress.get('completed')
                    program_key = {
                        'uuid': program_data.get('uuid'),
                        'title': program_data.get('title'),
                        'marketing_url': program_data.get('marketing_url'),
                        'org_name': org_name,
                        'courses_not_started': courses_not_started,
                        'courses_in_progress': courses_in_progress,
                        'courses_completed': courses_completed,
                    }
            # TODO: clean up as part of REVEM-106 (END)
    except CourseEnrollment.DoesNotExist:
        pass  # Not enrolled, used the default None values

    # upgrade_link and upgrade_date should be None if user has passed their dynamic pacing deadline.
    upgrade_link, upgrade_date = check_and_get_upgrade_link_and_date(
        user, enrollment, course)
    has_staff_access = has_staff_access_to_preview_mode(user, course.id)
    forum_roles = []
    if user.is_authenticated:
        forum_roles = list(
            Role.objects.filter(
                users=user,
                course_id=course.id).values_list('name').distinct())

    # get user partition data
    if user.is_authenticated():
        partition_groups = get_all_partitions_for_course(course)
        user_partitions = get_user_partition_groups(course.id,
                                                    partition_groups, user,
                                                    'name')
    else:
        user_partitions = {}

    return {
        'upgrade_link': upgrade_link,
        'upgrade_price': unicode(get_cosmetic_verified_display_price(course)),
        'enrollment_mode': enrollment_mode,
        'enrollment_time': enrollment_time,
        'pacing_type':
        'self_paced' if course.self_paced else 'instructor_paced',
        'upgrade_deadline': upgrade_date,
        'course_key': course.id,
        'course_start': course.start,
        'course_end': course.end,
        'has_staff_access': has_staff_access,
        'forum_roles': forum_roles,
        'partition_groups': user_partitions,
        # TODO: clean up as part of REVO-28 (START)
        'has_non_audit_enrollments': has_non_audit_enrollments,
        # TODO: clean up as part of REVO-28 (END)
        # TODO: clean up as part of REVEM-106 (START)
        'program_key_fields': program_key,
        # TODO: clean up as part of REVEM-106 (END)
    }