Example #1
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
        site = request.site
        if not programs_config.enabled or not request.user.is_authenticated:
            raise Http404
        try:
            mobile_only = json.loads(request.GET.get('mobile_only', 'false'))
        except ValueError:
            mobile_only = False

        program_data, course_data = get_program_and_course_data(site, user, program_uuid, mobile_only)

        if not program_data:
            raise Http404

        certificate_data = get_certificates(user, program_data)
        program_data.pop('courses')

        urls = get_program_urls(program_data)
        if not certificate_data:
            urls['program_record_url'] = None

        industry_pathways, credit_pathways = get_industry_and_credit_pathways(program_data, site)

        program_discussion_lti = ProgramDiscussionLTI(program_uuid, request)
        program_live_lti = ProgramLiveLTI(program_uuid, request)

        def program_tab_view_enabled() -> bool:
            return program_tab_view_is_enabled() and (
                industry_pathways or
                credit_pathways or
                program_discussion_lti.is_configured or
                program_live_lti.is_configured
            )

        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_tab_view_enabled': program_tab_view_enabled(),
            'discussion_fragment': {
                'configured': program_discussion_lti.is_configured,
                'iframe': program_discussion_lti.render_iframe()
            },
            'live_fragment': {
                'configured': program_live_lti.is_configured,
                'iframe': program_live_lti.render_iframe()
            }
        }

        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
Example #2
0
    def test_get_certificates(self, mock_get_credentials):
        """
        Verify course and program certificates are found when present. Only one
        course run certificate should be returned for each course when the user
        has earned certificates in multiple runs of the same course.
        """
        expected = [
            {
                'type': 'course',
                'title': course_run['title'],
                'url': course_run['certificate_url'],
            } for course_run in self._first_course_runs()
        ]

        expected.append({
            'type': 'program',
            'title': self.program['title'],
            'url': self.program_certificate_url,
        })

        mock_get_credentials.return_value = [{
            'certificate_url': self.program_certificate_url
        }]

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #3
0
    def test_get_certificates(self, mock_get_credentials):
        """
        Verify course and program certificates are found when present. Only one
        course run certificate should be returned for each course when the user
        has earned certificates in multiple runs of the same course.
        """
        expected = []
        for course in self.program['courses']:
            # Give all course runs a certificate URL, but only expect one to come
            # back. This verifies the break in the function under test that ensures
            # only one certificate per course comes back.
            for index, course_run in enumerate(course['course_runs']):
                course_run['certificate_url'] = self.course_certificate_url

                if index == 0:
                    expected.append({
                        'type': 'course',
                        'title': course_run['title'],
                        'url': self.course_certificate_url,
                    })

        expected.append({
            'type': 'program',
            'title': self.program['title'],
            'url': self.program_certificate_url,
        })

        mock_get_credentials.return_value = [{
            'certificate_url': self.program_certificate_url
        }]

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #4
0
    def test_get_certificates(self, mock_get_credentials):
        """
        Verify course and program certificates are found when present. Only one
        course run certificate should be returned for each course when the user
        has earned certificates in multiple runs of the same course.
        """
        expected = []
        for course in self.program['courses']:
            # Give all course runs a certificate URL, but only expect one to come
            # back. This verifies the break in the function under test that ensures
            # only one certificate per course comes back.
            for index, course_run in enumerate(course['course_runs']):
                course_run['certificate_url'] = self.course_certificate_url

                if index == 0:
                    expected.append({
                        'type': 'course',
                        'title': course_run['title'],
                        'url': self.course_certificate_url,
                    })

        expected.append({
            'type': 'program',
            'title': self.program['title'],
            'url': self.program_certificate_url,
        })

        mock_get_credentials.return_value = [{
            'certificate_url': self.program_certificate_url
        }]

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #5
0
    def test_course_run_certificates_missing(self, mock_get_credentials):
        """
        Verify program certificates are not included when the learner has not earned all course certificates.
        """
        # make the first course have no certification, the second have no url...
        for course_index, course in enumerate(self.program['courses']):
            for index, course_run in enumerate(course['course_runs']):
                if course_index == 0:
                    course_run['may_certify'] = False
                elif course_index == 1:
                    course_run['certificate_url'] = False

        # ...but the third course should still be included
        expected = [{
            'type':
            'course',
            'title':
            self.program['courses'][2]['course_runs'][0]['title'],
            'url':
            self.program['courses'][2]['course_runs'][0]['certificate_url'],
        }]

        mock_get_credentials.return_value = [{
            'certificate_url':
            self.program_certificate_url
        }]

        certificates = get_certificates(self.user, self.program)
        self.assertTrue(mock_get_credentials.called)
        self.assertEqual(certificates, expected)
Example #6
0
    def test_get_certificates(self, mock_get_credentials):
        """
        Verify course and program certificates are found when present. Only one
        course run certificate should be returned for each course when the user
        has earned certificates in multiple runs of the same course.
        """
        expected = [
            {
                'type': 'course',
                'title': course_run['title'],
                'url': course_run['certificate_url'],
            } for course_run in self._first_course_runs()
        ]

        expected.append({
            'type': 'program',
            'title': self.program['title'],
            'url': self.program_certificate_url,
        })

        mock_get_credentials.return_value = [{
            'certificate_url': self.program_certificate_url
        }]

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #7
0
 def test_course_run_certificates_missing(self, mock_get_credentials):
     """
     Verify an empty list is returned when course run certificates are missing,
     and that no attempt is made to retrieve program certificates.
     """
     certificates = get_certificates(self.user, self.program)
     self.assertEqual(certificates, [])
     self.assertFalse(mock_get_credentials.called)
