Commit fbac0672 by Awais Qureshi Committed by GitHub

Merge pull request #479 from edx/awais786/ECOM-6293-list-as-pc

Awais786/ecom 6293 list as pc
parents 39303c11 056b58c7
# Name of the administrative group for the Publisher app # Name of the administrative group for the Publisher app
ADMIN_GROUP_NAME = 'Publisher Admins' ADMIN_GROUP_NAME = 'Publisher Admins'
INTERNAL_USER_GROUP_NAME = 'Internal Users' INTERNAL_USER_GROUP_NAME = 'Internal Users'
PARTNER_COORDINATOR_GROUP_NAME = 'Partner Coordinators'
REVIEWER_GROUP_NAME = 'Marketing Reviewers'
PUBLISHER_GROUP_NAME = 'Publishers'
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from course_discovery.apps.publisher.constants import (
PARTNER_COORDINATOR_GROUP_NAME, PUBLISHER_GROUP_NAME, REVIEWER_GROUP_NAME
)
GROUPS = [PARTNER_COORDINATOR_GROUP_NAME, REVIEWER_GROUP_NAME, PUBLISHER_GROUP_NAME]
def create_groups(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
for group in GROUPS:
Group.objects.get_or_create(name=group)
def remove_groups(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
for group in GROUPS:
Group.objects.filter(name=group).delete()
class Migration(migrations.Migration):
dependencies = [
('publisher', '0018_create_internal_user_group'),
('auth', '0006_require_contenttypes_0002'),
]
operations = [
migrations.RunPython(create_groups, remove_groups)
]
...@@ -3,10 +3,13 @@ from django.contrib.auth.models import Group ...@@ -3,10 +3,13 @@ from django.contrib.auth.models import Group
from django.test import TestCase from django.test import TestCase
from course_discovery.apps.core.tests.factories import UserFactory from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.publisher.constants import ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME from course_discovery.apps.publisher.constants import (
ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME, PARTNER_COORDINATOR_GROUP_NAME
)
from course_discovery.apps.publisher.tests import factories from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.utils import ( from course_discovery.apps.publisher.utils import (
is_email_notification_enabled, is_publisher_admin, is_internal_user, get_internal_users is_email_notification_enabled, is_publisher_admin, is_internal_user,
get_internal_users, is_partner_coordinator_user
) )
...@@ -69,3 +72,13 @@ class PublisherUtilsTests(TestCase): ...@@ -69,3 +72,13 @@ class PublisherUtilsTests(TestCase):
self.user.groups.add(internal_user_group) self.user.groups.add(internal_user_group)
self.assertEqual(get_internal_users(), [self.user]) self.assertEqual(get_internal_users(), [self.user])
def test_is_partner_coordinator_user(self):
""" Verify the function returns a boolean indicating if the user
is a member of the partner coordinator group.
"""
self.assertFalse(is_partner_coordinator_user(self.user))
partner_coordinator_group = Group.objects.get(name=PARTNER_COORDINATOR_GROUP_NAME)
self.user.groups.add(partner_coordinator_group)
self.assertTrue(is_partner_coordinator_user(self.user))
...@@ -23,7 +23,9 @@ from course_discovery.apps.core.tests.helpers import make_image_file ...@@ -23,7 +23,9 @@ from course_discovery.apps.core.tests.helpers import make_image_file
from course_discovery.apps.course_metadata.tests import toggle_switch from course_discovery.apps.course_metadata.tests import toggle_switch
from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory
from course_discovery.apps.publisher.choices import PublisherUserRole from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.constants import INTERNAL_USER_GROUP_NAME, ADMIN_GROUP_NAME from course_discovery.apps.publisher.constants import (
INTERNAL_USER_GROUP_NAME, ADMIN_GROUP_NAME, PARTNER_COORDINATOR_GROUP_NAME, REVIEWER_GROUP_NAME
)
from course_discovery.apps.publisher.models import Course, CourseRun, Seat, State from course_discovery.apps.publisher.models import Course, CourseRun, Seat, State
from course_discovery.apps.publisher.tests import factories, JSON_CONTENT_TYPE from course_discovery.apps.publisher.tests import factories, JSON_CONTENT_TYPE
from course_discovery.apps.publisher.tests.utils import create_non_staff_user_and_login from course_discovery.apps.publisher.tests.utils import create_non_staff_user_and_login
...@@ -1027,30 +1029,49 @@ class DashboardTests(TestCase): ...@@ -1027,30 +1029,49 @@ class DashboardTests(TestCase):
def setUp(self): def setUp(self):
super(DashboardTests, self).setUp() super(DashboardTests, self).setUp()
self.user1 = UserFactory() self.publisher_admin_group = Group.objects.get(name=ADMIN_GROUP_NAME)
self.group_a = factories.GroupFactory() self.group_internal = Group.objects.get(name=INTERNAL_USER_GROUP_NAME)
self.user1.groups.add(self.group_a) self.group_partner_coordinator = Group.objects.get(name=PARTNER_COORDINATOR_GROUP_NAME)
self.group_reviewer = Group.objects.get(name=REVIEWER_GROUP_NAME)
self.user1 = UserFactory()
self.user2 = UserFactory() self.user2 = UserFactory()
self.group_b = factories.GroupFactory()
self.user2.groups.add(self.group_b) self.user1.groups.add(self.group_internal)
self.user1.groups.add(self.group_partner_coordinator)
self.user1.groups.add(self.group_reviewer)
self.user2.groups.add(self.group_internal)
self.client.login(username=self.user1.username, password=USER_PASSWORD) self.client.login(username=self.user1.username, password=USER_PASSWORD)
self.page_url = reverse('publisher:publisher_dashboard') self.page_url = reverse('publisher:publisher_dashboard')
# group-a course pc = PublisherUserRole.PartnerCoordinator
self.course_run_1 = self._create_course_assign_permissions(State.DRAFT, self.group_a)
self.course_run_2 = self._create_course_assign_permissions(State.NEEDS_REVIEW, self.group_a) # user1 courses data set ( 2 studio-request, 1 published, 1 in preview ready, 1 in progress )
self.course_run_3 = self._create_course_assign_permissions(State.PUBLISHED, self.group_a) self.course_run_1 = self._create_course_assign_role(State.DRAFT, self.user1, pc)
self.course_run_2 = self._create_course_assign_role(State.NEEDS_REVIEW, self.user1, pc)
# mark course as in progress
self.course_run_2.change_state(target=State.NEEDS_FINAL_APPROVAL)
self.course_run_2.save()
# mark course as in in preview
self.course_run_2.preview_url = 'http://'
self.course_run_2.save()
self.course_run_3 = self._create_course_assign_role(State.PUBLISHED, self.user1, pc)
self._create_course_assign_role(State.DRAFT, self.user1, PublisherUserRole.MarketingReviewer)
# group-b course # user2 courses
self._create_course_assign_permissions(State.DRAFT, self.group_b) self._create_course_assign_role(State.DRAFT, self.user2, pc)
self.table_class = "data-table-{id} display" self.table_class = "data-table-{id} display"
def _create_course_assign_permissions(self, state, group): # admin user can see all courses.
def _create_course_assign_role(self, state, user, role):
""" DRY method to create course and assign the permissions""" """ DRY method to create course and assign the permissions"""
course_run = factories.CourseRunFactory(state=factories.StateFactory(name=state)) course_run = factories.CourseRunFactory(state=factories.StateFactory(name=state))
course_run.course.assign_permission_by_group(group) factories.CourseUserRoleFactory(course=course_run.course, role=role, user=user)
return course_run return course_run
def test_page_without_login(self): def test_page_without_login(self):
...@@ -1068,53 +1089,70 @@ class DashboardTests(TestCase): ...@@ -1068,53 +1089,70 @@ class DashboardTests(TestCase):
target_status_code=302 target_status_code=302
) )
def test_page_with_different_group_user(self): def test_un_authorize_group_user_cannot_view_courses(self):
""" Verify that user from one group can access only that group courses. """ """ Verify that user from un-authorize group can access only that group courses. """
self.client.logout() self.client.logout()
self.client.login(username=self.user2.username, password=USER_PASSWORD) self.client.login(username=UserFactory(), password=USER_PASSWORD)
self.assert_dashboard_response() self.assert_dashboard_response(studio_count=0, published_count=0, progress_count=0, preview_count=0)
def test_page_with_staff_user(self): def test_with_internal_group(self):
""" Verify that staff user can see all tabs with all course runs from all groups. """ """ Verify that internal user can see courses assigned to the groups. """
self.client.logout() self.client.logout()
staff_user = UserFactory(is_staff=True) self.client.login(username=self.user1.username, password=USER_PASSWORD)
self.client.login(username=staff_user.username, password=USER_PASSWORD) self.assert_dashboard_response(studio_count=2, published_count=1, progress_count=1, preview_count=1)
self.assert_dashboard_response()
def test_with_permissions(self):
def test_different_course_runs_counts(self): """ Verify that user can view only those courses on which user group have permissions assigned. """
""" Verify that user can access published, un-published and self.client.logout()
studio requests course runs. """ user = UserFactory()
self.assert_dashboard_response() self.client.login(username=user.username, password=USER_PASSWORD)
assign_perm(Course.VIEW_PERMISSION, factories.GroupFactory(), self.course_run_1.course)
def test_studio_request_course_runs(self): self.assert_dashboard_response(studio_count=0, published_count=0, progress_count=0, preview_count=0)
""" Verify that page loads the list course runs which need studio request. """
self.course_run_1.lms_course_id = 'test' def test_with_permissions_with_data(self):
self.course_run_1.save() """ Verify that user with assigned permission on course can see all tabs
response = self.assert_dashboard_response() with all course runs from all groups.
self.assertContains(response, self.table_class.format(id='studio')) """
self.assertEqual(len(response.context['studio_request_courses']), 1) self.client.logout()
user = UserFactory()
group = factories.GroupFactory()
user.groups.add(group)
self.client.login(username=user.username, password=USER_PASSWORD)
assign_perm(Course.VIEW_PERMISSION, group, self.course_run_1.course)
assign_perm(Course.VIEW_PERMISSION, group, self.course_run_2.course)
self.assert_dashboard_response(studio_count=0, published_count=0, progress_count=1, preview_count=1)
def test_studio_request_course_runs_as_pc(self):
""" Verify that PC user can see only those courses on which he is assigned as PC role. """
self.assert_dashboard_response(studio_count=2, published_count=1, progress_count=1, preview_count=1)
def test_studio_request_course_runs_without_pc_group(self):
""" Verify that PC user can see only those courses on which he is assigned as PC role. """
self.client.logout()
self.user1.groups.remove(self.group_partner_coordinator)
self.client.login(username=self.user1.username, password=USER_PASSWORD)
self.assert_dashboard_response(studio_count=0, published_count=1, progress_count=1, preview_count=1)
def test_without_studio_request_course_runs(self): def test_without_studio_request_course_runs(self):
""" Verify that studio tab indicates a message if no course-run available. """ """ Verify that studio tab indicates a message if no course-run available. """
self.course_run_1.lms_course_id = 'test' self.course_run_1.lms_course_id = 'test-1'
self.course_run_1.save() self.course_run_1.save()
self.course_run_2.lms_course_id = 'test-2' self.course_run_2.lms_course_id = 'test-2'
self.course_run_2.save() self.course_run_2.save()
response = self.assert_dashboard_response() response = self.assert_dashboard_response(studio_count=0, published_count=1, progress_count=1, preview_count=1)
self.assertEqual(len(response.context['studio_request_courses']), 0)
self.assertContains(response, 'There are no course-runs require studio instance.') self.assertContains(response, 'There are no course-runs require studio instance.')
def test_without_published_course_runs(self): def test_without_published_course_runs(self):
""" Verify that published tab indicates a message if no course-run available. """ """ Verify that published tab indicates a message if no course-run available. """
self.course_run_3.change_state(target=State.DRAFT) self.course_run_3.change_state(target=State.DRAFT)
response = self.assert_dashboard_response() self.course_run_3.save()
self.assertEqual(len(response.context['published_course_runs']), 0) response = self.assert_dashboard_response(studio_count=3, published_count=0, progress_count=1, preview_count=1)
self.assertContains(response, "Looks like you haven't published any course yet") self.assertContains(response, "Looks like you haven't published any course yet")
def test_published_course_runs(self): def test_published_course_runs(self):
""" Verify that published tab loads course runs list. """ """ Verify that published tab loads course runs list. """
response = self.assert_dashboard_response() response = self.assert_dashboard_response(studio_count=2, published_count=1, progress_count=1, preview_count=1)
self.assertEqual(len(response.context['published_course_runs']), 1)
self.assertContains(response, self.table_class.format(id='published')) self.assertContains(response, self.table_class.format(id='published'))
self.assertContains(response, 'The list below contains all course runs published in the past 30 days') self.assertContains(response, 'The list below contains all course runs published in the past 30 days')
...@@ -1122,13 +1160,14 @@ class DashboardTests(TestCase): ...@@ -1122,13 +1160,14 @@ class DashboardTests(TestCase):
""" """
Verify that user can see all published course runs as a user in a role for a course. Verify that user can see all published course runs as a user in a role for a course.
""" """
self.client.logout()
internal_user = UserFactory() internal_user = UserFactory()
internal_user.groups.add(Group.objects.get(name=INTERNAL_USER_GROUP_NAME)) internal_user.groups.add(Group.objects.get(name=INTERNAL_USER_GROUP_NAME))
self.client.login(username=internal_user.username, password=USER_PASSWORD) self.client.login(username=internal_user.username, password=USER_PASSWORD)
# Verify that user cannot see any published course run # Verify that user cannot see any published course run
response = self.assert_dashboard_response() self.assert_dashboard_response(studio_count=0, published_count=0, progress_count=0, preview_count=0)
self.assertEqual(len(response.context['published_course_runs']), 0)
# assign user course role # assign user course role
factories.CourseUserRoleFactory( factories.CourseUserRoleFactory(
...@@ -1136,63 +1175,52 @@ class DashboardTests(TestCase): ...@@ -1136,63 +1175,52 @@ class DashboardTests(TestCase):
) )
# Verify that user can see 1 published course run # Verify that user can see 1 published course run
response = self.assert_dashboard_response() self.assert_dashboard_response(studio_count=0, published_count=1, progress_count=0, preview_count=0)
self.assertEqual(len(response.context['published_course_runs']), 1)
def test_published_course_runs_as_admin(self): def test_published_course_runs_as_admin(self):
""" """
Verify that publisher admin can see all published course runs. Verify that publisher admin can see all published course runs.
""" """
self.client.logout()
publisher_admin = UserFactory() publisher_admin = UserFactory()
publisher_admin.groups.add(Group.objects.get(name=ADMIN_GROUP_NAME)) publisher_admin.groups.add(self.publisher_admin_group)
self.client.login(username=publisher_admin.username, password=USER_PASSWORD) self.client.login(username=publisher_admin.username, password=USER_PASSWORD)
factories.CourseRunFactory(state=factories.StateFactory(name=State.PUBLISHED)) self.assert_dashboard_response(studio_count=4, published_count=1, progress_count=1, preview_count=1)
response = self.assert_dashboard_response()
self.assertEqual(len(response.context['published_course_runs']), 2)
def test_with_preview_ready_course_runs(self): def test_with_preview_ready_course_runs(self):
""" Verify that preview ready tabs loads the course runs list. """ """ Verify that preview ready tabs loads the course runs list. """
self.course_run_2.change_state(target=State.NEEDS_FINAL_APPROVAL) response = self.assert_dashboard_response(studio_count=2, preview_count=1, progress_count=1, published_count=1)
self.course_run_2.save()
response = self.assert_dashboard_response()
self.assertEqual(len(response.context['preview_course_runs']), 1)
self.assertContains(response, self.table_class.format(id='preview')) self.assertContains(response, self.table_class.format(id='preview'))
self.assertContains(response, 'The list below contains all course runs awaiting course team approval') self.assertContains(response, 'The list below contains all course runs awaiting course team approval')
def test_without_preview_ready_course_runs(self): def test_without_preview_ready_course_runs(self):
""" Verify preview ready tabs shows a message if no course run available. """ """ Verify preview ready tabs shows a message if no course run available. """
response = self.assert_dashboard_response() self.course_run_2.preview_url = None
self.assertEqual(len(response.context['preview_course_runs']), 0) self.course_run_2.save()
response = self.assert_dashboard_response(studio_count=2, preview_count=0, progress_count=1, published_count=1)
self.assertContains(response, 'There are no course runs marked for preview.') self.assertContains(response, 'There are no course runs marked for preview.')
def test_without_preview_url(self): def test_without_preview_url(self):
""" Verify preview ready tabs shows a message if no course run available. """ """ Verify preview ready tabs shows a message if no course run available. """
self.course_run_2.preview_url = None self.course_run_2.preview_url = None
self.course_run_2.save() self.course_run_2.save()
response = self.assert_dashboard_response() response = self.assert_dashboard_response(studio_count=2, preview_count=0, progress_count=1, published_count=1)
self.assertEqual(len(response.context['preview_course_runs']), 0)
self.assertContains(response, 'There are no course runs marked for preview.') self.assertContains(response, 'There are no course runs marked for preview.')
def test_without_in_progress_course_runs(self):
""" Verify in progress tabs shows a message if no course run available. """
response = self.assert_dashboard_response()
self.assertEqual(len(response.context['in_progress_course_runs']), 0)
self.assertContains(response, 'There are no in progress course runs.')
def test_with_in_progress_course_runs(self): def test_with_in_progress_course_runs(self):
""" Verify that in progress tabs loads the course runs list. """ """ Verify that in progress tabs loads the course runs list. """
self.course_run_2.change_state(target=State.NEEDS_FINAL_APPROVAL) response = self.assert_dashboard_response(studio_count=2, preview_count=1, progress_count=1, published_count=1)
self.course_run_2.save()
response = self.assert_dashboard_response()
self.assertEqual(len(response.context['in_progress_course_runs']), 1)
self.assertContains(response, self.table_class.format(id='in-progress')) self.assertContains(response, self.table_class.format(id='in-progress'))
def assert_dashboard_response(self): def assert_dashboard_response(self, studio_count=0, published_count=0, progress_count=0, preview_count=0):
""" Dry method to assert the response.""" """ Dry method to assert the response."""
response = self.client.get(self.page_url) response = self.client.get(self.page_url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.context['studio_request_courses']), studio_count)
self.assertEqual(len(response.context['published_course_runs']), published_count)
self.assertEqual(len(response.context['in_progress_course_runs']), progress_count)
self.assertEqual(len(response.context['preview_course_runs']), preview_count)
return response return response
......
""" Publisher Utils.""" """ Publisher Utils."""
from course_discovery.apps.core.models import User from course_discovery.apps.core.models import User
from course_discovery.apps.publisher.constants import ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME from course_discovery.apps.publisher.constants import (
ADMIN_GROUP_NAME, INTERNAL_USER_GROUP_NAME, PARTNER_COORDINATOR_GROUP_NAME
)
def is_email_notification_enabled(user): def is_email_notification_enabled(user):
...@@ -50,3 +52,15 @@ def get_internal_users(): ...@@ -50,3 +52,15 @@ def get_internal_users():
list list
""" """
return list(User.objects.filter(groups__name=INTERNAL_USER_GROUP_NAME)) return list(User.objects.filter(groups__name=INTERNAL_USER_GROUP_NAME))
def is_partner_coordinator_user(user):
""" Returns True if the user is an partner coordinator user.
Arguments:
user (:obj:`User`): User whose permissions should be checked.
Returns:
bool: True, if user is an PC user; otherwise, False.
"""
return user.groups.filter(name=PARTNER_COORDINATOR_GROUP_NAME).exists()
...@@ -27,7 +27,10 @@ from course_discovery.apps.publisher.models import ( ...@@ -27,7 +27,10 @@ from course_discovery.apps.publisher.models import (
Course, CourseRun, Seat, State, UserAttributes Course, CourseRun, Seat, State, UserAttributes
) )
from course_discovery.apps.publisher.serializers import UpdateCourseKeySerializer from course_discovery.apps.publisher.serializers import UpdateCourseKeySerializer
from course_discovery.apps.publisher.utils import is_internal_user, get_internal_users, is_publisher_admin from course_discovery.apps.publisher.utils import (
is_internal_user, get_internal_users, is_publisher_admin,
is_partner_coordinator_user
)
from course_discovery.apps.publisher.wrappers import CourseRunWrapper from course_discovery.apps.publisher.wrappers import CourseRunWrapper
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -56,6 +59,7 @@ class Dashboard(mixins.LoginRequiredMixin, ListView): ...@@ -56,6 +59,7 @@ class Dashboard(mixins.LoginRequiredMixin, ListView):
internal_user_courses = Course.objects.filter(course_user_roles__user=user) internal_user_courses = Course.objects.filter(course_user_roles__user=user)
course_runs = CourseRun.objects.filter(course__in=internal_user_courses).select_related('course').all() course_runs = CourseRun.objects.filter(course__in=internal_user_courses).select_related('course').all()
else: else:
# in future we will change permission from course to OrganizationExtension model
courses = get_objects_for_user(user, Course.VIEW_PERMISSION, Course) courses = get_objects_for_user(user, Course.VIEW_PERMISSION, Course)
course_runs = CourseRun.objects.filter(course__in=courses).select_related('course').all() course_runs = CourseRun.objects.filter(course__in=courses).select_related('course').all()
...@@ -72,7 +76,16 @@ class Dashboard(mixins.LoginRequiredMixin, ListView): ...@@ -72,7 +76,16 @@ class Dashboard(mixins.LoginRequiredMixin, ListView):
unpublished_course_runs = course_runs.exclude(state__name=State.PUBLISHED) unpublished_course_runs = course_runs.exclude(state__name=State.PUBLISHED)
studio_request_courses = unpublished_course_runs.filter(lms_course_id__isnull=True) # Studio requests needs to check depending upon the user role with course
# Also user should be part of partner coordinator group.
if is_publisher_admin(self.request.user):
studio_request_courses = unpublished_course_runs.filter(lms_course_id__isnull=True)
elif is_partner_coordinator_user(self.request.user):
studio_request_courses = unpublished_course_runs.filter(lms_course_id__isnull=True).filter(
course__course_user_roles__role=PublisherUserRole.PartnerCoordinator
)
else:
studio_request_courses = []
context['studio_request_courses'] = [CourseRunWrapper(course_run) for course_run in studio_request_courses] context['studio_request_courses'] = [CourseRunWrapper(course_run) for course_run in studio_request_courses]
context['unpublished_course_runs'] = [CourseRunWrapper(course_run) for course_run in unpublished_course_runs] context['unpublished_course_runs'] = [CourseRunWrapper(course_run) for course_run in unpublished_course_runs]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment