Example #1
0
 def test_retrieve_with_sorting_flag(self, order_courses_by_start_date):
     """ Verify the number of queries is the same with sorting flag set to true. """
     course_list = CourseFactory.create_batch(3)
     for course in course_list:
         CourseRunFactory(course=course)
     program = ProgramFactory(courses=course_list, order_courses_by_start_date=order_courses_by_start_date)
     with self.assertNumQueries(90):
         self.assert_retrieve_success(program)
     self.assertEqual(course_list, list(program.courses.all()))  # pylint: disable=no-member
    def test_list_query(self):
        """ Verify the endpoint returns a filtered list of courses """
        title = 'Some random title'
        courses = CourseFactory.create_batch(3, title=title)
        courses = sorted(courses, key=lambda course: course.key.lower())
        query = 'title:' + title
        url = '{root}?q={query}'.format(root=reverse('api:v1:course-list'), query=query)

        response = self.client.get(url)
        self.assertListEqual(response.data['results'], self.serialize_course(courses, many=True))
Example #3
0
    def test_get_distinct_count_runs_query_when_cache_empty(self):
        """ Verify that get_distinct_count runs the query and caches/returns the distinct_count."""
        course = CourseFactory()
        CourseRunFactory(title='foo', course=course)
        CourseRunFactory(title='foo', course=course)

        query = DistinctCountsSearchQuery()
        query.aggregation_key = 'aggregation_key'
        query.add_filter(SQ(title='foo'))
        query.add_model(CourseRun)

        assert query._distinct_hit_count is None
        assert query.get_distinct_count() == 1
        assert query._distinct_hit_count == 1
Example #4
0
    def create_curriculum(self, parent_program):
        person = PersonFactory()
        course = CourseFactory(partner=self.partner)
        CourseRunFactory(course=course, staff=[person])
        CourseRunFactory(course=course, staff=[person])

        curriculum = CurriculumFactory(
            program=parent_program
        )
        CurriculumCourseMembershipFactory(
            course=course,
            curriculum=curriculum
        )
        return curriculum
Example #5
0
    def test_ensure_draft_world_not_draft_course_run_given(self):
        course = CourseFactory()
        course_run = CourseRunFactory(course=course)
        verified_seat = SeatFactory(type='verified', course_run=course_run)
        audit_seat = SeatFactory(type='audit', course_run=course_run)
        course_run.seats.add(verified_seat, audit_seat)

        ensured_draft_course_run = utils.ensure_draft_world(course_run)
        not_draft_course_run = CourseRun.objects.get(uuid=course_run.uuid)

        self.assertNotEqual(ensured_draft_course_run, not_draft_course_run)
        self.assertEqual(ensured_draft_course_run.uuid,
                         not_draft_course_run.uuid)
        self.assertTrue(ensured_draft_course_run.draft)
        self.assertNotEqual(ensured_draft_course_run.course,
                            not_draft_course_run.course)
        self.assertEqual(ensured_draft_course_run.course.uuid,
                         not_draft_course_run.course.uuid)

        # Check slugs are equal
        self.assertEqual(ensured_draft_course_run.slug,
                         not_draft_course_run.slug)

        # Seat checks
        draft_seats = ensured_draft_course_run.seats.all()
        not_draft_seats = Seat.objects.filter(course_run=not_draft_course_run)
        self.assertNotEqual(draft_seats, not_draft_seats)
        for i, __ in enumerate(draft_seats):
            self.assertEqual(draft_seats[i].price, not_draft_seats[i].price)
            self.assertEqual(draft_seats[i].sku, not_draft_seats[i].sku)
            self.assertNotEqual(draft_seats[i].course_run,
                                not_draft_seats[i].course_run)
            self.assertEqual(draft_seats[i].course_run.uuid,
                             not_draft_seats[i].course_run.uuid)
            self.assertEqual(draft_seats[i].official_version,
                             not_draft_seats[i])
            self.assertEqual(not_draft_seats[i].draft_version, draft_seats[i])

        # Check draft course is also created
        not_draft_course = Course.objects.get(uuid=course.uuid)
        draft_course = ensured_draft_course_run.course
        self.assertNotEqual(draft_course, not_draft_course)
        self.assertEqual(draft_course.uuid, not_draft_course.uuid)
        self.assertTrue(draft_course.draft)

        # Check official and draft versions match up
        self.assertEqual(ensured_draft_course_run.official_version,
                         not_draft_course_run)
        self.assertEqual(not_draft_course_run.draft_version,
                         ensured_draft_course_run)
