def test_publish_with_staff_removed(self, mock_access_token): """ Test that the publish button adds and removes staff from discovery_course_run """ publisher_course_run = self._create_course_run_for_publication() partner = publisher_course_run.course.organizations.first().partner self._set_test_client_domain_and_login(partner) self._mock_studio_api_success(publisher_course_run) self._mock_ecommerce_api(publisher_course_run) publish_url = reverse('publisher:api:v1:course_run-publish', kwargs={'pk': publisher_course_run.pk}) response = self.client.post(publish_url, {}) assert response.status_code == 200 discovery_course_run = CourseRun.objects.get( key=publisher_course_run.lms_course_id) assert discovery_course_run.staff.all().count() == 2 publisher_course_run.staff.clear() publisher_course_run.staff = PersonFactory.create_batch(1) response = self.client.post(publish_url, {}) assert response.status_code == 200 discovery_course_run = CourseRun.objects.get( key=publisher_course_run.lms_course_id) assert discovery_course_run.staff.all().count() == 1
def test_course_published_email(self): """ Verify that course published email functionality works fine. """ project_coordinator = UserFactory() factories.CourseUserRoleFactory( course=self.course, role=PublisherUserRole.ProjectCoordinator, user=project_coordinator) self.course_run.lms_course_id = 'course-v1:testX+test45+2017T2' self.course_run.save() person = PersonFactory() DiscoveryCourseRunFactory(key=self.course_run.lms_course_id, staff=[person]) emails.send_course_run_published_email(self.course_run, self.site) course_key = CourseKey.from_string(self.course_run.lms_course_id) subject = 'Publication complete: About page for {course_name} {run_number}'.format( course_name=self.course_run.course.title, run_number=course_key.run) assert len(mail.outbox) == 1 message = mail.outbox[0] assert message.to == [self.user.email] assert message.cc == [project_coordinator.email] self.assertEqual(str(mail.outbox[0].subject), subject) body = mail.outbox[0].body.strip() self.assertIn(self.course_run.preview_url, body) self.assertIn('has been published', body)
def test_partial_update_staff(self): """ Verify partially updating allows staff updates. """ self.mock_patch_to_studio(self.draft_course_run.key) p1 = PersonFactory() p2 = PersonFactory() PersonFactory() url = reverse('api:v1:course_run-detail', kwargs={'key': self.draft_course_run.key}) response = self.client.patch(url, {'staff': [p2.uuid, p1.uuid]}, format='json') self.assertEqual(response.status_code, 200) self.draft_course_run.refresh_from_db() self.assertListEqual(list(self.draft_course_run.staff.all()), [p2, p1])
def setUp(self): super(ChangeCourseRunStateViewTests, self).setUp() self.seat = factories.SeatFactory(type=Seat.VERIFIED, price=2) self.course_run = self.seat.course_run self.run_state = factories.CourseRunStateFactory(name=CourseRunStateChoices.Draft, course_run=self.course_run) self.course_state = factories.CourseStateFactory( name=CourseStateChoices.Approved, course=self.course_run.course ) self.user = UserFactory() self.user.groups.add(Group.objects.get(name=INTERNAL_USER_GROUP_NAME)) self.change_state_url = reverse('publisher:api:change_course_run_state', kwargs={'pk': self.run_state.id}) self.client.login(username=self.user.username, password=USER_PASSWORD) language_tag = LanguageTag(code='te-st', name='Test Language') language_tag.save() self.course_run.transcript_languages.add(language_tag) self.course_run.language = language_tag self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course' self.course_run.course.organizations.add(OrganizationFactory()) self.course_run.save() self.course_run.staff.add(PersonFactory()) toggle_switch('enable_publisher_email_notifications', True)
def test_sanity_check_success(self): """ Verify the command does not raise a CommandError error if the new index passes the sanity check. """ CourseRunFactory.create_batch(59) ProgramFactory.create_batch(59) PersonFactory.create_batch(59) record_count = 60 # Ensure that no error is raised and the sanity check passes the second time with mock.patch( 'course_discovery.apps.core.utils.ElasticsearchUtils.set_alias', return_value=True): with mock.patch( 'course_discovery.apps.edx_elasticsearch_dsl_extensions.management.commands.' 'update_index.Command.get_record_count', return_value=record_count): call_command('update_index')
def test_update_preview_url(self): """Verify the user can update course preview url.""" self.course_run.lms_course_id = 'course-v1:testX+TC167+2018T1' self.course_run.save() factories.CourseRunStateFactory.create(course_run=self.course_run, owner_role=PublisherUserRole.Publisher) factories.CourseUserRoleFactory( course=self.course_run.course, role=PublisherUserRole.Publisher ) course_team_role = factories.CourseUserRoleFactory( course=self.course_run.course, role=PublisherUserRole.CourseTeam ) person = PersonFactory() DiscoveryCourseRunFactory(key=self.course_run.lms_course_id, staff=[person]) preview_url = 'https://example.com/abc/new-course-preview' response = self._make_request(preview_url) self.assertEqual(response.status_code, 200) course_run = CourseRun.objects.get(id=self.course_run.id) self.assertTrue(course_run.preview_url.endswith('/new-course-preview')) course_key = CourseKey.from_string(course_run.lms_course_id) subject = 'Review requested: Preview for {course_name} {run_number}'.format( course_name=self.course_run.course.title, run_number=course_key.run ) self.assertEqual(len(mail.outbox), 1) self.assertEqual([course_team_role.user.email], mail.outbox[0].to) self.assertEqual(str(mail.outbox[0].subject), subject)
def setUp(self): super(CourseRunStateSerializerTests, self).setUp() self.run_state = CourseRunStateFactory( name=CourseRunStateChoices.Draft) self.course_run = self.run_state.course_run self.user = UserFactory() self.request.user = self.user CourseStateFactory(name=CourseStateChoices.Approved, course=self.course_run.course) SeatFactory(course_run=self.course_run, type=Seat.AUDIT) language_tag = LanguageTag(code='te-st', name='Test Language') language_tag.save() self.course_run.transcript_languages.add(language_tag) self.course_run.language = language_tag organization = OrganizationFactory() self.course_run.course.organizations.add(organization) self.course_run.save() self.course_run.staff.add(PersonFactory()) toggle_switch('enable_publisher_email_notifications', True) CourseUserRoleFactory(course=self.course_run.course, role=PublisherUserRole.CourseTeam, user=self.user) CourseUserRoleFactory(course=self.course_run.course, role=PublisherUserRole.ProjectCoordinator, user=UserFactory())
def test_list_filter_by_slug(self): """ Verify the endpoint allows people to be filtered by slug. """ person = PersonFactory() url = '{root}?slug={slug}'.format(root=self.people_list_url, slug=person.slug) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertListEqual(response.data['results'], self.serialize_person([person], many=True))
def test_list_filter_by_slug(self): """ Verify the endpoint allows people to be filtered by slug. """ with mock.patch.object(MarketingSitePeople, 'update_or_publish_person'): person = PersonFactory(partner=self.partner) url = '{root}?slug={slug}'.format(root=self.people_list_url, slug=person.slug) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertListEqual(response.data['results'], self.serialize_person([person], many=True))
def test_list_different_partner(self): """ Verify the endpoint only shows people for the current partner. """ with mock.patch.object(MarketingSitePeople, 'update_or_publish_person'): PersonFactory() # create person for a partner that isn't self.partner; we expect this to not show up later response = self.client.get(self.people_list_url) self.assertEqual(response.status_code, 200) # Make sure the list does not include the new person above self.assertListEqual(response.data['results'], self.serialize_person([self.person], many=True))
def setUp(self): super(AutoCompletePersonTests, self).setUp() self.user = UserFactory(is_staff=True) self.client.login(username=self.user.username, password=USER_PASSWORD) self.courses = factories.CourseFactory.create_batch(3, title='Some random course title') for course in self.courses: factories.CourseRunFactory(course=course) self.organizations = OrganizationFactory.create_batch(3) self.organization_extensions = [] for organization in self.organizations: self.organization_extensions.append(factories.OrganizationExtensionFactory(organization=organization)) self.user.groups.add(self.organization_extensions[0].group) first_instructor = PersonFactory(given_name="First Instructor") second_instructor = PersonFactory(given_name="Second Instructor") self.instructors = [first_instructor, second_instructor] for instructor in self.instructors: PositionFactory(organization=self.organizations[0], title="professor", person=instructor)
def test_has_valid_staff(self): """ Verify that property returns True if course-run must have a staff member with bio and image. """ self.assertFalse(self.course_run.has_valid_staff) staff = PersonFactory() self.course_run.staff.add(staff) self.assertTrue(self.course_run.has_valid_staff)
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
def _create_course_run_for_publication(self): organization = OrganizationFactory() transcript_languages = [LanguageTag.objects.first()] mock_image_file = make_image_file('test_image.jpg') return CourseRunFactory(course__organizations=[organization], course__tertiary_subject=None, course__image__from_file=mock_image_file, lms_course_id='a/b/c', transcript_languages=transcript_languages, staff=PersonFactory.create_batch(2))
def setUp(self): super(PersonViewSetTests, self).setUp() self.user = UserFactory(is_staff=True, is_superuser=True) self.client.force_authenticate(self.user) self.person = PersonFactory() self.organization = OrganizationFactory() # DEFAULT_PARTNER_ID is used explicitly here to avoid issues with differences in # auto-incrementing behavior across databases. Otherwise, it's not safe to assume # that the partner created here will always have id=DEFAULT_PARTNER_ID. self.partner = PartnerFactory(id=settings.DEFAULT_PARTNER_ID)
def test_list_filter_by_slug_unauthenticated(self): """ Verify the endpoint allows people to be filtered by slug, unauthenticated """ self.client.logout() with mock.patch.object(MarketingSitePeople, 'update_or_publish_person'): person = PersonFactory(partner=self.partner) url = f'{self.people_list_url}?slug={person.slug}' response = self.client.get(url) assert response.status_code == 200 self.assertCountEqual(response.data['results'], self.serialize_person([person], many=True))
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.assertEqual(response.data['results'], self.serialize_person([person1, person2], many=True))
def create_program(self): organizations = [OrganizationFactory(partner=self.partner)] person = PersonFactory() course = CourseFactory(partner=self.partner) 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), instructor_ordering=PersonFactory.create_batch(1), banner_image=make_image_file('test_banner.jpg'), video=VideoFactory(), partner=self.partner) return program
def setUp(self): super(CourseRunSerializerTests, self).setUp() self.course_run = CourseRunFactory() self.course_run.lms_course_id = 'course-v1:edX+DemoX+Demo_Course' self.person = PersonFactory() self.discovery_course_run = DiscoveryCourseRunFactory( key=self.course_run.lms_course_id, staff=[self.person]) self.request = RequestFactory() self.user = UserFactory() self.request.user = self.user self.course_state = CourseRunStateFactory( course_run=self.course_run, owner_role=PublisherUserRole.Publisher)
def test_handle_with_push_people(self): DrupalPublishUuidConfigFactory( course_run_ids='', push_people=True, ) person = PersonFactory() command = Command() with mock.patch.object(MarketingSitePeople, 'update_person') as cm: command.handle() self.assertEqual(cm.call_count, 1) self.assertEqual(cm.call_args[0][0], person)
def setUp(self): super(AutoCompletePersonTests, self).setUp() self.user = UserFactory(is_staff=True) self.client.login(username=self.user.username, password=USER_PASSWORD) self.courses = publisher_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") self.instructors = [first_instructor, second_instructor] self.organizations = OrganizationFactory.create_batch(3) self.organization_extensions = [] for instructor in self.instructors: PositionFactory(organization=self.organizations[0], title="professor", person=instructor) self.course_runs = [ publisher_factories.CourseRunFactory(course=course) for course in self.courses ] for organization in self.organizations: org_ex = publisher_factories.OrganizationExtensionFactory( organization=organization) self.organization_extensions.append(org_ex) disco_course = CourseFactory( authoring_organizations=[self.organizations[0]]) disco_course2 = CourseFactory( authoring_organizations=[self.organizations[1]]) CourseRunFactory(course=disco_course, staff=[first_instructor]) CourseRunFactory(course=disco_course2, staff=[second_instructor]) self.user.groups.add(self.organization_extensions[0].group)
def setUp(self): super(PersonViewSetTests, self).setUp() self.user = UserFactory() self.request.user = self.user self.target_permissions = Permission.objects.filter( codename__in=['add_person', 'change_person', 'delete_person']) self.permisson_class = ReadOnlyByPublisherUser() self.internal_test_group = Group.objects.create(name='internal-test') self.internal_test_group.permissions.add(*self.target_permissions) self.user.groups.add(self.internal_test_group) self.client.login(username=self.user.username, password=USER_PASSWORD) self.person = PersonFactory(partner=self.partner) self.organization = OrganizationFactory(partner=self.partner) PositionFactory(person=self.person, organization=self.organization) toggle_switch('publish_person_to_marketing_site', True) self.expected_node = { 'resource': 'node', '' 'id': '28691', 'uuid': '18d5542f-fa80-418e-b416-455cfdeb4d4e', 'uri': 'https://stage.edx.org/node/28691' }
def test_course_staff(self): """Verify that the wrapper return staff list.""" staff = PersonFactory() # another staff with position staff_2 = PersonFactory() position = PositionFactory(person=staff_2) self.course_run.staff = [staff, staff_2] self.course_run.save() expected = [{ 'full_name': staff.full_name, 'image_url': staff.get_profile_image_url, }, { 'full_name': staff_2.full_name, 'image_url': staff_2.get_profile_image_url, 'position': position.title, 'organization': position.organization_name }] self.assertEqual(self.wrapped_course_run.course_staff, expected)
def setUp(self): super(CourseRunStateTests, self).setUp() language_tag = LanguageTag(code='te-st', name='Test Language') language_tag.save() self.course_run.transcript_languages.add(language_tag) self.course_run.language = language_tag self.course_run.is_micromasters = True self.course_run.micromasters_name = 'test' self.course_run.save() self.course.course_state.name = CourseStateChoices.Approved self.course.save() self.course_run.staff.add(PersonFactory()) self.assertTrue(self.course_run_state.can_send_for_review())
def _create_decline_comment(self): # First, establish a preview_url self.course_run.lms_course_id = 'course-v1:testX+testX2.0+testCourse' self.course_run.save() person = PersonFactory() DiscoveryCourseRunFactory(key=self.course_run.lms_course_id, staff=[person]) factories.CourseRunStateFactory( course_run=self.course_run, owner_role=PublisherUserRole.CourseTeam) return self.create_comment( content_object=self.course_run, comment_type=CommentTypeChoices.Decline_Preview)
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)
def test_person_multiple_choice(self): """ Verify that PersonModelMultipleChoice returns `full_name` and `profile_image_url` as choice label. """ course_form = CustomCourseRunForm() course_form.fields['staff'].empty_label = None person = PersonFactory() course_form.fields['staff'].queryset = Person.objects.all() # we need to loop through choices because it is a ModelChoiceIterator for __, choice_label in course_form.fields['staff'].choices: expected = '<img src="{url}"/><span>{full_name}</span>'.format( full_name=person.full_name, url=person.get_profile_image_url) self.assertEqual(choice_label.strip(), expected)
def setUp(self): super(PersonViewSetTests, self).setUp() self.user = UserFactory(is_staff=True, is_superuser=True) self.user.groups.add(Group.objects.get(name=INTERNAL_USER_GROUP_NAME)) self.client.force_authenticate(self.user) self.person = PersonFactory(partner=self.partner) self.organization = OrganizationFactory(partner=self.partner) PositionFactory(person=self.person, organization=self.organization) toggle_switch('publish_person_to_marketing_site', True) self.expected_node = { 'resource': 'node', '' 'id': '28691', 'uuid': '18d5542f-fa80-418e-b416-455cfdeb4d4e', 'uri': 'https://stage.edx.org/node/28691' }
def test_published(self): person = PersonFactory() org = OrganizationFactory() primary = DiscoveryCourseRunFactory(key=self.course_run.lms_course_id, staff=[person], status=CourseRunStatus.Unpublished, announcement=None, course__partner=self.partner, end=None, enrollment_end=None) second = DiscoveryCourseRunFactory(course=primary.course, status=CourseRunStatus.Published, end=None, enrollment_end=None, start=(primary.start + datetime.timedelta(days=1))) third = DiscoveryCourseRunFactory(course=primary.course, status=CourseRunStatus.Published, end=datetime.datetime(2010, 1, 1, tzinfo=UTC), enrollment_end=None) primary.course.authoring_organizations.add(org) self.course.organizations.add(org) ensure_draft_world(DiscoveryCourse.objects.get(pk=primary.course.pk)) pc = UserFactory() factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.ProjectCoordinator, user=pc) factories.OrganizationUserRoleFactory(organization=org, role=InternalUserRole.ProjectCoordinator, user=pc) self.mock_api_client() lookup_value = getattr(primary, self.publisher.unique_field) self.mock_node_retrieval(self.publisher.node_lookup_field, lookup_value) lookup_value = getattr(third, self.publisher.unique_field) self.mock_node_retrieval(self.publisher.node_lookup_field, lookup_value) self.mock_get_redirect_form() self.mock_add_redirect() self.course_run.course_run_state.name = CourseRunStateChoices.Approved self.course_run.course_run_state.change_state(CourseRunStateChoices.Published, self.user, self.site) primary.refresh_from_db() second.refresh_from_db() third.refresh_from_db() self.assertIsNotNone(primary.announcement) self.assertEqual(primary.status, CourseRunStatus.Published) self.assertEqual(second.status, CourseRunStatus.Published) # doesn't change end=None runs self.assertEqual(third.status, CourseRunStatus.Unpublished) # does change archived runs # Check email was sent (only one - from old publisher, not new publisher flow) assert len(mail.outbox) == 1 message = mail.outbox[0] self.assertTrue(message.subject.startswith('Publication complete: ')) self.assertEqual(message.to, [self.user.email]) self.assertEqual(message.cc, [pc.email])
def setUp(self): super(PersonViewSetTests, self).setUp() self.user = UserFactory(is_staff=True, is_superuser=True) self.client.force_authenticate(self.user) self.person = PersonFactory() PositionFactory(person=self.person) self.organization = OrganizationFactory() # DEFAULT_PARTNER_ID is used explicitly here to avoid issues with differences in # auto-incrementing behavior across databases. Otherwise, it's not safe to assume # that the partner created here will always have id=DEFAULT_PARTNER_ID. self.partner = PartnerFactory(id=settings.DEFAULT_PARTNER_ID) toggle_switch('publish_person_to_marketing_site', True) self.expected_node = { 'resource': 'node', '' 'id': '28691', 'uuid': '18d5542f-fa80-418e-b416-455cfdeb4d4e', 'uri': 'https://stage.edx.org/node/28691' }