Beispiel #1
0
    def test_ingest_verified_deadline(self, mock_push_to_ecomm):
        """ Verify the method ingests data from the Courses API. """
        api_data = self.mock_api()

        self.assertEqual(Course.objects.count(), 0)
        self.assertEqual(CourseRun.objects.count(), 0)

        # Assume that while we are relying on ORGS_ON_OLD_PUBLISHER it will never be empty
        with self.settings(ORGS_ON_OLD_PUBLISHER='OTHER'):
            self.loader.ingest()

        # Verify the API was called with the correct authorization header
        self.assert_api_called(4)

        runs = CourseRun.objects.all()
        # Run with a verified entitlement, but no change in end date
        run1 = runs[0]
        run1.seats.add(
            SeatFactory(course_run=run1, type=SeatTypeFactory.verified()))
        run1.save()

        # Run with a verified entitlement, and the end date has changed
        run2 = runs[1]
        run2.seats.add(
            SeatFactory(
                course_run=run2,
                type=SeatTypeFactory.verified(),
                upgrade_deadline=datetime.datetime.now(pytz.UTC),
            ))
        original_run2_deadline = run2.seats.first().upgrade_deadline
        run2.end = datetime.datetime.now(pytz.UTC)
        run2.save()

        # Run with a credit entitlement, and the end date has changed should not
        run3 = runs[2]
        run3.seats.add(
            SeatFactory(
                course_run=run3,
                type=SeatTypeFactory.credit(),
                upgrade_deadline=None,
            ))
        run3.end = datetime.datetime.now(pytz.UTC)
        run3.save()

        # Verify the CourseRuns were created correctly
        expected_num_course_runs = len(api_data)
        self.assertEqual(CourseRun.objects.count(), expected_num_course_runs)

        # Verify multiple calls to ingest data do NOT result in data integrity errors.
        self.loader.ingest()
        calls = [
            mock.call(run2),
            mock.call(run3),
        ]
        mock_push_to_ecomm.assert_has_calls(calls)
        # Make sure the verified seat with a course run end date is changed
        self.assertNotEqual(original_run2_deadline,
                            run2.seats.first().upgrade_deadline)
        # Make sure the credit seat with a course run end date is unchanged
        self.assertIsNone(run3.seats.first().upgrade_deadline)
 def test_update_fails_with_multiple_errors(self, course_data,
                                            expected_error_message):
     course = CourseFactory(partner=self.partner, key='Org/Course/Number')
     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)
Beispiel #3
0
    def test_update_existing_program_type(self):

        fixture = json_serializer.Serializer().serialize([
            self.organization,
            self.seat_type_verified,
            self.program_type_masters,
            self.program,
        ])
        self._mock_fixture_response(fixture)
        self.reset_db_state()

        # set DB to have a conflicting program type on load
        seat_type = SeatTypeFactory(
            name='Something',
            slug='something',
        )
        existing_program_type = ProgramTypeFactory(
            name='Masters', slug='masters', applicable_seat_types=[seat_type])

        self._call_load_program_fixture([str(self.program.uuid)])

        stored_program = Program.objects.get(uuid=self.program.uuid)

        # assert existing DB value is used
        stored_program_type = stored_program.type
        self.assertEqual(stored_program_type, existing_program_type)

        # assert existing DB value is updated to match fixture
        stored_seat_types = list(
            stored_program_type.applicable_seat_types.all())
        self.assertEqual(len(stored_seat_types), 1)
        self.assertEqual(stored_seat_types[0].name,
                         self.seat_type_verified.name)