Example #8
0
 def test_course_run_certificates_missing(self, mock_get_credentials):
     """
     Verify an empty list is returned when course run certificates are missing,
     and that no attempt is made to retrieve program certificates.
     """
     certificates = get_certificates(self.user, self.program)
     self.assertEqual(certificates, [])
     self.assertFalse(mock_get_credentials.called)
Example #9
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
Example #10
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
Example #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()

        # 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
Example #12
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)
Example #13
0
    def test_course_run_certificates_missing(self, mock_get_credentials):
        """
        Verify program certificates are retrieved even if the learner has not earned any course certificates.
        """
        expected = [{
            'type': 'program',
            'title': self.program['title'],
            'url': self.program_certificate_url,
        }]
        mock_get_credentials.return_value = [{'certificate_url': self.program_certificate_url}]

        certificates = get_certificates(self.user, self.program)
        self.assertTrue(mock_get_credentials.called)
        self.assertEqual(certificates, expected)
Example #14
0
    def test_course_run_certificates_missing(self, mock_get_credentials):
        """
        Verify program certificates are retrieved even if the learner has not earned any course certificates.
        """
        expected = [{
            'type': 'program',
            'title': self.program['title'],
            'url': self.program_certificate_url,
        }]
        mock_get_credentials.return_value = [{'certificate_url': self.program_certificate_url}]

        certificates = get_certificates(self.user, self.program)
        self.assertTrue(mock_get_credentials.called)
        self.assertEqual(certificates, expected)
Example #15
0
    def test_program_certificate_missing(self, mock_get_credentials):
        """
        Verify that the function can handle a missing program certificate.
        """
        expected = [{
            'type': 'course',
            'title': course_run['title'],
            'url': course_run['certificate_url'],
        } for course_run in self._first_course_runs()]

        mock_get_credentials.return_value = []

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #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)
Example #17
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)
Example #18
0
    def test_program_certificate_missing(self, mock_get_credentials):
        """
        Verify that the function can handle a missing program certificate.
        """
        expected = [
            {
                'type': 'course',
                'title': course_run['title'],
                'url': course_run['certificate_url'],
            } for course_run in self._first_course_runs()
        ]

        mock_get_credentials.return_value = []

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #19
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)
Example #20
0
    def test_program_certificate_missing(self, mock_get_credentials):
        """
        Verify that the function can handle a missing program certificate.
        """
        expected = []
        for course in self.program['courses']:
            for index, course_run in enumerate(course['course_runs']):
                course_run['certificate_url'] = self.course_certificate_url

                if index == 0:
                    expected.append({
                        'type': 'course',
                        'title': course_run['title'],
                        'url': self.course_certificate_url,
                    })

        mock_get_credentials.return_value = []

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #21
0
    def test_program_certificate_missing(self, mock_get_credentials):
        """
        Verify that the function can handle a missing program certificate.
        """
        expected = []
        for course in self.program['courses']:
            for index, course_run in enumerate(course['course_runs']):
                course_run['certificate_url'] = self.course_certificate_url

                if index == 0:
                    expected.append({
                        'type': 'course',
                        'title': course_run['title'],
                        'url': self.course_certificate_url,
                    })

        mock_get_credentials.return_value = []

        certificates = get_certificates(self.user, self.program)
        self.assertEqual(certificates, expected)
Example #22
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
Example #23
0
    def get(self, request, program_uuid):
        """
        Retrieves progress details of a user in a specified program.

        Args:
            request (Request): Django request object.
            program_uuid (string): URI element specifying uuid of the program.

        Return:
        """
        user = request.user
        site = request.site
        program_data, course_data = get_program_and_course_data(
            site, user, program_uuid)
        if not program_data:
            return Response(status=404,
                            data={'error_code': 'No program data available.'})

        certificate_data = get_certificates(user, program_data)
        program_data.pop('courses')

        urls = get_program_urls(program_data)
        if not certificate_data:
            urls['program_record_url'] = None

        industry_pathways, credit_pathways = get_industry_and_credit_pathways(
            program_data, site)

        return Response({
            'urls': urls,
            'program_data': program_data,
            'course_data': course_data,
            'certificate_data': certificate_data,
            'industry_pathways': industry_pathways,
            'credit_pathways': credit_pathways,
        })
Example #24
0
    def test_course_run_certificates_missing(self, mock_get_credentials):
        """
        Verify program certificates are not included when the learner has not earned all course certificates.
        """
        # make the first course have no certification, the second have no url...
        for course_index, course in enumerate(self.program['courses']):
            for index, course_run in enumerate(course['course_runs']):
                if course_index == 0:
                    course_run['may_certify'] = False
                elif course_index == 1:
                    course_run['certificate_url'] = False

        # ...but the third course should still be included
        expected = [{
            'type': 'course',
            'title': self.program['courses'][2]['course_runs'][0]['title'],
            'url': self.program['courses'][2]['course_runs'][0]['certificate_url'],
        }]

        mock_get_credentials.return_value = [{'certificate_url': self.program_certificate_url}]

        certificates = get_certificates(self.user, self.program)
        self.assertTrue(mock_get_credentials.called)
        self.assertEqual(certificates, expected)
Example #25
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