Example #6
0
 def test_get(self):
     """
     Verify that GET request works as expected for `AggregateSearchViewSet`
     """
     CourseFactory(key='course:edX+DemoX', title='ABCs of Ͳҽʂէìղց')
     expected = {'previous': None, 'results': [], 'next': None, 'count': 0}
     query = {
         'content_type': 'course',
         'aggregation_key': ['course:edX+DemoX']
     }
     qs = urllib.parse.urlencode(query)
     url = '{path}?{qs}'.format(path=self.path, qs=qs)
     response = self.client.get(url)
     assert response.json() == expected
    def test_facet_counts_includes_distinct_counts(self):
        """ Verify that facet_counts include distinct counts. """
        course = CourseFactory()
        CourseRunFactory(title='foo',
                         pacing_type='self_paced',
                         hidden=True,
                         course=course)
        CourseRunFactory(title='foo',
                         pacing_type='self_paced',
                         hidden=True,
                         course=course)
        CourseRunFactory(title='foo',
                         pacing_type='instructor_paced',
                         hidden=False,
                         course=course)

        # Make sure to add both a field facet and a query facet so that we can be sure that both work.
        queryset = DSLFacetedSearch(
            index=CourseRunDocument._index._name).filter('term', title='foo')
        facet_field = 'pacing_type'
        agg_filter = ESDSLQ('match_all')
        agg = TermsFacet(field=facet_field)
        queryset.aggs.bucket('_filter_' + facet_field,
                             'filter',
                             filter=agg_filter).bucket(facet_field,
                                                       agg.get_aggregation())
        queryset.aggs.bucket('_query_{0}'.format('hidden'),
                             'filter',
                             filter=ESDSLQ('bool',
                                           filter=ESDSLQ('term', hidden=True)))
        dc_queryset = DistinctCountsSearchQuerySet.from_queryset(
            queryset).with_distinct_counts('aggregation_key')
        facet_counts = dc_queryset.facet_counts()

        # Field facets are expected to be formatted as a list of three-tuples (field_value, count, distinct_count)
        for val, count, distinct_count in facet_counts['fields'][
                'pacing_type']:
            assert val in {'self_paced', 'instructor_paced'}
            if val == 'self_paced':
                assert count == 2
                assert distinct_count == 1
            elif val == 'instructor_paced':
                assert count == 1
                assert distinct_count == 1

        # Query facets are expected to be formatted as a dictionary mapping facet_names to two-tuples (count,
        # distinct_count)
        hidden_count, hidden_distinct_count = facet_counts['queries']['hidden']
        assert hidden_count == 2
        assert hidden_distinct_count == 1