Beispiel #4
0
    def test_affiliate_with_supported_seats(self):
        """ Verify that endpoint returns course runs for verified and professional seats only. """
        response = self.client.get(self.affiliate_url)

        self.assertEqual(response.status_code, 200)
        root = ET.fromstring(response.content)
        self.assertEqual(1, len(root.findall('product')))
        self.assert_product_xml(
            root.findall('product/[pid="{}-{}"]'.format(
                self.course_run.key, self.seat_verified.type.slug))[0],
            self.seat_verified)

        # Add professional seat
        seat_professional = SeatFactory(course_run=self.course_run,
                                        type=SeatTypeFactory.professional())

        response = self.client.get(self.affiliate_url)
        root = ET.fromstring(response.content)
        self.assertEqual(2, len(root.findall('product')))

        self.assert_product_xml(
            root.findall('product/[pid="{}-{}"]'.format(
                self.course_run.key, self.seat_verified.type.slug))[0],
            self.seat_verified)
        self.assert_product_xml(
            root.findall('product/[pid="{}-{}"]'.format(
                self.course_run.key, seat_professional.type.slug))[0],
            seat_professional)
Beispiel #5
0
    def mock_courses_api(self):
        # Create existing seats to be removed by ingest
        audit_run_type = CourseRunType.objects.get(slug=CourseRunType.AUDIT)
        credit_run_type = CourseRunType.objects.get(slug=CourseRunType.CREDIT_VERIFIED_AUDIT)
        verified_run_type = CourseRunType.objects.get(slug=CourseRunType.VERIFIED_AUDIT)
        audit_run = CourseRunFactory(title_override='audit', key='audit/course/run', type=audit_run_type)
        verified_run = CourseRunFactory(title_override='verified', key='verified/course/run', type=verified_run_type)
        credit_run = CourseRunFactory(title_override='credit', key='credit/course/run', type=credit_run_type)
        no_currency_run = CourseRunFactory(title_override='no currency', key='nocurrency/course/run',
                                           type=verified_run_type)

        professional_type = SeatTypeFactory.professional()
        SeatFactory(course_run=audit_run, type=professional_type)
        SeatFactory(course_run=verified_run, type=professional_type)
        SeatFactory(course_run=credit_run, type=professional_type)
        SeatFactory(course_run=no_currency_run, type=professional_type)

        bodies = mock_data.ECOMMERCE_API_BODIES
        url = self.api_url + 'courses/'
        responses.add_callback(
            responses.GET,
            url,
            callback=mock_api_callback(url, bodies),
            content_type=JSON
        )
        return bodies
    def setUp(self):
        super().setUp()
        self.pk_generator = itertools.count(1)

        stored_site, created = Site.objects.get_or_create(  # pylint: disable=unused-variable
            domain='example.com')
        self.default_partner = Partner.objects.create(site=stored_site,
                                                      name='edX',
                                                      short_code='edx')

        SeatType.objects.all().delete()
        ProgramType.objects.all().delete()

        self.partner = PartnerFactory(name='Test')
        self.organization = OrganizationFactory(partner=self.partner)
        self.seat_type_verified = SeatTypeFactory(name='Verified',
                                                  slug='verified')
        self.program_type_masters = ProgramTypeFactory(
            name='Masters',
            name_t='Masters',
            slug='masters',
            applicable_seat_types=[self.seat_type_verified])
        self.program_type_masters_translation = self.program_type_masters.translations.all(
        )[0]

        self.program_type_mm = ProgramTypeFactory(
            name='MicroMasters',
            name_t='MicroMasters',
            slug='micromasters',
            applicable_seat_types=[self.seat_type_verified])
        self.program_type_mm_translation = self.program_type_mm.translations.all(
        )[0]

        self.course = CourseFactory(
            partner=self.partner, authoring_organizations=[self.organization])
        self.course_run = CourseRunFactory(course=self.course)
        self.program = ProgramFactory(
            type=self.program_type_masters,
            partner=self.partner,
            authoring_organizations=[self.organization])
        self.course_mm = CourseFactory(
            partner=self.partner, authoring_organizations=[self.organization])
        self.course_run_mm = CourseRunFactory(course=self.course)
        self.program_mm = ProgramFactory(
            type=self.program_type_mm,
            partner=self.partner,
            authoring_organizations=[self.organization],
            courses=[self.course_mm])
        self.curriculum = CurriculumFactory(program=self.program)
        self.curriculum_course_membership = CurriculumCourseMembershipFactory(
            course=self.course, curriculum=self.curriculum)
        self.curriculum_program_membership = CurriculumProgramMembershipFactory(
            program=self.program_mm, curriculum=self.curriculum)

        self.program_2 = ProgramFactory(
            type=self.program_type_masters,
            partner=self.partner,
            authoring_organizations=[self.organization])

        self._mock_oauth_request()
