Get students experiments, groups, and cohorts

TNL-498
This commit is contained in:
muhammad-ammar
2014-12-10 21:00:08 +05:00
parent 58fd7a92c3
commit 5ba492ecf3
11 changed files with 215 additions and 27 deletions

View File

@@ -4,7 +4,6 @@ running state of a course.
"""
import json
import urllib
from datetime import datetime
from time import time
import unicodecsv
@@ -22,8 +21,7 @@ from track.views import task_track
from util.file import course_filename_prefix_generator, UniversalNewlineIterator
from xmodule.modulestore.django import modulestore
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
from courseware.courses import get_course_by_id
from courseware.grades import iterate_grades_for
from courseware.models import StudentModule
from courseware.model_data import FieldDataCache
@@ -31,8 +29,13 @@ from courseware.module_render import get_module_for_descriptor_internal
from instructor_analytics.basic import enrolled_students_features
from instructor_analytics.csvs import format_dictlist
from instructor_task.models import ReportStore, InstructorTask, PROGRESS
from lms.djangoapps.lms_xblock.runtime import LmsPartitionService
from openedx.core.djangoapps.course_groups.cohorts import get_cohort
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
from student.models import CourseEnrollment
# define different loggers for use within tasks and on client side
TASK_LOG = get_task_logger(__name__)
@@ -547,6 +550,13 @@ def upload_grades_csv(_xmodule_instance_args, _entry_id, course_id, _task_input,
enrolled_students = CourseEnrollment.users_enrolled_in(course_id)
task_progress = TaskProgress(action_name, enrolled_students.count(), start_time)
course = get_course_by_id(course_id)
cohorts_header = ['Cohort Group Name'] if course.is_cohorted else []
partition_service = LmsPartitionService(user=None, course_id=course_id)
partitions = partition_service.course_partitions
group_configs_header = ['Group Configuration Group Name ({})'.format(partition.name) for partition in partitions]
# Loop over all our students and build our CSV lists in memory
header = None
rows = []
@@ -564,7 +574,9 @@ def upload_grades_csv(_xmodule_instance_args, _entry_id, course_id, _task_input,
if not header:
# Encode the header row in utf-8 encoding in case there are unicode characters
header = [section['label'].encode('utf-8') for section in gradeset[u'section_breakdown']]
rows.append(["id", "email", "username", "grade"] + header)
rows.append(
["id", "email", "username", "grade"] + header + cohorts_header + group_configs_header
)
percents = {
section['label']: section.get('percent', 0.0)
@@ -572,6 +584,16 @@ def upload_grades_csv(_xmodule_instance_args, _entry_id, course_id, _task_input,
if 'label' in section
}
cohorts_group_name = []
if course.is_cohorted:
group = get_cohort(student, course_id, assign=False)
cohorts_group_name.append(group.name if group else '')
group_configs_group_names = []
for partition in partitions:
group = LmsPartitionService(student, course_id).get_group(partition, assign=False)
group_configs_group_names.append(group.name if group else '')
# Not everybody has the same gradable items. If the item is not
# found in the user's gradeset, just assume it's a 0. The aggregated
# grades for their sections and overall course will be calculated
@@ -579,7 +601,10 @@ def upload_grades_csv(_xmodule_instance_args, _entry_id, course_id, _task_input,
# possible for a student to have a 0.0 show up in their row but
# still have 100% for the course.
row_percents = [percents.get(label, 0.0) for label in header]
rows.append([student.id, student.email, student.username, gradeset['percent']] + row_percents)
rows.append(
[student.id, student.email, student.username, gradeset['percent']] +
row_percents + cohorts_group_name + group_configs_group_names
)
else:
# An empty gradeset means we failed to grade a student.
task_progress.failed += 1

View File

@@ -611,15 +611,30 @@ class TestGradeReportConditionalContent(TestReportMixin, TestIntegrationTask):
representing their grades we expect to see in the CSV.
For example: [student_a: {'grade': 1.0, 'HW': 1.0}]
"""
def merge_dicts(dict_1, dict_2):
"""Return the union of dict_1 and dict_2"""
return dict(dict_1.items() + dict_2.items())
def merge_dicts(*dicts):
"""
Return the union of dicts
Arguments:
dicts: tuple of dicts
"""
return dict([item for d in dicts for item in d.items()])
def user_partition_group(user):
"""Return a dict having single key with value equals to students group in partition"""
group_config_hdr_tpl = 'Group Configuration Group Name ({})'
return {
group_config_hdr_tpl.format(self.partition.name): self.partition.scheme.get_group_for_user( # pylint: disable=E1101
self.course.id, user, self.partition, track_function=None
).name
}
self.verify_rows_in_csv(
[
merge_dicts(
{'id': str(student.id), 'username': student.username, 'email': student.email},
grades
grades,
user_partition_group(student)
)
for student_grades in students_grades for student, grades in student_grades.iteritems()
]

View File

@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
"""
Unit tests for LMS instructor-initiated background tasks helper functions.
@@ -7,8 +9,12 @@ Tests that CSV grade report generation works with unicode emails.
import ddt
from mock import Mock, patch
import tempfile
import unicodecsv
from xmodule.modulestore.tests.factories import CourseFactory
from student.tests.factories import UserFactory
from student.models import CourseEnrollment
from xmodule.partitions.partitions import Group, UserPartition
from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory
from instructor_task.models import ReportStore
@@ -57,6 +63,82 @@ class TestInstructorGradeReport(TestReportMixin, InstructorTaskCourseTestCase):
report_store = ReportStore.from_config()
self.assertTrue(any('grade_report_err' in item[0] for item in report_store.links_for(self.course.id)))
def _verify_cohort_data(self, course_id, expected_cohort_groups):
"""
Verify cohort data.
"""
cohort_groups_in_csv = []
with patch('instructor_task.tasks_helper._get_current_task'):
result = upload_grades_csv(None, None, course_id, None, 'graded')
self.assertDictContainsSubset({'attempted': 2, 'succeeded': 2, 'failed': 0}, result)
report_store = ReportStore.from_config()
report_csv_filename = report_store.links_for(course_id)[0][0]
with open(report_store.path_to(course_id, report_csv_filename)) as csv_file:
for row in unicodecsv.DictReader(csv_file):
cohort_groups_in_csv.append(row['Cohort Group Name'])
self.assertEqual(cohort_groups_in_csv, expected_cohort_groups)
def test_cohort_data_in_grading(self):
"""
Test that cohort data is included in grades csv if cohort configuration is enabled for course.
"""
cohort_groups = ['cohort 1', 'cohort 2']
course = CourseFactory.create(cohort_config={'cohorted': True, 'auto_cohort': True,
'auto_cohort_groups': cohort_groups})
for _ in range(2):
CourseEnrollment.enroll(UserFactory.create(), course.id)
# In auto cohorting a group will be assigned to a user only when user visits a problem
# In grading calculation we only add a group in csv if group is already assigned to
# user rather than creating a group automatically at runtime
expected_groups = ['', '']
self._verify_cohort_data(course.id, expected_groups)
def test_unicode_cohort_data_in_grading(self):
"""
Test that cohort groups can contain unicode characters.
"""
cohort_groups = [u'ÞrÖfessÖr X', u'MàgnëtÖ']
course = CourseFactory.create(cohort_config={'cohorted': True})
# Create users and manually assign cohort groups
user1 = UserFactory.create(username='user1')
user2 = UserFactory.create(username='user2')
CourseEnrollment.enroll(user1, course.id)
CourseEnrollment.enroll(user2, course.id)
cohort1 = CohortFactory(course_id=course.id, name=u'ÞrÖfessÖr X')
cohort2 = CohortFactory(course_id=course.id, name=u'MàgnëtÖ')
cohort1.users.add(user1)
cohort2.users.add(user2)
self._verify_cohort_data(course.id, cohort_groups)
def test_unicode_user_partitions(self):
"""
Test that user partition groups can contain unicode characters.
"""
user_groups = [u'ÞrÖfessÖr X', u'MàgnëtÖ']
user_partition = UserPartition(
0,
'x_man',
'X Man',
[
Group(0, user_groups[0]),
Group(1, user_groups[1])
]
)
# Create course with group configurations
self.initialize_course(
course_factory_kwargs={
'user_partitions': [user_partition]
}
)
_groups = [group.name for group in self.course.user_partitions[0].groups]
self.assertEqual(_groups, user_groups)
@ddt.ddt
class TestStudentReport(TestReportMixin, InstructorTaskCourseTestCase):