Example #8
0
 def test_retrieve_with_sorting_flag(self, order_courses_by_start_date):
     """ Verify the number of queries is the same with sorting flag set to true. """
     course_list = CourseFactory.create_batch(3)
     for course in course_list:
         CourseRunFactory(course=course)
     program = ProgramFactory(
         courses=course_list,
         order_courses_by_start_date=order_courses_by_start_date)
     # property does not have the right values while being indexed
     del program._course_run_weeks_to_complete
     with self.assertNumQueries(26):
         response = self.assert_retrieve_success(program)
     assert response.data == self.serialize_program(program)
     self.assertEqual(course_list, list(program.courses.all()))  # pylint: disable=no-member
    def test_selected_query_facet(self):
        """ Verify that the response is accurate when a query facet is selected."""
        now = datetime.datetime.now(pytz.UTC)
        current = (now - datetime.timedelta(days=1),
                   now + datetime.timedelta(days=1))
        archived = (now - datetime.timedelta(days=2),
                    now - datetime.timedelta(days=1))

        course = CourseFactory(partner=self.partner)
        run_1 = self.build_courserun(course=course,
                                     start=current[0],
                                     end=current[1],
                                     pacing_type='self_paced')
        run_2 = self.build_courserun(course=course,
                                     start=current[0],
                                     end=current[1],
                                     pacing_type='self_paced')
        self.build_courserun(course=course,
                             start=archived[0],
                             end=archived[1],
                             pacing_type='self_paced')
        self.build_courserun(course=course,
                             start=archived[0],
                             end=archived[1],
                             pacing_type='instructor_paced')

        response = self.get_response(
            {'selected_query_facets': 'availability_current'})
        assert response.status_code == 200

        assert response.data['objects']['count'] == 2
        assert response.data['objects']['distinct_count'] == 1
        expected = sorted([run_1.key, run_2.key])
        actual = sorted(
            [run['key'] for run in response.data['objects']['results']])
        assert expected == actual

        pacing_types = {
            facet['text']: facet
            for facet in response.data['fields']['pacing_type']
        }
        assert pacing_types['self_paced']['count'] == 2
        assert pacing_types['self_paced']['distinct_count'] == 1
        expected_query_params = {
            'selected_query_facets': ['availability_current'],
            'selected_facets': ['pacing_type_exact:self_paced'],
        }
        self.assert_url_path_and_query(
            pacing_types['self_paced']['narrow_url'], self.path,
            expected_query_params)
Example #10
0
    def test_editable_get_gives_drafts(self):
        draft = CourseFactory(partner=self.partner,
                              uuid=self.course.uuid,
                              key=self.course.key,
                              draft=True)
        self.course.draft_version = draft
        self.course.save()
        extra = CourseFactory(partner=self.partner)

        response = self.client.get(
            reverse('api:v1:course-detail', kwargs={'key': self.course.uuid}) +
            '?editable=1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data, self.serialize_course(draft,
                                                              many=False))

        response = self.client.get(
            reverse('api:v1:course-detail', kwargs={'key': extra.uuid}) +
            '?editable=1')
        # TODO: Swap commented lines with one below it when completing DISCO-818
        # self.assertEqual(response.status_code, 200)
        # self.assertEqual(response.data, self.serialize_course(extra, many=False))
        self.assertEqual(response.status_code, 404)
Example #11
0
 def test_update_fails_with_multiple_errors(self, course_data,
                                            expected_error_message):
     # TODO: Swap commented line with one below it when completing DISCO-818
     # course = CourseFactory(partner=self.partner, key='Org/Course/Number')
     course = CourseFactory(partner=self.partner,
                            key='Org/Course/Number',
                            draft=True)
     url = reverse('api:v1:course-detail', kwargs={'key': course.uuid})
     mode1 = SeatTypeFactory(name='Mode1')
     SeatTypeFactory(name='Mode2')
     CourseEntitlementFactory(course=course, mode=mode1, sku=None)
     response = self.client.patch(url, course_data, format='json')
     self.assertEqual(response.status_code, 400)
     self.assertEqual(response.data, expected_error_message)
Example #12
0
 def test_list_with_org_multiple(self):
     org1 = OrganizationFactory()
     org2 = OrganizationFactory()
     course1 = CourseFactory(authoring_organizations=[org1])
     course2 = CourseFactory(authoring_organizations=[org2])
     with mock.patch.object(MarketingSitePeople,
                            'update_or_publish_person'):
         person1 = PersonFactory(partner=self.partner)
         person2 = PersonFactory(partner=self.partner)
         PersonFactory(partner=self.partner)
         CourseRunFactory(staff=[person1], course=course1)
         CourseRunFactory(staff=[person2], course=course2)
         url = '{url}?org={org1_key}&org={org2_key}'.format(
             url=self.people_list_url,
             org1_key=org1.key,
             org2_key=org2.key,
         )
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(len(response.data['results']), 2)
         self.assertCountEqual(
             response.data['results'],
             self.serialize_person([person1, person2], many=True))
Example #13
0
    def test_data(self):
        user = UserFactory()
        catalog = CatalogFactory(query='*:*', viewers=[user])  # We intentionally use a query for all Courses.
        courses = CourseFactory.create_batch(10)
        serializer = CatalogSerializer(catalog)

        expected = {
            'id': catalog.id,
            'name': catalog.name,
            'query': catalog.query,
            'courses_count': len(courses),
            'viewers': [user.username]
        }
        self.assertDictEqual(serializer.data, expected)
Example #14
0
    def test_field_facet_response(self):
        """ Verify that field facets are included in the response and that they are properly formatted."""
        for course in [CourseFactory(partner=self.partner), CourseFactory(partner=self.partner)]:
            self.build_courserun(course=course)
            self.build_courserun(course=course)
        self.build_program(partner=self.partner)

        response = self.get_response()
        assert response.status_code == 200

        expected_facets = DistinctCountsAggregateFacetSearchSerializer.Meta.field_options.keys()
        assert sorted(expected_facets) == sorted(response.data['fields'].keys())

        content_types = {facet['text']: facet for facet in response.data['fields']['content_type']}
        assert content_types['courserun']['count'] == 4
        assert content_types['courserun']['distinct_count'] == 2
        narrow_url = content_types['courserun']['narrow_url']
        self.assert_url_path_and_query(narrow_url, self.path, {'selected_facets': ['content_type_exact:courserun']})

        assert content_types['program']['count'] == 1
        assert content_types['program']['distinct_count'] == 1
        narrow_url = content_types['program']['narrow_url']
        self.assert_url_path_and_query(narrow_url, self.path, {'selected_facets': ['content_type_exact:program']})
Example #15
0
    def create_mock_courses_and_runs(self, programs):
        for program in programs:
            for course_code in program.get('course_codes', []):
                key = '{org}+{course}'.format(
                    org=course_code['organization']['key'],
                    course=course_code['key'])
                course = CourseFactory(key=key, partner=self.partner)

                for course_run in course_code['run_modes']:
                    CourseRunFactory(course=course,
                                     key=course_run['course_key'])

                # Add an additional course run that should be excluded
                CourseRunFactory(course=course)
Example #16
0
    def setUpClass(cls):
        super().setUpClass()
        cls.user = UserFactory(is_staff=True)
        cls.courses = factories.CourseFactory.create_batch(
            3, title='Some random course title')

        first_instructor = PersonFactory(given_name="First",
                                         family_name="Instructor")
        second_instructor = PersonFactory(given_name="Second",
                                          family_name="Instructor")
        cls.instructors = [first_instructor, second_instructor]

        cls.organizations = OrganizationFactory.create_batch(3)
        cls.organization_extensions = []

        for instructor in cls.instructors:
            PositionFactory(organization=cls.organizations[0],
                            title="professor",
                            person=instructor)

        cls.course_runs = [
            factories.CourseRunFactory(course=course) for course in cls.courses
        ]

        for organization in cls.organizations:
            cls.organization_extensions.append(
                factories.OrganizationExtensionFactory(
                    organization=organization))

        disco_course = CourseFactory(
            authoring_organizations=[cls.organizations[0]])
        disco_course2 = CourseFactory(
            authoring_organizations=[cls.organizations[1]])
        CourseRunFactory(course=disco_course, staff=[first_instructor])
        CourseRunFactory(course=disco_course2, staff=[second_instructor])

        cls.user.groups.add(cls.organization_extensions[0].group)
