Commit 7cab70b6 by Zia Fazal Committed by Jonathan Piacenti

ziafazal/api-fix-bug-progress-tab: fix progress

Fixed progress value by using total modules in the course as
denominator.

variable names changes and login for total_actual_completions

efficient way to load leaf modules

 and added required settings to tests
parent 87b43939
......@@ -48,6 +48,10 @@ def _fake_get_get_course_social_stats(course_id):
@mock.patch("lms.lib.comment_client.user.get_course_social_stats", _fake_get_get_course_social_stats)
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
@override_settings(EDX_API_KEY=TEST_API_KEY)
@mock.patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': False,
'ADVANCED_SECURITY': False,
'PREVENT_CONCURRENT_LOGINS': False
})
class CoursesApiTests(TestCase):
""" Test suite for Courses API views """
......@@ -133,12 +137,12 @@ class CoursesApiTests(TestCase):
)
self.sub_section = ItemFactory.create(
parent_location=self.course_content.location,
parent_location=self.chapter.location,
category="sequential",
display_name=u"test subsection",
)
unit = ItemFactory.create(
self.unit = ItemFactory.create(
parent_location=self.sub_section.location,
category="vertical",
metadata={'graded': True, 'format': 'Homework'},
......@@ -171,7 +175,7 @@ class CoursesApiTests(TestCase):
module_type = 'group-project'
self.item = ItemFactory.create(
parent_location=unit.location,
parent_location=self.unit.location,
category=category,
data=StringResponseXMLFactory().build_xml(answer='foo'),
metadata={'rerandomize': 'always'},
......@@ -325,7 +329,7 @@ class CoursesApiTests(TestCase):
chapter = response.data['content'][0]
self.assertEqual(chapter['category'], 'chapter')
self.assertEqual(chapter['name'], 'Overview')
self.assertEqual(len(chapter['children']), 1)
self.assertEqual(len(chapter['children']), 2)
sequence = chapter['children'][0]
self.assertEqual(sequence['category'], 'videosequence')
......@@ -1597,7 +1601,6 @@ class CoursesApiTests(TestCase):
self.assertEqual(response.status_code, 400)
def test_courses_completions_leaders_list_get(self):
completion_uri = '{}/{}/completions/'.format(self.base_courses_uri, unicode(self.course.id))
users = []
for i in xrange(1, 5):
......@@ -1619,7 +1622,7 @@ class CoursesApiTests(TestCase):
local_content_name = 'Video_Sequence{}'.format(i)
local_content = ItemFactory.create(
category="videosequence",
parent_location=self.chapter.location,
parent_location=self.unit.location,
data=self.test_data,
display_name=local_content_name
)
......@@ -1647,7 +1650,7 @@ class CoursesApiTests(TestCase):
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['leaders']), 4)
self.assertEqual(response.data['course_avg'], 6.3)
self.assertEqual(response.data['course_avg'], 24)
# without count filter and user_id
test_uri = '{}/{}/metrics/completions/leaders/?user_id={}'.format(self.base_courses_uri, self.test_course_id,
......@@ -1656,7 +1659,7 @@ class CoursesApiTests(TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['leaders']), 3)
self.assertEqual(response.data['position'], 1)
self.assertEqual(response.data['completions'], 40)
self.assertEqual(response.data['completions'], 38)
# test with bogus course
test_uri = '{}/{}/metrics/completions/leaders/'.format(self.base_courses_uri, self.test_bogus_course_id)
......
......@@ -27,7 +27,7 @@ from student.roles import CourseRole, CourseAccessRole, CourseInstructorRole, Co
from xmodule.modulestore.django import modulestore
from api_manager.courseware_access import get_course, get_course_child
from api_manager.courseware_access import get_course, get_course_child, get_course_leaf_nodes
from api_manager.models import CourseGroupRelationship, CourseContentGroupRelationship, GroupProfile, \
CourseModuleCompletion
from api_manager.permissions import SecureAPIView, SecureListAPIView
......@@ -1604,31 +1604,33 @@ class CoursesCompletionsLeadersList(SecureAPIView):
if not course_descriptor:
return Response({}, status=status.HTTP_404_NOT_FOUND)
total_possible_completions = len(get_course_leaf_nodes(course_key,
['discussion-course', 'group-project']))
exclude_users = _get_aggregate_exclusion_user_ids(course_key)
queryset = CourseModuleCompletion.objects.filter(course_id=course_key)\
.exclude(user__in=exclude_users)
total_completions = queryset.filter(user__is_active=True).count()
total_actual_completions = queryset.filter(user__is_active=True).count()
if user_id:
user_completions = queryset.filter(user__id=user_id).count()
completions_above_user = queryset.filter(user__is_active=True).values('user__id')\
.annotate(completions=Count('content_id')).filter(completions__gt=user_completions).count()
data['position'] = completions_above_user + 1
completion_percentage = 0
if total_completions > 0:
completion_percentage = int(round(100 * user_completions/total_completions))
if total_possible_completions > 0:
completion_percentage = int(round(100 * user_completions/total_possible_completions))
data['completions'] = completion_percentage
total_users = CourseEnrollment.users_enrolled_in(course_key).exclude(id__in=exclude_users).count()
if total_users:
course_avg = round(total_completions / float(total_users), 1)
if total_users and total_actual_completions:
course_avg = round(total_actual_completions / float(total_users), 1)
course_avg = int(round(100 * course_avg / total_possible_completions)) # avg in percentage
data['course_avg'] = course_avg
queryset = queryset.filter(user__is_active=True).values('user__id', 'user__username', 'user__profile__title',
'user__profile__avatar_url')\
.annotate(completions=Count('content_id')).order_by('-completions')[:count]
serializer = CourseCompletionsLeadersSerializer(queryset, many=True,
context={'total_completions': total_completions})
context={'total_completions': total_possible_completions})
data['leaders'] = serializer.data # pylint: disable=E1101
return Response(data, status=status.HTTP_200_OK)
......
......@@ -80,3 +80,15 @@ def get_course_total_score(course_summary):
if section['section_total']:
score += section['section_total'][1]
return score
def get_course_leaf_nodes(course_key, detached_categories):
"""
Get count of the leaf nodes with ability to exclude some categories
"""
nodes = []
verticals = modulestore().get_items(course_key, category='vertical')
for vertical in verticals:
nodes.extend([unit.location for unit in vertical.get_children()
if getattr(unit, 'category') not in detached_categories])
return nodes
......@@ -8,6 +8,7 @@ from datetime import datetime
from random import randint
import uuid
import json
import mock
from urllib import urlencode
from django.core.cache import cache
......@@ -31,6 +32,10 @@ class SecureClient(Client):
@override_settings(EDX_API_KEY=TEST_API_KEY)
@mock.patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': False,
'ADVANCED_SECURITY': False,
'PREVENT_CONCURRENT_LOGINS': False
})
class GroupsApiTests(ModuleStoreTestCase):
""" Test suite for Groups API views """
......
......@@ -6,6 +6,7 @@ Run these tests @ Devstack:
"""
import json
import uuid
import mock
from django.contrib.auth.models import User
from django.core.cache import cache
......@@ -29,8 +30,11 @@ class SecureClient(Client):
@override_settings(EDX_API_KEY=TEST_API_KEY)
@mock.patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': False,
'ADVANCED_SECURITY': False,
'PREVENT_CONCURRENT_LOGINS': False
})
class OrganizationsApiTests(ModuleStoreTestCase):
""" Test suite for Users API views """
def setUp(self):
......
......@@ -21,7 +21,8 @@ TEST_API_KEY = str(uuid.uuid4())
@override_settings(EDX_API_KEY=TEST_API_KEY)
@patch.dict("django.conf.settings.FEATURES", {'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': False})
@patch.dict("django.conf.settings.FEATURES", {'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': False,
'PREVENT_CONCURRENT_LOGINS': False})
class SessionApiRateLimitingProtectionTest(TestCase):
"""
Test api_manager.session.login.ratelimit
......
......@@ -20,8 +20,9 @@ TEST_API_KEY = str(uuid.uuid4())
@override_settings(EDX_API_KEY=TEST_API_KEY)
@patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': True})
@patch.dict("django.conf.settings.FEATURES", {'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': True})
@patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': True,
'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': True,
'PREVENT_CONCURRENT_LOGINS': False})
class SessionApiSecurityTest(TestCase):
"""
Test api_manager.session.session_list view
......
......@@ -7,6 +7,7 @@ Run these tests @ Devstack:
"""
from random import randint
import uuid
import mock
from django.contrib.auth.models import User
from django.core.cache import cache
......@@ -25,6 +26,10 @@ class SecureClient(Client):
@override_settings(EDX_API_KEY=TEST_API_KEY)
@mock.patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': False,
'ADVANCED_SECURITY': False,
'PREVENT_CONCURRENT_LOGINS': False
})
class SessionsApiTests(TestCase):
""" Test suite for Sessions API views """
......
......@@ -4,6 +4,7 @@ Run these tests @ Devstack:
"""
from random import randint
import uuid
import mock
from django.test import TestCase
from django.test.utils import override_settings
......@@ -12,6 +13,10 @@ TEST_API_KEY = "123456ABCDEF"
@override_settings(API_ALLOWED_IP_ADDRESSES=['127.0.0.1', '10.0.2.2', '192.168.0.0/24'])
@mock.patch.dict("django.conf.settings.FEATURES", {'ENFORCE_PASSWORD_POLICY': False,
'ADVANCED_SECURITY': False,
'PREVENT_CONCURRENT_LOGINS': False
})
class PermissionsTests(TestCase):
""" Test suite for Permissions helper classes """
def setUp(self):
......
......@@ -30,7 +30,6 @@ from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
TEST_API_KEY = str(uuid.uuid4())
class SecureClient(Client):
""" Django test client using a "secure" connection. """
......@@ -52,7 +51,7 @@ class UsersApiTests(ModuleStoreTestCase):
def setUp(self):
self.test_server_prefix = 'https://testserver'
self.test_username = str(uuid.uuid4())
self.test_password = str(uuid.uuid4())
self.test_password = 'Test.Me64!'
self.test_email = str(uuid.uuid4()) + '@test.org'
self.test_first_name = str(uuid.uuid4())
self.test_last_name = str(uuid.uuid4())
......@@ -79,11 +78,6 @@ class UsersApiTests(ModuleStoreTestCase):
due=datetime(2016, 5, 16, 14, 30),
display_name="View_Sequence"
)
self.test_project = Project.objects.create(
course_id=unicode(self.course.id),
content_id=unicode(self.course_content.scope_ids.usage_id)
)
self.course2 = CourseFactory.create(display_name="TEST COURSE2", org='TESTORG2')
self.course2_content = ItemFactory.create(
category="videosequence",
......@@ -92,10 +86,6 @@ class UsersApiTests(ModuleStoreTestCase):
due=datetime(2016, 5, 16, 14, 30),
display_name="View_Sequence2"
)
self.second_test_project = Project.objects.create(
course_id=unicode(self.course2.id),
content_id=unicode(self.course2_content.scope_ids.usage_id)
)
self.user = UserFactory()
self.client = SecureClient()
......@@ -163,7 +153,7 @@ class UsersApiTests(ModuleStoreTestCase):
data = {
'email': 'test{}@example.com'.format(i),
'username': 'test_user{}'.format(i),
'password': 'test_pass',
'password': self.test_password,
'first_name': 'John{}'.format(i),
'last_name': 'Doe{}'.format(i)
}
......@@ -228,7 +218,7 @@ class UsersApiTests(ModuleStoreTestCase):
data = {
'email': 'test{}@example.com'.format(i),
'username': 'test_user{}'.format(i),
'password': 'test_pass',
'password': self.test_password,
'first_name': 'John{}'.format(i),
'last_name': 'Doe{}'.format(i)
}
......@@ -1265,12 +1255,24 @@ class UsersApiTests(ModuleStoreTestCase):
# create anonymous user
anonymous_id = anonymous_id_for_user(self.user, self.course.id)
for i in xrange(1, 12):
project_id = self.test_project.id
if i > 7: # set to other project
project_id = self.second_test_project.id
course = CourseFactory.create(
display_name="TEST COURSE {}".format(i),
)
course_content = ItemFactory.create(
category="videosequence",
parent_location=course.location,
data=self.test_course_data,
display_name="View_Sequence"
)
test_project = Project.objects.create(
course_id=unicode(course.id),
content_id=unicode(course_content.scope_ids.usage_id)
)
data = {
'name': 'Workgroup ' + str(i),
'project': project_id
'project': test_project.id
}
response = self.do_post(test_workgroups_uri, data)
self.assertEqual(response.status_code, 201)
......@@ -1288,11 +1290,11 @@ class UsersApiTests(ModuleStoreTestCase):
self.assertEqual(response.data['num_pages'], 2)
# test with course_id filter and integer user id
course_id = {'course_id': unicode(self.course.id)}
course_id = {'course_id': unicode(course.id)}
response = self.do_get('{}/{}/workgroups/?{}'.format(self.users_base_uri, user_id, urlencode(course_id)))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['count'], 7)
self.assertEqual(len(response.data['results']), 7)
self.assertEqual(response.data['count'], 1)
self.assertEqual(len(response.data['results']), 1)
self.assertIsNotNone(response.data['results'][0]['name'])
self.assertIsNotNone(response.data['results'][0]['project'])
......
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