def test_get_many(self, mock_warning, mock_info): pathways = PathwayFactory.create_batch(3) # Cache details for 2 of 3 programs. partial_pathways = { PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']): pathway for pathway in pathways[:2] } cache.set_many(partial_pathways, None) # When called before pathways are cached, the function should return an # empty list and log a warning. assert get_pathways(self.site) == [] mock_warning.assert_called_once_with( 'Failed to get credit pathway ids from the cache.') mock_warning.reset_mock() # Cache all 3 pathways cache.set( SITE_PATHWAY_IDS_CACHE_KEY_TPL.format(domain=self.site.domain), [pathway['id'] for pathway in pathways], None) actual_pathways = get_pathways(self.site) # The 2 cached pathways should be returned while info and warning # messages should be logged for the missing one. assert {pathway['id'] for pathway in actual_pathways} ==\ {pathway['id'] for pathway in partial_pathways.values()} mock_info.assert_called_with( 'Failed to get details for 1 pathways. Retrying.') mock_warning.assert_called_with( 'Failed to get details for credit pathway {id} from the cache.'. format(id=pathways[2]['id'])) mock_warning.reset_mock() # We can't use a set comparison here because these values are dictionaries # and aren't hashable. We've already verified that all pathways came out # of the cache above, so all we need to do here is verify the accuracy of # the data itself. for pathway in actual_pathways: key = PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']) assert pathway == partial_pathways[key] # Cache details for all 3 pathways. all_pathways = { PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']): pathway for pathway in pathways } cache.set_many(all_pathways, None) actual_pathways = get_pathways(self.site) # All 3 pathways should be returned. assert {pathway['id'] for pathway in actual_pathways} ==\ {pathway['id'] for pathway in all_pathways.values()} assert not mock_warning.called for pathway in actual_pathways: key = PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']) assert pathway == all_pathways[key]
def test_get_one(self, mock_warning, _mock_info): expected_pathway = PathwayFactory() expected_id = expected_pathway['id'] self.assertEqual(get_pathways(self.site, pathway_id=expected_id), None) mock_warning.assert_called_once_with( u'Failed to get details for credit pathway {id} from the cache.'. format(id=expected_id)) mock_warning.reset_mock() cache.set(PATHWAY_CACHE_KEY_TPL.format(id=expected_id), expected_pathway, None) actual_pathway = get_pathways(self.site, pathway_id=expected_id) self.assertEqual(actual_pathway, expected_pathway) self.assertFalse(mock_warning.called)
def test_get_many_with_missing(self, mock_cache, mock_warning, mock_info): pathways = PathwayFactory.create_batch(3) all_pathways = { PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']): pathway for pathway in pathways } partial_pathways = { PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']): pathway for pathway in pathways[:2] } def fake_get_many(keys): if len(keys) == 1: return {PATHWAY_CACHE_KEY_TPL.format(id=pathways[-1]['id']): pathways[-1]} else: return partial_pathways mock_cache.get.return_value = [pathway['id'] for pathway in pathways] mock_cache.get_many.side_effect = fake_get_many actual_pathways = get_pathways(self.site) # All 3 cached pathways should be returned. An info message should be # logged about the one that was initially missing, but the code should # be able to stitch together all the details. self.assertEqual( set(pathway['id'] for pathway in actual_pathways), set(pathway['id'] for pathway in all_pathways.values()) ) self.assertFalse(mock_warning.called) mock_info.assert_called_with('Failed to get details for 1 pathways. Retrying.') for pathway in actual_pathways: key = PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']) self.assertEqual(pathway, all_pathways[key])
def test_get_one(self, mock_warning, _mock_info): expected_pathway = PathwayFactory() expected_id = expected_pathway['id'] assert get_pathways(self.site, pathway_id=expected_id) is None mock_warning.assert_called_once_with( f'Failed to get details for credit pathway {expected_id} from the cache.' ) mock_warning.reset_mock() cache.set(PATHWAY_CACHE_KEY_TPL.format(id=expected_id), expected_pathway, None) actual_pathway = get_pathways(self.site, pathway_id=expected_id) assert actual_pathway == expected_pathway assert not mock_warning.called
def test_get_one(self, mock_warning, _mock_info): expected_pathway = PathwayFactory() expected_id = expected_pathway['id'] self.assertEqual(get_pathways(self.site, pathway_id=expected_id), None) mock_warning.assert_called_once_with( 'Failed to get details for credit pathway {id} from the cache.'.format(id=expected_id) ) mock_warning.reset_mock() cache.set( PATHWAY_CACHE_KEY_TPL.format(id=expected_id), expected_pathway, None ) actual_pathway = get_pathways(self.site, pathway_id=expected_id) self.assertEqual(actual_pathway, expected_pathway) self.assertFalse(mock_warning.called)
def get_industry_and_credit_pathways(program_data, site): """Returns pathways of a program.""" industry_pathways = [] credit_pathways = [] try: for pathway_id in program_data['pathway_ids']: pathway = get_pathways(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 return industry_pathways, credit_pathways
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 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(request.user), 'program_data': program_data, 'course_data': course_data, 'certificate_data': certificate_data, 'industry_pathways': industry_pathways, 'credit_pathways': credit_pathways, } 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
def test_get_many(self, mock_warning, mock_info): pathways = PathwayFactory.create_batch(3) # Cache details for 2 of 3 programs. partial_pathways = { PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']): pathway for pathway in pathways[:2] } cache.set_many(partial_pathways, None) # When called before pathways are cached, the function should return an # empty list and log a warning. self.assertEqual(get_pathways(self.site), []) mock_warning.assert_called_once_with('Failed to get credit pathway ids from the cache.') mock_warning.reset_mock() # Cache all 3 pathways cache.set( SITE_PATHWAY_IDS_CACHE_KEY_TPL.format(domain=self.site.domain), [pathway['id'] for pathway in pathways], None ) actual_pathways = get_pathways(self.site) # The 2 cached pathways should be returned while info and warning # messages should be logged for the missing one. self.assertEqual( set(pathway['id'] for pathway in actual_pathways), set(pathway['id'] for pathway in partial_pathways.values()) ) mock_info.assert_called_with('Failed to get details for 1 pathways. Retrying.') mock_warning.assert_called_with( 'Failed to get details for credit pathway {id} from the cache.'.format(id=pathways[2]['id']) ) mock_warning.reset_mock() # We can't use a set comparison here because these values are dictionaries # and aren't hashable. We've already verified that all pathways came out # of the cache above, so all we need to do here is verify the accuracy of # the data itself. for pathway in actual_pathways: key = PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']) self.assertEqual(pathway, partial_pathways[key]) # Cache details for all 3 pathways. all_pathways = { PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']): pathway for pathway in pathways } cache.set_many(all_pathways, None) actual_pathways = get_pathways(self.site) # All 3 pathways should be returned. self.assertEqual( set(pathway['id'] for pathway in actual_pathways), set(pathway['id'] for pathway in all_pathways.values()) ) self.assertFalse(mock_warning.called) for pathway in actual_pathways: key = PATHWAY_CACHE_KEY_TPL.format(id=pathway['id']) self.assertEqual(pathway, all_pathways[key])
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 pathways = [] try: for pathway_id in program_data['pathway_ids']: pathway = get_pathways(request.site, pathway_id) if pathway and pathway['email']: 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(request.user), 'program_data': program_data, 'course_data': course_data, 'certificate_data': certificate_data, 'pathways': pathways, } 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