Example #17
0
 def setUp(self):
     super(CourseRunViewSetTests, self).setUp()
     self.user = UserFactory(is_staff=True)
     self.client.force_authenticate(self.user)
     self.course_run = CourseRunFactory(course__partner=self.partner)
     self.course_run_2 = CourseRunFactory(course__key='Test+Course',
                                          course__partner=self.partner)
     self.draft_course = CourseFactory(partner=self.partner, draft=True)
     self.draft_course_run = CourseRunFactory(course=self.draft_course,
                                              draft=True)
     self.draft_course_run.course.authoring_organizations.add(
         OrganizationFactory(key='course-id'))
     self.refresh_index()
     self.request = APIRequestFactory().get('/')
     self.request.user = self.user
    def test_editable_list_gives_drafts(self):
        draft = CourseFactory(partner=self.partner,
                              uuid=self.course.uuid,
                              key=self.course.key,
                              draft=True)
        draft_course_run = CourseRunFactory(
            status=CourseRunStatus.Published,
            end=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=10),
            course=draft,
            draft=True,
        )
        self.course.draft_version = draft
        self.course.save()
        extra = CourseFactory(partner=self.partner, key=self.course.key +
                              'Z')  # set key so it sorts later

        response = self.client.get(
            reverse('api:v1:course-list') + '?editable=1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['results'],
                         self.serialize_course([draft, extra], many=True))
        self.assertEqual(len(response.data['results'][0]['course_runs']), 1)
        self.assertEqual(response.data['results'][0]['course_runs'][0]['uuid'],
                         str(draft_course_run.uuid))
 def test_list_with_org_single(self):
     org1 = OrganizationFactory()
     course = CourseFactory(authoring_organizations=[org1])
     with mock.patch.object(MarketingSitePeople,
                            'update_or_publish_person'):
         person1 = PersonFactory(partner=self.partner)
         PersonFactory(partner=self.partner)
         PersonFactory(partner=self.partner)
         CourseRunFactory(staff=[person1], course=course)
         url = f'{self.people_list_url}?org={org1.key}'
         response = self.client.get(url)
         assert response.status_code == 200
         assert len(response.data['results']) == 1
         assert response.data['results'] == self.serialize_person([person1],
                                                                  many=True)
Example #20
0
    def setUp(self):
        super(CreateCoursesTests, self).setUp()

        transcript_languages = LanguageTag.objects.all()[:2]
        self.subjects = SubjectFactory.create_batch(3)
        self.test_image = make_image_file('testimage.jpg')
        self.course = CourseFactory(
            subjects=self.subjects,
            image__from_file=self.test_image
        )

        self.command_name = 'import_metadata_courses'
        self.command_args = ['--start_id={}'.format(self.course.id), '--end_id={}'.format(self.course.id)]

        # create multiple course-runs against course.
        course_runs = CourseRunFactory.create_batch(
            3, course=self.course, transcript_languages=transcript_languages,
            language=transcript_languages[0],
            short_description_override='Testing description'
        )

        canonical_course_run = course_runs[0]
        for seat_type in ['honor', 'credit', 'verified']:  # to avoid same type seat creation.
            seat_type_obj = getattr(SeatTypeFactory, seat_type)()
            SeatFactory(course_run=canonical_course_run, type=seat_type_obj)

        staff = PersonFactory.create_batch(2)
        canonical_course_run.staff.add(*staff)

        self.course.canonical_course_run = canonical_course_run
        self.course.save()

        # create org and assign to the course-metadata
        self.forganization_extension = factories.OrganizationExtensionFactory()
        self.organization = self.forganization_extension.organization
        self.course.authoring_organizations.add(self.organization)
Example #21
0
    def test_course_autocomplete(self, admin_client):
        """ Verify course autocomplete returns the data. """
        courses = CourseFactory.create_batch(3)
        path = reverse('admin_metadata:course-autocomplete')
        response = admin_client.get(path)
        data = json.loads(response.content.decode('utf-8'))
        assert response.status_code == 200
        assert len(data['results']) == 3

        # Search for substrings of course keys and titles
        course = courses[0]
        self.assert_valid_query_result(admin_client, path, course.key[12:],
                                       course)
        self.assert_valid_query_result(admin_client, path, course.title[12:],
                                       course)
Example #22
0
 def test_retrieve_with_sorting_flag(self, order_courses_by_start_date, django_assert_num_queries):
     """ Verify the number of queries is the same with sorting flag set to true. """
     course_list = CourseFactory.create_batch(3, partner=self.partner)
     for course in course_list:
         CourseRunFactory(course=course)
     program = ProgramFactory(
         courses=course_list,
         order_courses_by_start_date=order_courses_by_start_date,
         partner=self.partner)
     # property does not have the right values while being indexed
     del program._course_run_weeks_to_complete
     with django_assert_num_queries(FuzzyInt(40, 1)):  # CI is often 41
         response = self.assert_retrieve_success(program)
     assert response.data == self.serialize_program(program)
     assert course_list == list(program.courses.all())