Beispiel #7
0
 def setUp(self):
     super().setUp()
     self.user = UserFactory()
     self.staff = UserFactory(username='******', is_staff=True)
     seat_type = SeatTypeFactory(name="TestSeatType")
     self.program_type = ProgramTypeFactory(
         name="TestProgramType",
         slug="test-program-type",
         applicable_seat_types=[seat_type],
     )
Beispiel #8
0
    def create_course_with_basic_active_course_run(self):
        course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)

        course_run = CourseRunFactory(course=course,
                                      start=self.YESTERDAY,
                                      end=self.YESTERDAY,
                                      status=CourseRunStatus.Published)
        SeatFactory(
            course_run=course_run,
            type=SeatTypeFactory.audit(),
        )
        return course
 def create_current_upgradeable_course(self, **kwargs):
     course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)
     current_upgradeable_course_run = CourseRunFactory(
         course=course,
         start=self.YESTERDAY,
         end=self.IN_FIFTEEN_DAYS,
         enrollment_end=self.IN_FIFTEEN_DAYS,
         status=CourseRunStatus.Published,
         **kwargs)
     SeatFactory(course_run=current_upgradeable_course_run,
                 type=SeatTypeFactory.verified(),
                 upgrade_deadline=self.TOMORROW,
                 price=10)
     return course
Beispiel #10
0
 def _set_up_masters_program_type(self):
     """
     Set DB to have a conflicting program type on load.
     """
     seat_type = SeatTypeFactory(
         name='Something',
         slug='something',
     )
     existing_program_type = ProgramTypeFactory(
         name='Masters',
         name_t='Masters',
         slug='masters',
         applicable_seat_types=[seat_type])
     return existing_program_type
Beispiel #11
0
    def create_upgradeable_course_starting_soon(self):
        course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)
        upgradeable_course_run = CourseRunFactory(
            course=course,
            start=self.TOMORROW,
            end=self.IN_FIFTEEN_DAYS,
            enrollment_end=self.IN_FIFTEEN_DAYS,
            status=CourseRunStatus.Published)

        SeatFactory(course_run=upgradeable_course_run,
                    type=SeatTypeFactory.verified(),
                    upgrade_deadline=self.IN_FIFTEEN_DAYS,
                    price=10)
        return course
Beispiel #12
0
    def setUp(self):
        super().setUp()
        self.user = UserFactory()
        self.client.force_authenticate(self.user)
        self.catalog = CatalogFactory(query='*:*', viewers=[self.user])

        self.enrollment_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30)
        self.course_end = datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=60)
        self.course_run = CourseRunFactory(enrollment_end=self.enrollment_end, end=self.course_end)

        self.seat_verified = SeatFactory(course_run=self.course_run, type=SeatTypeFactory.verified())
        self.course = self.course_run.course
        self.affiliate_url = reverse('api:v1:partners:affiliate_window-detail', kwargs={'pk': self.catalog.id})
        self.refresh_index()
Beispiel #13
0
    def create_current_non_upgradeable_course(self):
        course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)

        non_upgradeable_course_run = CourseRunFactory(
            course=course,
            start=self.YESTERDAY,
            end=self.IN_FIFTEEN_DAYS,
            enrollment_end=self.IN_FIFTEEN_DAYS,
            status=CourseRunStatus.Published)
        # not upgradeable because upgrade_deadline has passed
        SeatFactory(course_run=non_upgradeable_course_run,
                    type=SeatTypeFactory.verified(),
                    upgrade_deadline=self.YESTERDAY,
                    price=10)
        return course
 def test_index_if_non_active_course_run_is_hidden(self):
     course = self.create_course_with_basic_active_course_run()
     course.authoring_organizations.add(OrganizationFactory())
     non_upgradeable_course_run = CourseRunFactory(
         course=course,
         start=self.YESTERDAY,
         end=self.IN_FIFTEEN_DAYS,
         enrollment_end=self.IN_FIFTEEN_DAYS,
         status=CourseRunStatus.Published,
         hidden=True)
     # not upgradeable because upgrade_deadline has passed
     SeatFactory(course_run=non_upgradeable_course_run,
                 type=SeatTypeFactory.verified(),
                 upgrade_deadline=self.YESTERDAY,
                 price=10)
     assert course.should_index
