Commit 661f37ec by Calen Pennington

Merge remote-tracking branch 'edx/master' into opaque-keys-merge-master

Conflicts:
	lms/djangoapps/courseware/tests/test_views.py
	lms/djangoapps/instructor/tests/test_api.py
	lms/djangoapps/instructor/tests/test_legacy_anon_csv.py
	lms/djangoapps/instructor/views/api.py
	lms/djangoapps/instructor/views/legacy.py
parents 44b15619 0a6ea26c
@shard_3
@shard_1
Feature: CMS Video Component Editor
As a course author, I want to be able to create video components
......
......@@ -142,3 +142,8 @@ To add a course team member:
#. Click **Add a New Team Member**.
#. Enter the new team member's email address, then click **ADD USER**.
You can also assign privileged roles to users when you work in the LMS.
Regardless of where the role is assigned, these administrative team members can
work on your course in both the LMS and in Studio. For more information on
assigning roles while you run your course, see
:ref:`Course_Staffing`.
\ No newline at end of file
......@@ -12,12 +12,22 @@ May, 2014
* - Date
- Change
- 05/13/14
* - 05/14/14
- Updated the :ref:`Running Your Course Index` chapter to remove references
to the "new beta" Instructor Dashboard.
* -
- Updated the :ref:`Enrollment` section to reflect that usernames or email
addresses can be used to batch enroll students.
* -
- Updated the :ref:`Grades` section to reflect new features on the problem
**Staff Debug** viewer for rescoring, resetting attempts, and deleting
student state.
* -
- Updated the :ref:`Course_Staffing` section to state the labeling
differences between Studio and the LMS with respect to course team roles.
* - 05/09/14
- Updated :ref:`Assigning_discussion_roles` with a note about course staff requiring
explicit granting of discussion administration roles.
- Updated :ref:`Assigning_discussion_roles` with a note about course staff
requiring explicit granting of discussion administration roles.
* -
- Added :ref:`VitalSource` topic.
* - 05/08/14
......@@ -26,7 +36,8 @@ May, 2014
- Updated the :ref:`Discussions` chapter to include a topic on closing
discussions.
* - 05/06/14
- Expanded the :ref:`Grades` chapter to include a topic on interpreting the score histograms for problems.
- Expanded the :ref:`Grades` chapter to include a topic on interpreting the
score histograms for problems.
* -
- Updated :ref:`LTI Component` to reflect changes to the Studio UI.
* - 05/02/14
......
......@@ -21,9 +21,7 @@ To view course data:
#. View the live version of your course.
#. Click **Instructor** then **Try New Beta Dashboard**.
#. Click **Course Info** if necessary.
#. Click **Instructor**, then click **Course Info** if necessary.
The **Basic Course Information** section of the page that opens lists
information about the course.
......
......@@ -86,9 +86,7 @@ To enroll students or staff members:
#. View the live version of your course.
#. Click **Instructor** then **Try New Beta Dashboard**.
#. Click **Membership**.
#. Click **Instructor**, then click **Membership**.
#. In the **Batch Enrollment** section of the page, enter the username or email
address of the student, or enter multiple names or addresses separated by
......@@ -98,7 +96,7 @@ To enroll students or staff members:
note that this feature is better suited to courses with smaller enrollments,
rather than courses with massive enrollments.
5. To streamline the course enrollment process, leave **Auto Enroll** selected.
4. To streamline the course enrollment process, leave **Auto Enroll** selected.
#. To send students an email message, leave **Notify students by email**
selected.
......@@ -129,9 +127,7 @@ To view the enrollment count for a course:
#. View the live version of your course.
#. Click **Instructor** then **Try New Beta Dashboard**.
#. Click **Course Info** if necessary.
#. Click **Instructor**, then click **Course Info** if necessary.
The **Enrollment Information** section of the page that opens shows the total
number of people who are currently enrolled.
......
......@@ -12,14 +12,13 @@ for working with students, grades, and other members of the staff.
* Instructors
.. **Question**: how does this team, set up on the Instructor Dashboard, differ from the "Course Team Members" that you add in Studio (Settings > Course Team)?
The administrative team that helps you run your course in the LMS can include
some, all, or none of the people who help you set up the course in Studio. You
assign these administrative roles in the LMS, separately from the roles you
assign in Studio. For more information on setting up a team in Studio, see
You can assign these privileged roles when you work in either the LMS or in
Studio, and the users who have these roles can work on your course in both the
LMS and Studio. For more information on setting up a team in Studio, see
:ref:`Add Course Team Members`.
.. note:: The LMS "Course Staff" role is the same as the Studio "Staff" role, and the LMS "Instructors" role is the same as the Studio "Admin" role.
You can also designate teams of people to beta test your course and to
moderate and manage its discussions by assigning other LMS roles. The beta
testers and discussion administrators must be enrolled in your course, but
......@@ -76,9 +75,7 @@ To assign a staff role:
#. View the live version of your course.
#. Click **Instructor** then **Try New Beta Dashboard**.
#. Click **Membership**.
#. Click **Instructor**, then click **Membership**.
#. In the **Administration List Management** section, use the drop-down list to
select **Course Staff** or **Instructors**.
......
......@@ -81,9 +81,7 @@ To download or view student data:
#. View the live version of your course.
#. Click **Instructor** then **Try New Beta Dashboard**.
#. Click **Data Download**.
#. Click **Instructor**, then click **Data Download**.
#. To download data about enrolled students in a CSV file, click **Download profile information as a CSV**.
......@@ -108,9 +106,7 @@ To display demographic data for your students:
#. View the live version of your course.
#. Click **Instructor** then **Try New Beta Dashboard**.
#. Click **Analytics**.
#. Click **Instructor**, then click **Analytics**.
* The Year of Birth section displays a chart of enrolled students plotted by year of birth.
......@@ -139,9 +135,8 @@ To download a file of assigned user IDs and anonymized user IDs:
#. View the live version of your course.
#. Click **Instructor** > **Try New Beta Dashboard**.
#. Click **Data Download** > **Get Student Anonymized IDs CSV**.
#. Click **Instructor**, then click **Data Download**.
#. Click **Get Student Anonymized IDs CSV**.
You are prompted to open or save the (course-id)-anon-id.csv file for your course. This file contains the user ID that is assigned to each student at registration and its corresponding anonymized ID. Values are included for every student who ever enrolled for your course.
......
......@@ -97,15 +97,11 @@ Before you can assign roles to your discussion administrators, you need their em
* To get this information for a staff member, on the Instructor Dashboard click **Membership** and then select **Course Staff** from the drop-down list.
* To get this information for an enrolled student, on the Instructor Dashboard click **Data Download** > **Download profile information as a CSV**.
**Tip**: These instructions are for the new Instructor Dashboard: click **Try New Beta Dashboard**.
To assign a role:
#. View the live version of your course.
#. Click **Instructor** then **Try New Beta Dashboard**.
#. Click **Membership**.
#. Click **Instructor**, then click **Membership**.
#. In the Administration List Management section, use the drop-down list to select Discussion Admins, Discussion Moderators, or Discussion Community TAs.
......
......@@ -11,7 +11,7 @@ Change Log
* - Date
- Change
* - 05/0614
* - 05/06/14
- Added enrollment event types to the :ref:`Tracking Logs` chapter.
* - 05/05/14
- Removed information on the Poll module. `Polls <http://edx.readthedocs.org/projects/ca/en/latest/exercises_tools/poll.html>`_ are now covered in the *Building and Running an edX Course* guide.
......@@ -31,7 +31,7 @@ Change Log
* -
- Reformatted the :ref:`Tracking Logs` chapter to improve readability.
* - 03/28/14
- Added the :ref:'Data_Czar' chapter.
- Added the :ref:`Data_Czar` chapter.
* - 03/24/14
- Added the ``user_api_usercoursetag`` table to the :ref:`Student_Info` chapter and the ``assigned_user_to_partition`` and ``child_render`` event types to the :ref:`Tracking Logs` chapter.
* - 03/19/14
......
# coding=UTF-8
"""
Tests courseware views.py
"""
......@@ -46,7 +47,7 @@ class TestJumpTo(TestCase):
self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
def test_jumpto_invalid_location(self):
location = self.course_key.make_usage_key(None, 'NoSuchPlace')
location = self.course_key.make_usage_key(None, 'NoSuchPlace')
# This is fragile, but unfortunately the problem is that within the LMS we
# can't use the reverse calls from the CMS
jumpto_url = '{0}/{1}/jump_to/{2}'.format('/courses', self.course_key.to_deprecated_string(), location.to_deprecated_string())
......@@ -121,14 +122,14 @@ class ViewsTestCase(TestCase):
# depreciated function
mock_user = MagicMock()
mock_user.is_authenticated.return_value = False
self.assertEquals(views.user_groups(mock_user), [])
self.assertEqual(views.user_groups(mock_user), [])
def test_get_current_child(self):
self.assertIsNone(views.get_current_child(MagicMock()))
mock_xmodule = MagicMock()
mock_xmodule.position = -1
mock_xmodule.get_display_items.return_value = ['one', 'two']
self.assertEquals(views.get_current_child(mock_xmodule), 'one')
self.assertEqual(views.get_current_child(mock_xmodule), 'one')
mock_xmodule_2 = MagicMock()
mock_xmodule_2.position = 3
mock_xmodule_2.get_display_items.return_value = []
......@@ -206,13 +207,13 @@ class ViewsTestCase(TestCase):
chat_settings = views.chat_settings(mock_course, mock_user)
# Test the proper format of all chat settings
self.assertEquals(chat_settings['domain'], domain)
self.assertEquals(chat_settings['room'], "a-b-c_class")
self.assertEquals(chat_settings['username'], "johndoe@%s" % domain)
self.assertEqual(chat_settings['domain'], domain)
self.assertEqual(chat_settings['room'], "a-b-c_class")
self.assertEqual(chat_settings['username'], "johndoe@%s" % domain)
# TODO: this needs to be changed once we figure out how to
# generate/store a real password.
self.assertEquals(chat_settings['password'], "johndoe@%s" % domain)
self.assertEqual(chat_settings['password'], "johndoe@%s" % domain)
def test_course_mktg_about_coming_soon(self):
# we should not be able to find this course
......@@ -450,8 +451,16 @@ class ProgressPageTests(ModuleStoreTestCase):
MakoMiddleware().process_request(self.request)
<<<<<<< HEAD
course = CourseFactory(start=datetime(2013, 9, 16, 7, 17, 28))
self.course = modulestore().get_course(course.id) # pylint: disable=no-member
=======
course = CourseFactory(
start=datetime(2013, 9, 16, 7, 17, 28),
grade_cutoffs={u'çü†øƒƒ': 0.75, 'Pass': 0.5},
)
self.course = modulestore().get_instance(course.id, course.location) # pylint: disable=no-member
>>>>>>> edx/master
self.chapter = ItemFactory(category='chapter', parent_location=self.course.location) # pylint: disable=no-member
self.section = ItemFactory(category='sequential', parent_location=self.chapter.location)
......@@ -460,5 +469,15 @@ class ProgressPageTests(ModuleStoreTestCase):
def test_pure_ungraded_xblock(self):
ItemFactory(category='acid', parent_location=self.vertical.location)
<<<<<<< HEAD
resp = views.progress(self.request, self.course.id.to_deprecated_string())
self.assertEquals(resp.status_code, 200)
=======
resp = views.progress(self.request, self.course.id)
self.assertEqual(resp.status_code, 200)
def test_non_asci_grade_cutoffs(self):
resp = views.progress(self.request, self.course.id)
self.assertEqual(resp.status_code, 200)
>>>>>>> edx/master
......@@ -1324,18 +1324,28 @@ class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCa
self.assertEqual(student_json['username'], student.username)
self.assertEqual(student_json['email'], student.email)
@patch.object(instructor.views.api, 'anonymous_id_for_user', Mock(return_value='42'))
@patch.object(instructor.views.api, 'unique_id_for_user', Mock(return_value='41'))
def test_get_anon_ids(self):
"""
Test the CSV output for the anonymized user ids.
"""
<<<<<<< HEAD
url = reverse('get_anon_ids', kwargs={'course_id': self.course.id.to_deprecated_string()})
with patch('instructor.views.api.unique_id_for_user') as mock_unique:
mock_unique.return_value = '42'
response = self.client.get(url, {})
=======
url = reverse('get_anon_ids', kwargs={'course_id': self.course.id})
response = self.client.get(url, {})
>>>>>>> edx/master
self.assertEqual(response['Content-Type'], 'text/csv')
body = response.content.replace('\r', '')
self.assertTrue(body.startswith('"User ID","Anonymized user ID"\n"2","42"\n'))
self.assertTrue(body.endswith('"7","42"\n'))
self.assertTrue(body.startswith(
'"User ID","Anonymized user ID","Course Specific Anonymized user ID"'
'\n"2","41","42"\n'
))
self.assertTrue(body.endswith('"7","41","42"\n'))
def test_list_report_downloads(self):
url = reverse('list_report_downloads', kwargs={'course_id': self.course.id.to_deprecated_string()})
......
......@@ -17,12 +17,13 @@ from django.core.urlresolvers import reverse
from courseware.tests.helpers import LoginEnrollmentTestCase
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
import instructor.views.legacy
from student.roles import CourseStaffRole
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.django import modulestore, clear_existing_modulestores
from xmodule.modulestore.locations import SlashSeparatedCourseKey
from mock import patch
from mock import Mock, patch
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
......@@ -51,14 +52,25 @@ class TestInstructorDashboardAnonCSV(ModuleStoreTestCase, LoginEnrollmentTestCas
self.login(self.instructor, self.password)
self.enroll(self.toy)
@patch.object(instructor.views.legacy, 'anonymous_id_for_user', Mock(return_value='42'))
@patch.object(instructor.views.legacy, 'unique_id_for_user', Mock(return_value='41'))
def test_download_anon_csv(self):
course = self.toy
<<<<<<< HEAD
url = reverse('instructor_dashboard_legacy', kwargs={'course_id': course.id.to_deprecated_string()})
with patch('instructor.views.legacy.unique_id_for_user') as mock_unique:
mock_unique.return_value = 42
response = self.client.post(url, {'action': 'Download CSV of all student anonymized IDs'})
=======
url = reverse('instructor_dashboard_legacy', kwargs={'course_id': course.id})
response = self.client.post(url, {'action': 'Download CSV of all student anonymized IDs'})
>>>>>>> edx/master
self.assertEqual(response['Content-Type'], 'text/csv')
body = response.content.replace('\r', '')
self.assertEqual(body, '"User ID","Anonymized user ID"\n"2","42"\n')
self.assertEqual(
body,
('"User ID","Anonymized user ID","Course Specific Anonymized user ID"'
'\n"2","41","42"\n')
)
......@@ -33,7 +33,7 @@ from django_comment_common.models import (
)
from courseware.models import StudentModule
from student.models import unique_id_for_user, CourseEnrollment
from student.models import CourseEnrollment, unique_id_for_user, anonymous_id_for_user
import instructor_task.api
from instructor_task.api_helper import AlreadyRunningError
from instructor_task.views import get_task_completion_info
......@@ -630,9 +630,15 @@ def get_anon_ids(request, course_id): # pylint: disable=W0613
students = User.objects.filter(
courseenrollment__course_id=course_id,
).order_by('id')
<<<<<<< HEAD
header = ['User ID', 'Anonymized user ID']
rows = [[s.id, unique_id_for_user(s)] for s in students]
return csv_response(course_id.to_deprecated_string().replace('/', '-') + '-anon-ids.csv', header, rows)
=======
header = ['User ID', 'Anonymized user ID', 'Course Specific Anonymized user ID']
rows = [[s.id, unique_id_for_user(s), anonymous_id_for_user(s, course_id)] for s in students]
return csv_response(course_id.replace('/', '-') + '-anon-ids.csv', header, rows)
>>>>>>> edx/master
@ensure_csrf_cookie
......
......@@ -40,7 +40,6 @@ from lms.lib.xblock.runtime import quote_slashes
# Submissions is a Django app that is currently installed
# from the edx-ora2 repo, although it will likely move in the future.
from submissions import api as sub_api
from student.models import anonymous_id_for_user
from bulk_email.models import CourseEmail, CourseAuthorization
from courseware import grades
......@@ -68,7 +67,12 @@ from instructor_task.views import get_task_completion_info
from edxmako.shortcuts import render_to_response, render_to_string
from class_dashboard import dashboard_data
from psychometrics import psychoanalyze
from student.models import CourseEnrollment, CourseEnrollmentAllowed, unique_id_for_user
from student.models import (
CourseEnrollment,
CourseEnrollmentAllowed,
unique_id_for_user,
anonymous_id_for_user
)
from student.views import course_from_id
import track.views
from xblock.field_data import DictFieldData
......@@ -688,9 +692,15 @@ def instructor_dashboard(request, course_id):
courseenrollment__course_id=course_key,
).order_by('id')
<<<<<<< HEAD
datatable = {'header': ['User ID', 'Anonymized user ID']}
datatable['data'] = [[s.id, unique_id_for_user(s)] for s in students]
return return_csv(course_key.to_deprecated_string().replace('/', '-') + '-anon-ids.csv', datatable)
=======
datatable = {'header': ['User ID', 'Anonymized user ID', 'Course Specific Anonymized user ID']}
datatable['data'] = [[s.id, unique_id_for_user(s), anonymous_id_for_user(s, course_id)] for s in students]
return return_csv(course_id.replace('/', '-') + '-anon-ids.csv', datatable)
>>>>>>> edx/master
#----------------------------------------
# Group management
......
......@@ -103,7 +103,7 @@ $(function () {
descending_grades = sorted(grade_cutoffs, key=lambda x: grade_cutoffs[x], reverse=True)
for grade in descending_grades:
percent = grade_cutoffs[grade]
grade_cutoff_ticks.append( [ percent, "{0} {1:.0%}".format(grade, percent) ] )
grade_cutoff_ticks.append( [ percent, u"{0} {1:.0%}".format(grade, percent) ] )
else:
grade_cutoff_ticks = [ ]
%>
......
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