Example #23
0
    def test_course_ordering_with_exclusions(self):
        """
        Verify that excluded course runs aren't used when ordering courses.
        """
        request = make_request()
        course_list = CourseFactory.create_batch(3)

        # Create a course run with arbitrary start and empty enrollment_start.
        # This run will be excluded from the program. If it wasn't excluded,
        # the expected course ordering, by index, would be: 0, 2, 1.
        excluded_run = CourseRunFactory(
            course=course_list[0],
            enrollment_start=None,
            start=datetime(2014, 1, 1),
        )

        # Create a run with later start and empty enrollment_start.
        CourseRunFactory(
            course=course_list[2],
            enrollment_start=None,
            start=datetime(2014, 2, 1),
        )

        # Create a run with matching start, but later enrollment_start.
        CourseRunFactory(
            course=course_list[1],
            enrollment_start=datetime(2014, 1, 2),
            start=datetime(2014, 2, 1),
        )

        # Create a run with later start and enrollment_start.
        CourseRunFactory(
            course=course_list[0],
            enrollment_start=datetime(2014, 2, 1),
            start=datetime(2014, 3, 1),
        )

        program = ProgramFactory(courses=course_list, excluded_course_runs=[excluded_run])
        serializer = self.serializer_class(program, context={'request': request})

        expected = MinimalProgramCourseSerializer(
            # The expected ordering is the reverse of course_list.
            course_list[::-1],
            many=True,
            context={'request': request, 'program': program, 'course_runs': list(program.course_runs)}
        ).data

        self.assertEqual(serializer.data['courses'], expected)
Example #24
0
    def mock_products_api(self,
                          alt_course=None,
                          alt_currency=None,
                          alt_mode=None,
                          has_stockrecord=True,
                          valid_stockrecord=True):
        """ Return a new Course Entitlement to be added by ingest """
        course = CourseFactory()

        bodies = [{
            "structure":
            "child",
            "product_class":
            "Course Entitlement",
            "title":
            "Course Intro to Everything",
            "price":
            "10.00",
            "expires":
            None,
            "attribute_values": [{
                "name": "certificate_type",
                "value": alt_mode if alt_mode else "verified",
            }, {
                "name":
                "UUID",
                "value":
                alt_course if alt_course else str(course.uuid),
            }],
            "is_available_to_buy":
            True,
            "stockrecords": []
        }]
        stockrecord = {
            "price_currency": alt_currency if alt_currency else "USD",
            "price_excl_tax": "10.00",
        }
        if valid_stockrecord:
            stockrecord.update({"partner_sku": "sku132"})
        if has_stockrecord:
            bodies[0]["stockrecords"].append(stockrecord)

        url = '{url}products/'.format(url=self.api_url)
        responses.add_callback(responses.GET,
                               url,
                               callback=mock_api_callback(url, bodies),
                               content_type=JSON)
        return bodies
    def test_download_with_unexpected_error(self):
        image_url, __ = self.mock_image_response()
        course = CourseFactory(card_image_url=image_url, image=None)

        with mock.patch('stdimage.models.StdImageFieldFile.save',
                        side_effect=Exception) as mock_save:
            with mock.patch(self.LOGGER_PATH) as mock_logger:
                call_command('download_course_images')
                mock_logger.exception.assert_called_once_with(
                    'An unknown exception occurred while downloading image for course [%s]',
                    course.key)

            mock_save.assert_called_once()

        assert len(responses.calls) == 1
        self.assert_course_has_no_image(course)
    def test_data(self):
        user = UserFactory()
        catalog = CatalogFactory(
            query='*:*',
            viewers=[user])  # We intentionally use a query for all Courses.
        courses = CourseFactory.create_batch(10)
        serializer = CatalogSerializer(catalog)

        expected = {
            'id': catalog.id,
            'name': catalog.name,
            'query': catalog.query,
            'courses_count': len(courses),
            'viewers': [user.username]
        }
        self.assertDictEqual(serializer.data, expected)
    def test_download_with_invalid_content_type(self):
        content_type = 'text/plain'
        image_url, __ = self.mock_image_response(content_type=content_type)
        course = CourseFactory(card_image_url=image_url, image=None)

        with mock.patch(self.LOGGER_PATH) as mock_logger:
            call_command('download_course_images')
            mock_logger.error.assert_called_with(
                'Image retrieved for course [%s] from [%s] has an unknown content type [%s] and will not be saved.',
                course.key,
                image_url,
                content_type
            )

        assert len(responses.calls) == 1
        self.assert_course_has_no_image(course)