Beispiel #15
0
 def create_upcoming_non_upgradeable_course(self, additional_days=0):
     course = AlgoliaProxyCourseFactory(partner=self.__class__.edxPartner)
     future_course_run = CourseRunFactory(
         course=course,
         start=self.IN_THREE_DAYS + datetime.timedelta(days=additional_days),
         end=self.IN_FIFTEEN_DAYS + datetime.timedelta(days=additional_days),
         enrollment_end=self.IN_THREE_DAYS + datetime.timedelta(days=additional_days),
         status=CourseRunStatus.Published
     )
     SeatFactory(
         course_run=future_course_run,
         type=SeatTypeFactory.verified(),
         upgrade_deadline=self.YESTERDAY,
         price=10
     )
     return course
    def test_csv(self):
        SeatFactory(type=SeatTypeFactory.audit(), course_run=self.course_run)
        SeatFactory(type=SeatTypeFactory.verified(),
                    course_run=self.course_run)
        SeatFactory(type=SeatTypeFactory.credit(),
                    course_run=self.course_run,
                    credit_provider='ASU',
                    credit_hours=9)
        SeatFactory(type=SeatTypeFactory.credit(),
                    course_run=self.course_run,
                    credit_provider='Hogwarts',
                    credit_hours=4)

        url = reverse('api:v1:catalog-csv', kwargs={'id': self.catalog.id})

        with self.assertNumQueries(23):
            response = self.client.get(url)

        course_run = self.serialize_catalog_flat_course_run(self.course_run)
        expected = [
            course_run['announcement'],
            course_run['content_language'],
            course_run['course_key'],
            course_run['end'],
            course_run['enrollment_end'],
            course_run['enrollment_start'],
            course_run['expected_learning_items'],
            course_run['full_description'],
            '',  # image description
            '',  # image height
            course_run['image']['src'],
            '',  # image width
            course_run['key'],
            str(course_run['level_type']),
            course_run['marketing_url'],
            str(course_run['max_effort']),
            str(course_run['min_effort']),
            course_run['modified'],
            course_run['owners'],
            course_run['pacing_type'],
            course_run['prerequisites'],
            course_run['seats']['audit']['type'],
            '{}'.format(course_run['seats']['credit']['credit_hours']),
            '{}'.format(course_run['seats']['credit']['credit_provider']),
            '{}'.format(course_run['seats']['credit']['currency']),
            '{}'.format(str(course_run['seats']['credit']['price'])),
            '{}'.format(course_run['seats']['credit']['type']),
            '{}'.format(course_run['seats']['credit']['upgrade_deadline']),
            course_run['seats']['honor']['type'],
            course_run['seats']['masters']['type'],
            course_run['seats']['professional']['currency'],
            str(course_run['seats']['professional']['price']),
            course_run['seats']['professional']['type'],
            course_run['seats']['professional']['upgrade_deadline'],
            course_run['seats']['verified']['currency'],
            str(course_run['seats']['verified']['price']),
            course_run['seats']['verified']['type'],
            course_run['seats']['verified']['upgrade_deadline'],
            course_run['short_description'],
            course_run['sponsors'],
            course_run['start'],
            course_run['subjects'],
            course_run['title'],
            course_run['video']['description'],
            course_run['video']['image']['description'],
            str(course_run['video']['image']['height']),
            course_run['video']['image']['src'],
            str(course_run['video']['image']['width']),
            course_run['video']['src'],
        ]

        # collect streamed content
        received_content = b''
        for item in response.streaming_content:
            received_content += item

        # convert received content to csv for comparison
        f = StringIO(received_content.decode('utf-8'))
        reader = csv.reader(f)
        content = list(reader)

        assert response.status_code == 200
        assert expected == content[1]