Commit a647169a by Eric Fischer

Merge pull request #11167 from edx/christina/ora-data-download

WIP: Asynchronous download button for ORA2 data
parents f0925509 2b1a7eec
......@@ -912,6 +912,13 @@ class DataDownloadPage(PageObject):
"""
return self.q(css="#report-downloads-table .file-download-link>a")
@property
def generate_ora2_response_report_button(self):
"""
Returns the ORA2 response download button for the current page.
"""
return self.q(css='input[name=export-ora2-data]')
def wait_for_available_report(self):
"""
Waits for a downloadable report to be available.
......
......@@ -636,6 +636,20 @@ class DataDownloadsTest(BaseInstructorDashboardTest):
self.verify_report_requested_event(report_name)
self.verify_report_download(report_name)
def test_ora2_response_report_download(self):
"""
Scenario: Verify that an instructor can download an ORA2 grade report
Given that I am an instructor
And I visit the instructor dashboard's "Data Downloads" tab
And I click on the "Download ORA2 Responses" button
Then a report should be generated
"""
report_name = u"ORA_data"
self.data_download_section.generate_ora2_response_report_button.click()
self.data_download_section.wait_for_available_report()
self.verify_report_download(report_name)
@attr('shard_7')
class CertificatesTest(BaseInstructorDashboardTest):
......
......@@ -92,7 +92,7 @@ def click_a_button(step, button): # pylint: disable=unused-argument
# Expect to see a message that grade report is being generated
expected_msg = "The grade report is being created." \
" To view the status of the report, see" \
" Pending Instructor Tasks below."
" Pending Tasks below."
world.wait_for_visible('#report-request-response')
assert_in(
expected_msg, world.css_text('#report-request-response'),
......
......@@ -244,6 +244,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
('get_exec_summary_report', {}),
('get_proctored_exam_results', {}),
('get_problem_responses', {}),
('export_ora2_data', {}),
]
# Endpoints that only Instructors can access
self.instructor_level_endpoints = [
......@@ -322,6 +323,8 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
# update_forum_role(self.course.id, staff_member, FORUM_ROLE_ADMINISTRATOR, 'allow')
for endpoint, args in self.staff_level_endpoints:
expected_status = 200
# TODO: make these work
if endpoint in ['update_forum_role_membership', 'list_forum_members']:
continue
......@@ -333,7 +336,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
self._access_endpoint(
endpoint,
args,
200,
expected_status,
"Staff member should be allowed to access endpoint " + endpoint
)
......@@ -356,6 +359,8 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
self.client.login(username=inst.username, password='test')
for endpoint, args in self.staff_level_endpoints:
expected_status = 200
# TODO: make these work
if endpoint in ['update_forum_role_membership']:
continue
......@@ -367,18 +372,20 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
self._access_endpoint(
endpoint,
args,
200,
expected_status,
"Instructor should be allowed to access endpoint " + endpoint
)
for endpoint, args in self.instructor_level_endpoints:
expected_status = 200
# TODO: make this work
if endpoint in ['rescore_problem']:
continue
self._access_endpoint(
endpoint,
args,
200,
expected_status,
"Instructor should be allowed to access endpoint " + endpoint
)
......@@ -2866,8 +2873,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
response = self.client.get(url, {})
success_status = "The {report_type} report is being created." \
" To view the status of the report, see Pending" \
" Instructor Tasks" \
" below".format(report_type=report_type)
" Tasks below".format(report_type=report_type)
self.assertIn(success_status, response.content)
@ddt.data(*EXECUTIVE_SUMMARY_DATA)
......@@ -2888,12 +2894,30 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
mock.side_effect = AlreadyRunningError()
response = self.client.get(url, {})
already_running_status = "The {report_type} report is currently being created." \
" To view the status of the report, see Pending Instructor Tasks below." \
" To view the status of the report, see Pending Tasks below." \
" You will be able to download the report" \
" when it is" \
" complete.".format(report_type=report_type)
self.assertIn(already_running_status, response.content)
def test_get_ora2_responses_success(self):
url = reverse('export_ora2_data', kwargs={'course_id': unicode(self.course.id)})
with patch('instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task:
mock_submit_ora2_task.return_value = True
response = self.client.get(url, {})
success_status = "The ORA data report is being generated."
self.assertIn(success_status, response.content)
def test_get_ora2_responses_already_running(self):
url = reverse('export_ora2_data', kwargs={'course_id': unicode(self.course.id)})
with patch('instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task:
mock_submit_ora2_task.side_effect = AlreadyRunningError()
response = self.client.get(url, {})
already_running_status = "An ORA data report generation task is already in progress."
self.assertIn(already_running_status, response.content)
def test_get_student_progress_url(self):
""" Test that progress_url is in the successful response. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id.to_deprecated_string()})
......
......@@ -1290,12 +1290,12 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
try:
instructor_task.api.submit_calculate_students_features_csv(request, course_key, query_features)
success_status = _("The enrolled learner profile report is being created."
" To view the status of the report, see Pending Instructor Tasks below.")
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _(
"This enrollment report is currently being created."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({"status": already_running_status})
......@@ -1320,13 +1320,13 @@ def get_students_who_may_enroll(request, course_id):
success_status = _(
"The enrollment report is being created. This report contains"
" information about learners who can enroll in the course."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
)
return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _(
"This enrollment report is currently being created."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({"status": already_running_status})
......@@ -1420,11 +1420,11 @@ def get_enrollment_report(request, course_id):
try:
instructor_task.api.submit_detailed_enrollment_features_csv(request, course_key)
success_status = _("The detailed enrollment report is being created."
" To view the status of the report, see Pending Instructor Tasks below.")
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _("The detailed enrollment report is being created."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({
"status": already_running_status
......@@ -1444,11 +1444,11 @@ def get_exec_summary_report(request, course_id):
try:
instructor_task.api.submit_executive_summary_report(request, course_key)
status_response = _("The executive summary report is being created."
" To view the status of the report, see Pending Instructor Tasks below.")
" To view the status of the report, see Pending Tasks below.")
except AlreadyRunningError:
status_response = _(
"The executive summary report is currently being created."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({
......@@ -1468,11 +1468,11 @@ def get_course_survey_results(request, course_id):
try:
instructor_task.api.submit_course_survey_report(request, course_key)
status_response = _("The survey report is being created."
" To view the status of the report, see Pending Instructor Tasks below.")
" To view the status of the report, see Pending Tasks below.")
except AlreadyRunningError:
status_response = _(
"The survey report is currently being created."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({
......@@ -1503,11 +1503,11 @@ def get_proctored_exam_results(request, course_id):
try:
instructor_task.api.submit_proctored_exam_results_report(request, course_key, query_features)
status_response = _("The proctored exam results report is being created."
" To view the status of the report, see Pending Instructor Tasks below.")
" To view the status of the report, see Pending Tasks below.")
except AlreadyRunningError:
status_response = _(
"The proctored exam results report is currently being created."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({
......@@ -2331,6 +2331,31 @@ def list_financial_report_downloads(_request, course_id):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
def export_ora2_data(request, course_id):
"""
Pushes a Celery task which will aggregate ora2 responses for a course into a .csv
"""
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
try:
instructor_task.api.submit_export_ora2_data(request, course_key)
success_status = _("The ORA data report is being generated.")
return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _(
"An ORA data report generation task is already in "
"progress. Check the 'Pending Tasks' table "
"for the status of the task. When completed, the report "
"will be available for download in the table below."
)
return JsonResponse({"status": already_running_status})
@transaction.non_atomic_requests
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
def calculate_grades_csv(request, course_id):
"""
AlreadyRunningError is raised if the course's grades are already being updated.
......@@ -2339,15 +2364,13 @@ def calculate_grades_csv(request, course_id):
try:
instructor_task.api.submit_calculate_grades_csv(request, course_key)
success_status = _("The grade report is being created."
" To view the status of the report, see Pending Instructor Tasks below.")
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _("The grade report is currently being created."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({
"status": already_running_status
})
return JsonResponse({"status": already_running_status})
@transaction.non_atomic_requests
......@@ -2366,11 +2389,11 @@ def problem_grade_report(request, course_id):
try:
instructor_task.api.submit_problem_grade_report(request, course_key)
success_status = _("The problem grade report is being created."
" To view the status of the report, see Pending Instructor Tasks below.")
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _("A problem grade report is already being generated."
" To view the status of the report, see Pending Instructor Tasks below."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({
"status": already_running_status
......
......@@ -117,6 +117,8 @@ urlpatterns = patterns(
'instructor.views.api.get_exec_summary_report', name="get_exec_summary_report"),
url(r'get_course_survey_results$',
'instructor.views.api.get_course_survey_results', name="get_course_survey_results"),
url(r'export_ora2_data',
'instructor.views.api.export_ora2_data', name="export_ora2_data"),
# Coupon Codes..
url(r'get_coupon_codes',
......
......@@ -567,6 +567,7 @@ def _section_data_download(course, access):
'problem_grade_report_url': reverse('problem_grade_report', kwargs={'course_id': unicode(course_key)}),
'course_has_survey': True if course.course_survey_name else False,
'course_survey_results_url': reverse('get_course_survey_results', kwargs={'course_id': unicode(course_key)}),
'export_ora2_data_url': reverse('export_ora2_data', kwargs={'course_id': unicode(course_key)}),
}
return section_data
......
......@@ -28,7 +28,8 @@ from instructor_task.tasks import (
exec_summary_report_csv,
course_survey_report_csv,
generate_certificates,
proctored_exam_results_csv
proctored_exam_results_csv,
export_ora2_data,
)
from certificates.models import CertificateGenerationHistory
......@@ -424,6 +425,18 @@ def submit_cohort_students(request, course_key, file_name):
return submit_task(request, task_type, task_class, course_key, task_input, task_key)
def submit_export_ora2_data(request, course_key):
"""
AlreadyRunningError is raised if an ora2 report is already being generated.
"""
task_type = 'export_ora2_data'
task_class = export_ora2_data
task_input = {}
task_key = ''
return submit_task(request, task_type, task_class, course_key, task_input, task_key)
def generate_certificates_for_students(request, course_key, students=None): # pylint: disable=invalid-name
"""
Submits a task to generate certificates for given students enrolled in the course or
......
......@@ -44,7 +44,8 @@ from instructor_task.tasks_helper import (
upload_exec_summary_report,
upload_course_survey_report,
generate_students_certificates,
upload_proctored_exam_results_report
upload_proctored_exam_results_report,
upload_ora2_data,
)
......@@ -290,3 +291,13 @@ def cohort_students(entry_id, xmodule_instance_args):
action_name = ugettext_noop('cohorted')
task_fn = partial(cohort_students_and_upload, xmodule_instance_args)
return run_main_task(entry_id, task_fn, action_name)
@task(base=BaseInstructorTask, routing_key=settings.GRADES_DOWNLOAD_ROUTING_KEY) # pylint: disable=not-callable
def export_ora2_data(entry_id, xmodule_instance_args):
"""
Generate a CSV of ora2 responses and push it to S3.
"""
action_name = ugettext_noop('generated')
task_fn = partial(upload_ora2_data, xmodule_instance_args)
return run_main_task(entry_id, task_fn, action_name)
......@@ -57,6 +57,7 @@ from instructor_analytics.basic import (
list_problem_responses
)
from instructor_analytics.csvs import format_dictlist
from openassessment.data import OraAggregateData
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
......@@ -1599,3 +1600,70 @@ def invalidate_generated_certificates(course_id, enrolled_students, certificate_
download_url='',
grade='',
)
def upload_ora2_data(
_xmodule_instance_args, _entry_id, course_id, _task_input, action_name
):
"""
Collect ora2 responses and upload them to S3 as a CSV
"""
start_date = datetime.now(UTC)
start_time = time()
num_attempted = 1
num_total = 1
fmt = u'Task: {task_id}, InstructorTask ID: {entry_id}, Course: {course_id}, Input: {task_input}'
task_info_string = fmt.format(
task_id=_xmodule_instance_args.get('task_id') if _xmodule_instance_args is not None else None,
entry_id=_entry_id,
course_id=course_id,
task_input=_task_input
)
TASK_LOG.info(u'%s, Task type: %s, Starting task execution', task_info_string, action_name)
task_progress = TaskProgress(action_name, num_total, start_time)
task_progress.attempted = num_attempted
curr_step = {'step': "Collecting responses"}
TASK_LOG.info(
u'%s, Task type: %s, Current step: %s for all submissions',
task_info_string,
action_name,
curr_step,
)
task_progress.update_task_state(extra_meta=curr_step)
try:
header, datarows = OraAggregateData.collect_ora2_data(course_id)
rows = [header] + [row for row in datarows]
# Update progress to failed regardless of error type
except Exception: # pylint: disable=broad-except
TASK_LOG.exception('Failed to get ORA data.')
task_progress.failed = 1
curr_step = {'step': "Error while collecting data"}
task_progress.update_task_state(extra_meta=curr_step)
return UPDATE_STATUS_FAILED
task_progress.succeeded = 1
curr_step = {'step': "Uploading CSV"}
TASK_LOG.info(
u'%s, Task type: %s, Current step: %s',
task_info_string,
action_name,
curr_step,
)
task_progress.update_task_state(extra_meta=curr_step)
upload_csv_to_report_store(rows, 'ORA_data', course_id, start_date)
curr_step = {'step': 'Finalizing ORA data report'}
task_progress.update_task_state(extra_meta=curr_step)
TASK_LOG.info(u'%s, Task type: %s, Upload complete.', task_info_string, action_name)
return UPDATE_STATUS_SUCCEEDED
"""
Test for LMS instructor background task queue management
"""
from mock import patch, Mock
from mock import patch, Mock, MagicMock
from bulk_email.models import CourseEmail, SEND_TO_ALL
from courseware.tests.factories import UserFactory
from xmodule.modulestore.exceptions import ItemNotFoundError
......@@ -22,16 +22,20 @@ from instructor_task.api import (
submit_executive_summary_report,
submit_course_survey_report,
generate_certificates_for_students,
regenerate_certificates
regenerate_certificates,
submit_export_ora2_data,
)
from instructor_task.api_helper import AlreadyRunningError
from instructor_task.models import InstructorTask, PROGRESS
from instructor_task.tests.test_base import (InstructorTaskTestCase,
InstructorTaskCourseTestCase,
InstructorTaskModuleTestCase,
TestReportMixin,
TEST_COURSE_KEY)
from instructor_task.tasks import export_ora2_data
from instructor_task.tests.test_base import (
InstructorTaskTestCase,
InstructorTaskCourseTestCase,
InstructorTaskModuleTestCase,
TestReportMixin,
TEST_COURSE_KEY,
)
from certificates.models import CertificateStatuses, CertificateGenerationHistory
......@@ -256,6 +260,16 @@ class InstructorTaskCourseSubmitTest(TestReportMixin, InstructorTaskCourseTestCa
)
self._test_resubmission(api_call)
def test_submit_ora2_request_task(self):
request = self.create_task_request(self.instructor)
with patch('instructor_task.api.submit_task') as mock_submit_task:
mock_submit_task.return_value = MagicMock()
submit_export_ora2_data(request, self.course.id)
mock_submit_task.assert_called_once_with(
request, 'export_ora2_data', export_ora2_data, self.course.id, {}, '')
def test_submit_generate_certs_students(self):
"""
Tests certificates generation task submission api
......
......@@ -11,6 +11,8 @@ from uuid import uuid4
from mock import Mock, MagicMock, patch
from celery.states import SUCCESS, FAILURE
from django.utils.translation import ugettext_noop
from functools import partial
from xmodule.modulestore.exceptions import ItemNotFoundError
from opaque_keys.edx.locations import i4xEncoder
......@@ -27,8 +29,12 @@ from instructor_task.tasks import (
reset_problem_attempts,
delete_problem_state,
generate_certificates,
export_ora2_data,
)
from instructor_task.tasks_helper import (
UpdateProblemModuleStateError,
upload_ora2_data,
)
from instructor_task.tasks_helper import UpdateProblemModuleStateError
PROBLEM_URL_NAME = "test_urlname"
......@@ -471,3 +477,31 @@ class TestCertificateGenerationnstructorTask(TestInstructorTasks):
expected_attempted=1,
expected_total=1
)
class TestOra2ResponsesInstructorTask(TestInstructorTasks):
"""Tests instructor task that fetches ora2 response data."""
def test_ora2_missing_current_task(self):
self._test_missing_current_task(export_ora2_data)
def test_ora2_with_failure(self):
self._test_run_with_failure(export_ora2_data, 'We expected this to fail')
def test_ora2_with_long_error_msg(self):
self._test_run_with_long_error_msg(export_ora2_data)
def test_ora2_with_short_error_msg(self):
self._test_run_with_short_error_msg(export_ora2_data)
def test_ora2_runs_task(self):
task_entry = self._create_input_entry()
task_xmodule_args = self._get_xmodule_instance_args()
with patch('instructor_task.tasks.run_main_task') as mock_main_task:
export_ora2_data(task_entry.id, task_xmodule_args)
action_name = ugettext_noop('generated')
task_fn = partial(upload_ora2_data, task_xmodule_args)
mock_main_task.assert_called_once_with_args(task_entry.id, task_fn, action_name)
......@@ -3,10 +3,18 @@
"""
Unit tests for LMS instructor-initiated background tasks helper functions.
Tests that CSV grade report generation works with unicode emails.
- Tests that CSV grade report generation works with unicode emails.
- Tests all of the existing reports.
"""
import os
import shutil
from datetime import datetime
import urllib
import ddt
from freezegun import freeze_time
from mock import Mock, patch
import tempfile
import json
......@@ -22,6 +30,15 @@ from course_modes.models import CourseMode
from courseware.tests.factories import InstructorFactory
from instructor_task.tests.test_base import InstructorTaskCourseTestCase, TestReportMixin, InstructorTaskModuleTestCase
from openedx.core.djangoapps.course_groups.models import CourseUserGroupPartitionGroup, CohortMembership
from django.conf import settings
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from pytz import UTC
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
import openedx.core.djangoapps.user_api.course_tag.api as course_tag_api
from openedx.core.djangoapps.user_api.partition_schemes import RandomUserPartitionScheme
......@@ -45,6 +62,9 @@ from instructor_task.tasks_helper import (
upload_exec_summary_report,
upload_course_survey_report,
generate_students_certificates,
upload_ora2_data,
UPDATE_STATUS_FAILED,
UPDATE_STATUS_SUCCEEDED,
)
from instructor_analytics.basic import UNAVAILABLE
from openedx.core.djangoapps.util.testing import ContentGroupTestCase, TestConditionalContent
......@@ -2012,3 +2032,56 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
},
result
)
class TestInstructorOra2Report(SharedModuleStoreTestCase):
"""
Tests that ORA2 response report generation works.
"""
@classmethod
def setUpClass(cls):
super(TestInstructorOra2Report, cls).setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(TestInstructorOra2Report, self).setUp()
self.current_task = Mock()
self.current_task.update_state = Mock()
def tearDown(self):
super(TestInstructorOra2Report, self).tearDown()
if os.path.exists(settings.GRADES_DOWNLOAD['ROOT_PATH']):
shutil.rmtree(settings.GRADES_DOWNLOAD['ROOT_PATH'])
def test_report_fails_if_error(self):
with patch('instructor_task.tasks_helper.OraAggregateData.collect_ora2_data') as mock_collect_data:
mock_collect_data.side_effect = KeyError
with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task:
mock_current_task.return_value = self.current_task
response = upload_ora2_data(None, None, self.course.id, None, 'generated')
self.assertEqual(response, UPDATE_STATUS_FAILED)
@freeze_time('2001-01-01 00:00:00')
def test_report_stores_results(self):
test_header = ['field1', 'field2']
test_rows = [['row1_field1', 'row1_field2'], ['row2_field1', 'row2_field2']]
with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task:
mock_current_task.return_value = self.current_task
with patch('instructor_task.tasks_helper.OraAggregateData.collect_ora2_data') as mock_collect_data:
mock_collect_data.return_value = (test_header, test_rows)
with patch('instructor_task.models.LocalFSReportStore.store_rows') as mock_store_rows:
return_val = upload_ora2_data(None, None, self.course.id, None, 'generated')
# pylint: disable=maybe-no-member
timestamp_str = datetime.now(UTC).strftime('%Y-%m-%d-%H%M')
course_id_string = urllib.quote(self.course.id.to_deprecated_string().replace('/', '_'))
filename = u'{}_ORA_data_{}.csv'.format(course_id_string, timestamp_str)
self.assertEqual(return_val, UPDATE_STATUS_SUCCEEDED)
mock_store_rows.assert_called_once_with(self.course.id, filename, [test_header] + test_rows)
......@@ -2230,6 +2230,8 @@ BADGR_BASE_URL = "http://localhost:8005"
BADGR_ISSUER_SLUG = "example-issuer"
###################### Grade Downloads ######################
# These keys are used for all of our asynchronous downloadable files, including
# the ones that contain information other than grades.
GRADES_DOWNLOAD_ROUTING_KEY = HIGH_MEM_QUEUE
GRADES_DOWNLOAD = {
......@@ -2244,7 +2246,6 @@ FINANCIAL_REPORTS = {
'ROOT_PATH': '/tmp/edx-s3/financial_reports',
}
#### PASSWORD POLICY SETTINGS #####
PASSWORD_MIN_LENGTH = 8
PASSWORD_MAX_LENGTH = None
......
......@@ -83,6 +83,7 @@ class DataDownload
@$grade_config_btn = @$section.find("input[name='dump-gradeconf']'")
@$calculate_grades_csv_btn = @$section.find("input[name='calculate-grades-csv']'")
@$problem_grade_report_csv_btn = @$section.find("input[name='problem-grade-report']'")
@$async_report_btn = @$section.find("input[class='async-report-btn']'")
# response areas
@$download = @$section.find '.data-download-container'
......@@ -236,27 +237,26 @@ class DataDownload
@clear_display()
@$download_display_text.html data['grading_config_summary']
@$calculate_grades_csv_btn.click (e) =>
@onClickGradeDownload @$calculate_grades_csv_btn, gettext("Error generating grades. Please try again.")
@$problem_grade_report_csv_btn.click (e) =>
@onClickGradeDownload @$problem_grade_report_csv_btn, gettext("Error generating problem grade report. Please try again.")
onClickGradeDownload: (button, errorMessage) ->
# Clear any CSS styling from the request-response areas
#$(".msg-confirm").css({"display":"none"})
#$(".msg-error").css({"display":"none"})
@clear_display()
url = button.data 'endpoint'
$.ajax
dataType: 'json'
url: url
error: (std_ajax_err) =>
@$reports_request_response_error.text errorMessage
$(".msg-error").css({"display":"block"})
success: (data) =>
@$reports_request_response.text data['status']
$(".msg-confirm").css({"display":"block"})
@$async_report_btn.click (e) =>
# Clear any CSS styling from the request-response areas
#$(".msg-confirm").css({"display":"none"})
#$(".msg-error").css({"display":"none"})
@clear_display()
url = $(e.target).data 'endpoint'
$.ajax
dataType: 'json'
url: url
error: std_ajax_err =>
if e.target.name == 'calculate-grades-csv'
@$grades_request_response_error.text gettext("Error generating grades. Please try again.")
else if e.target.name == 'problem-grade-report'
@$grades_request_response_error.text gettext("Error generating problem grade report. Please try again.")
else if e.target.name == 'export-ora2-data'
@$grades_request_response_error.text gettext("Error generating ORA data report. Please try again.")
$(".msg-error").css({"display":"block"})
success: (data) =>
@$reports_request_response.text data['status']
$(".msg-confirm").css({"display":"block"})
# handler for when the section title is clicked.
onClickTitle: ->
......
......@@ -75,10 +75,11 @@
%if settings.FEATURES.get('ALLOW_COURSE_STAFF_GRADE_DOWNLOADS') or section_data['access']['admin']:
<p>${_("Click to generate a CSV grade report for all currently enrolled students.")}</p>
<p><input type="button" name="calculate-grades-csv" value="${_("Generate Grade Report")}" data-endpoint="${ section_data['calculate_grades_csv_url'] }"/></p>
<p><input type="button" name="problem-grade-report" value="${_("Generate Problem Grade Report")}" data-endpoint="${ section_data['problem_grade_report_url'] }"/></p>
<p>
<input type="button" name="calculate-grades-csv" class="async-report-btn" value="${_("Generate Grade Report")}" data-endpoint="${ section_data['calculate_grades_csv_url'] }"/>
<input type="button" name="problem-grade-report" class="async-report-btn" value="${_("Generate Problem Grade Report")}" data-endpoint="${ section_data['problem_grade_report_url'] }"/>
<input type="button" name="export-ora2-data" class="async-report-btn" value="${_("Generate ORA Data Report")}" data-endpoint="${ section_data['export_ora2_data_url'] }"/>
</p>
%endif
<div class="request-response msg msg-confirm copy" id="report-request-response"></div>
......
......@@ -76,8 +76,8 @@ git+https://github.com/edx/XBlock.git@xblock-0.4.5#egg=XBlock==0.4.5
-e git+https://github.com/edx/event-tracking.git@0.2.1#egg=event-tracking==0.2.1
-e git+https://github.com/edx/django-splash.git@v0.2#egg=django-splash==0.2
-e git+https://github.com/edx/acid-block.git@e46f9cda8a03e121a00c7e347084d142d22ebfb7#egg=acid-xblock
git+https://github.com/edx/edx-ora2.git@1.0.1#egg=ora2==1.0.1
-e git+https://github.com/edx/edx-submissions.git@1.0.0#egg=edx-submissions==1.0.0
git+https://github.com/edx/edx-ora2.git@1.1.0#egg=ora2==1.1.0
-e git+https://github.com/edx/edx-submissions.git@1.1.0#egg=edx-submissions==1.1.0
git+https://github.com/edx/ease.git@release-2015-07-14#egg=ease==0.1.3
git+https://github.com/edx/i18n-tools.git@v0.2#egg=i18n-tools==v0.2
git+https://github.com/edx/edx-val.git@0.0.9#egg=edxval==0.0.9
......
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