Example #28
0
    def setUp(self):
        super(RefreshCourseMetadataCommandTests, self).setUp()
        self.partner = PartnerFactory()
        partner = self.partner
        self.pipeline = [
            (CoursesApiDataLoader, partner.courses_api_url, None),
            (EcommerceApiDataLoader, partner.ecommerce_api_url, 1),
            (ProgramsApiDataLoader, partner.programs_api_url, None),
            (AnalyticsAPIDataLoader, partner.analytics_url, 1),
        ]

        # Courses must exist for the refresh_course_metadata command to use multiple threads. If there are no
        # courses, the command won't risk race conditions between threads trying to create the same course.
        CourseFactory(partner=self.partner)

        self.mock_access_token()
Example #29
0
    def test_create_for_self_and_draft_course(self, is_staff, is_draft):
        """Verify can make self an editor. Test cases: as staff and non-staff, on official and draft course"""

        self.user.is_staff = is_staff
        self.user.save()
        partner = Partner.objects.first()
        course = CourseFactory(draft=is_draft, partner=partner)
        self.user.groups.add(self.org_ext.group)
        course.authoring_organizations.add(self.org_ext.organization)

        self.client.login(username=self.user.username, password=USER_PASSWORD)
        self.client.post(self.list_path, {'course': course.uuid}, format='json')
        course_editor = CourseEditor.objects.first()

        assert course_editor.course == course
        assert course_editor.user == self.user
 def setUp(self):
     """
     Test set up
     """
     super().setUp()
     self.user = UserFactory.create(is_staff=True, is_active=True)
     self.user.set_password('QWERTY')
     self.user.save()
     self.course = CourseFactory()
     self.admin_context = {
         'has_permission': True,
         'opts': self.course._meta,
     }
     self.client = Client()
     self.view_url = reverse("admin:" + COURSE_SKILLS_URL_NAME,
                             args=(self.course.pk, ))
     self.context_parameters = CourseSkillsView.ContextParameters
Example #31
0
    def test_facet_counts_caches_results(self):
        """ Verify that facet_counts cache results when it is forced to run the query."""
        course = CourseFactory()
        runs = [
            CourseRunFactory(title='foo',
                             pacing_type='self_paced',
                             hidden=True,
                             course=course),
            CourseRunFactory(title='foo',
                             pacing_type='self_paced',
                             hidden=True,
                             course=course),
            CourseRunFactory(title='foo',
                             pacing_type='instructor_paced',
                             hidden=False,
                             course=course),
        ]

        queryset = SearchQuerySet().filter(title='foo').models(CourseRun)
        queryset = queryset.facet('pacing_type').query_facet(
            'hidden', 'hidden:true')
        dc_queryset = DistinctCountsSearchQuerySet.from_queryset(
            queryset).with_distinct_counts('aggregation_key')

        # This should force the query to run and the results to be cached
        facet_counts = dc_queryset.facet_counts()

        with mock.patch.object(DistinctCountsSearchQuery, 'run') as mock_run:
            # Calling facet_counts again shouldn't result in an additional query
            cached_facet_counts = dc_queryset.facet_counts()
            assert not mock_run.called
            assert facet_counts == cached_facet_counts

            # Calling count shouldn't result in another query, as we should have already cached it with the
            # first request.
            count = dc_queryset.count()
            assert not mock_run.called
            assert count == len(runs)

            # Fetching the results shouldn't result in another query, as we should have already cached them
            # with the initial request.
            results = dc_queryset[:]
            assert not mock_run.called
            expected = {run.key for run in runs}
            actual = {run.key for run in results}
            assert expected == actual
    def test_download_with_invalid_status_code(self):
        status = 500
        body = b'Oops!'
        image_url, __ = self.mock_image_response(status=status, body=body)
        course = CourseFactory(card_image_url=image_url, image=None)

        with mock.patch(self.LOGGER_PATH) as mock_logger:
            call_command('download_course_images')
            mock_logger.error.assert_called_with(
                'Failed to download image for course [%s] from [%s]! Response was [%d]:\n%s',
                course.key,
                image_url,
                status,
                body
            )

        assert len(responses.calls) == 1
        self.assert_course_has_no_image(course)
    def test_course_ordering(self):
        """
        Verify that courses in a program are ordered by ascending run start date,
        with ties broken by earliest run enrollment start date.
        """
        request = make_request()
        course_list = CourseFactory.create_batch(3)

        # Create a course run with arbitrary start and empty enrollment_start.
        CourseRunFactory(
            course=course_list[2],
            enrollment_start=None,
            start=datetime(2014, 2, 1),
        )

        # Create a second run with matching start, but later enrollment_start.
        CourseRunFactory(
            course=course_list[1],
            enrollment_start=datetime(2014, 1, 2),
            start=datetime(2014, 2, 1),
        )

        # Create a third run with later start and enrollment_start.
        CourseRunFactory(
            course=course_list[0],
            enrollment_start=datetime(2014, 2, 1),
            start=datetime(2014, 3, 1),
        )

        program = ProgramFactory(courses=course_list)
        serializer = self.serializer_class(program,
                                           context={'request': request})

        expected = MinimalProgramCourseSerializer(
            # The expected ordering is the reverse of course_list.
            course_list[::-1],
            many=True,
            context={
                'request': request,
                'program': program,
                'course_runs': list(program.course_runs)
            }).data

        self.assertEqual(serializer.data['courses'], expected)
Example #34
0
    def create_program(self):
        organizations = [OrganizationFactory()]
        person = PersonFactory()

        course = CourseFactory()
        CourseRunFactory(course=course, staff=[person])

        program = ProgramFactory(
            courses=[course],
            authoring_organizations=organizations,
            credit_backing_organizations=organizations,
            corporate_endorsements=CorporateEndorsementFactory.create_batch(1),
            individual_endorsements=EndorsementFactory.create_batch(1),
            expected_learning_items=ExpectedLearningItemFactory.create_batch(
                1),
            job_outlook_items=JobOutlookItemFactory.create_batch(1),
            banner_image=make_image_file('test_banner.jpg'),
            video=VideoFactory())
        return program
Example #35
0
    def create_program(self):
        organizations = OrganizationFactory.create_batch(2)
        person = PersonFactory()

        courses = CourseFactory.create_batch(3)
        for course in courses:
            CourseRunFactory.create_batch(2, course=course, staff=[person], start=datetime.now())

        return ProgramFactory(
            courses=courses,
            authoring_organizations=organizations,
            credit_backing_organizations=organizations,
            corporate_endorsements=CorporateEndorsementFactory.create_batch(1),
            individual_endorsements=EndorsementFactory.create_batch(1),
            expected_learning_items=ExpectedLearningItemFactory.create_batch(1),
            job_outlook_items=JobOutlookItemFactory.create_batch(1),
            banner_image=make_image_file('test_banner.jpg'),
            video=VideoFactory(),
            order_courses_by_start_date=False,
        )
Example #36
0
    def test_course_ordering(self):
        """
        Verify that courses in a program are ordered by ascending run start date,
        with ties broken by earliest run enrollment start date.
        """
        request = make_request()
        course_list = CourseFactory.create_batch(3)

        # Create a course run with arbitrary start and empty enrollment_start.
        CourseRunFactory(
            course=course_list[2],
            enrollment_start=None,
            start=datetime(2014, 2, 1),
        )

        # Create a second run with matching start, but later enrollment_start.
        CourseRunFactory(
            course=course_list[1],
            enrollment_start=datetime(2014, 1, 2),
            start=datetime(2014, 2, 1),
        )

        # Create a third run with later start and enrollment_start.
        CourseRunFactory(
            course=course_list[0],
            enrollment_start=datetime(2014, 2, 1),
            start=datetime(2014, 3, 1),
        )

        program = ProgramFactory(courses=course_list)
        serializer = self.serializer_class(program, context={'request': request})

        expected = MinimalProgramCourseSerializer(
            # The expected ordering is the reverse of course_list.
            course_list[::-1],
            many=True,
            context={'request': request, 'program': program, 'course_runs': list(program.course_runs)}
        ).data

        self.assertEqual(serializer.data['courses'], expected)