From 06390c014292e0f6ca526ebd3d628e22e9aeec12 Mon Sep 17 00:00:00 2001 From: Moeez Zahid Date: Fri, 25 Sep 2020 13:01:53 +0500 Subject: [PATCH 01/16] MCKIN-24885 CMS: Add missing app_label (#1922) --- cms/djangoapps/course_creators/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cms/djangoapps/course_creators/models.py b/cms/djangoapps/course_creators/models.py index bfb09a0c3053..b3361696b32d 100644 --- a/cms/djangoapps/course_creators/models.py +++ b/cms/djangoapps/course_creators/models.py @@ -49,6 +49,9 @@ class CourseCreator(models.Model): note = models.CharField(max_length=512, blank=True, help_text=_("Optional notes about this user (for example, " "why course creation access was denied)")) + class Meta: + app_label = 'course_creators' + def __str__(self): return u"{0} | {1} [{2}]".format(self.user, self.state, self.state_changed) From 9d1ffa3bbb4c600ce656b0192fcb48e8d0e0cb72 Mon Sep 17 00:00:00 2001 From: Moeez Zahid Date: Thu, 31 Dec 2020 11:34:21 +0500 Subject: [PATCH 02/16] MCKIN-24885 Fix CourseCreator app not being detected by LMS (#2053) --- cms/djangoapps/contentstore/tests/test_libraries.py | 2 +- cms/djangoapps/contentstore/views/course.py | 2 +- cms/djangoapps/contentstore/views/library.py | 2 +- cms/djangoapps/contentstore/views/tests/test_library.py | 2 +- cms/djangoapps/contentstore/views/user.py | 2 +- cms/djangoapps/course_creators/admin.py | 4 ++-- cms/djangoapps/course_creators/tests/test_admin.py | 8 ++++---- cms/djangoapps/course_creators/tests/test_views.py | 2 +- cms/djangoapps/course_creators/views.py | 2 +- cms/envs/common.py | 2 +- lms/envs/common.py | 3 +++ 11 files changed, 17 insertions(+), 14 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_libraries.py b/cms/djangoapps/contentstore/tests/test_libraries.py index ea3bf9980650..9b94143b0be4 100644 --- a/cms/djangoapps/contentstore/tests/test_libraries.py +++ b/cms/djangoapps/contentstore/tests/test_libraries.py @@ -15,7 +15,7 @@ from contentstore.views.item import _duplicate_item from contentstore.views.preview import _load_preview_module from contentstore.views.tests.test_library import LIBRARY_REST_URL -from course_creators.views import add_user_with_status_granted +from cms.djangoapps.course_creators.views import add_user_with_status_granted from student import auth from student.auth import has_studio_read_access, has_studio_write_access from student.roles import ( diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 8f1208ea2660..ec1c89572d79 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -56,7 +56,7 @@ from contentstore.views.entrance_exam import create_entrance_exam, delete_entrance_exam, update_entrance_exam from course_action_state.managers import CourseActionStateItemNotFoundError from course_action_state.models import CourseRerunState, CourseRerunUIStateManager -from course_creators.views import add_user_with_status_unrequested, get_course_creator_status +from cms.djangoapps.course_creators.views import add_user_with_status_unrequested, get_course_creator_status from course_modes.models import CourseMode from edxmako.shortcuts import render_to_response from edx_notifications.data import NotificationMessage diff --git a/cms/djangoapps/contentstore/views/library.py b/cms/djangoapps/contentstore/views/library.py index 8fc60dc9ad36..7b8c29278128 100644 --- a/cms/djangoapps/contentstore/views/library.py +++ b/cms/djangoapps/contentstore/views/library.py @@ -21,7 +21,7 @@ from contentstore.utils import add_instructor, reverse_library_url from contentstore.views.item import create_xblock_info -from course_creators.views import get_course_creator_status +from cms.djangoapps.course_creators.views import get_course_creator_status from edxmako.shortcuts import render_to_response from student.auth import ( STUDIO_EDIT_ROLES, diff --git a/cms/djangoapps/contentstore/views/tests/test_library.py b/cms/djangoapps/contentstore/views/tests/test_library.py index 234476150064..c4dce1f91191 100644 --- a/cms/djangoapps/contentstore/views/tests/test_library.py +++ b/cms/djangoapps/contentstore/views/tests/test_library.py @@ -17,7 +17,7 @@ from contentstore.utils import reverse_course_url, reverse_library_url from contentstore.views.component import get_component_templates from contentstore.views.library import get_library_creator_status -from course_creators.views import add_user_with_status_granted as grant_course_creator_status +from cms.djangoapps.course_creators.views import add_user_with_status_granted as grant_course_creator_status from student.roles import LibraryUserRole from xmodule.modulestore.tests.factories import LibraryFactory diff --git a/cms/djangoapps/contentstore/views/user.py b/cms/djangoapps/contentstore/views/user.py index ff37bd675618..d6091bf75b40 100644 --- a/cms/djangoapps/contentstore/views/user.py +++ b/cms/djangoapps/contentstore/views/user.py @@ -11,7 +11,7 @@ from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import LibraryLocator -from course_creators.views import user_requested_access +from cms.djangoapps.course_creators.views import user_requested_access from edxmako.shortcuts import render_to_response from student import auth from student.auth import STUDIO_EDIT_ROLES, STUDIO_VIEW_USERS, get_user_permissions diff --git a/cms/djangoapps/course_creators/admin.py b/cms/djangoapps/course_creators/admin.py index 345fb52325d5..aa3e8827d0be 100644 --- a/cms/djangoapps/course_creators/admin.py +++ b/cms/djangoapps/course_creators/admin.py @@ -11,8 +11,8 @@ from django.core.mail import send_mail from django.dispatch import receiver -from course_creators.models import CourseCreator, send_admin_notification, send_user_notification, update_creator_state -from course_creators.views import update_course_creator_group +from cms.djangoapps.course_creators.models import CourseCreator, send_admin_notification, send_user_notification, update_creator_state +from cms.djangoapps.course_creators.views import update_course_creator_group from edxmako.shortcuts import render_to_string log = logging.getLogger("studio.coursecreatoradmin") diff --git a/cms/djangoapps/course_creators/tests/test_admin.py b/cms/djangoapps/course_creators/tests/test_admin.py index 550d9b77d83b..042e793af83a 100644 --- a/cms/djangoapps/course_creators/tests/test_admin.py +++ b/cms/djangoapps/course_creators/tests/test_admin.py @@ -11,8 +11,8 @@ from django.test import TestCase from six.moves import range -from course_creators.admin import CourseCreatorAdmin -from course_creators.models import CourseCreator +from cms.djangoapps.course_creators.admin import CourseCreatorAdmin +from cms.djangoapps.course_creators.models import CourseCreator from student import auth from student.roles import CourseCreatorRole @@ -48,7 +48,7 @@ def setUp(self): "STUDIO_REQUEST_EMAIL": self.studio_request_email } - @mock.patch('course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) + @mock.patch('cms.djangoapps.course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) @mock.patch('django.contrib.auth.models.User.email_user') def test_change_status(self, email_user): """ @@ -92,7 +92,7 @@ def change_state_and_verify_email(state, is_creator): change_state_and_verify_email(CourseCreator.DENIED, False) - @mock.patch('course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) + @mock.patch('cms.djangoapps.course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) def test_mail_admin_on_pending(self): """ Tests that the admin account is notified when a user is in the 'pending' state. diff --git a/cms/djangoapps/course_creators/tests/test_views.py b/cms/djangoapps/course_creators/tests/test_views.py index 7b195706189a..faea3a70e63e 100644 --- a/cms/djangoapps/course_creators/tests/test_views.py +++ b/cms/djangoapps/course_creators/tests/test_views.py @@ -9,7 +9,7 @@ from django.test import TestCase from django.urls import reverse -from course_creators.views import ( +from cms.djangoapps.course_creators.views import ( add_user_with_status_granted, add_user_with_status_unrequested, get_course_creator_status, diff --git a/cms/djangoapps/course_creators/views.py b/cms/djangoapps/course_creators/views.py index efc3bf6653c2..06727c17be08 100644 --- a/cms/djangoapps/course_creators/views.py +++ b/cms/djangoapps/course_creators/views.py @@ -3,7 +3,7 @@ """ -from course_creators.models import CourseCreator +from cms.djangoapps.course_creators.models import CourseCreator from student import auth from student.roles import CourseCreatorRole diff --git a/cms/envs/common.py b/cms/envs/common.py index fd291fc48c08..8e0e643803ee 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1356,7 +1356,7 @@ 'contentstore.apps.ContentstoreConfig', 'openedx.core.djangoapps.contentserver', - 'course_creators', + 'cms.djangoapps.course_creators', 'student.apps.StudentConfig', # misleading name due to sharing with lms 'openedx.core.djangoapps.course_groups', # not used in cms (yet), but tests run 'xblock_config.apps.XBlockConfig', diff --git a/lms/envs/common.py b/lms/envs/common.py index 74b219e8283a..4c9dd9b5202f 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2353,6 +2353,9 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring # edX Video Pipeline integration 'openedx.core.djangoapps.video_pipeline', + # CMS apps + 'cms.djangoapps.course_creators', + # Our courseware 'lms.djangoapps.courseware', 'coursewarehistoryextended', From 25e52c635f09807fd91f5270b4a05901f47af4c2 Mon Sep 17 00:00:00 2001 From: Moeez Zahid Date: Mon, 4 Jan 2021 12:43:31 +0500 Subject: [PATCH 03/16] MCKIN-24885 Add user_tasks to lms installed apps (#2057) --- lms/envs/common.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lms/envs/common.py b/lms/envs/common.py index 4c9dd9b5202f..17bc70ec8815 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2544,6 +2544,9 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring # be retried 'celery_utils', + # management of user-triggered async tasks (course import/export, etc.) + 'user_tasks', + # Ability to detect and special-case crawler behavior 'openedx.core.djangoapps.crawlers', From 51ca0573597cd1cd03e4813433e3177b6a9a4bb2 Mon Sep 17 00:00:00 2001 From: ahmed-zubair12 <74174850+ahmed-zubair12@users.noreply.github.com> Date: Fri, 12 Mar 2021 12:27:50 +0500 Subject: [PATCH 04/16] MCKIN-31550 - Parsing xml using unicode to handle special characters (#2127) --- common/lib/xmodule/xmodule/modulestore/xml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/lib/xmodule/xmodule/modulestore/xml.py b/common/lib/xmodule/xmodule/modulestore/xml.py index 8680ea7d9c25..7f22926a2728 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml.py +++ b/common/lib/xmodule/xmodule/modulestore/xml.py @@ -249,7 +249,7 @@ def load_item(usage_key, for_parent=None): # id_generator is ignored, because each ImportSystem is already local to # a course, and has it's own id_generator already in place def add_node_as_child(self, block, node, id_generator): - child_block = self.process_xml(etree.tostring(node)) + child_block = self.process_xml(etree.tostring(node, encoding='unicode')) block.children.append(child_block.scope_ids.usage_id) From f6bc9cfd671be619e94d00cd654ad536f0b01965 Mon Sep 17 00:00:00 2001 From: ahmed-zubair12 <74174850+ahmed-zubair12@users.noreply.github.com> Date: Fri, 12 Mar 2021 17:06:20 +0500 Subject: [PATCH 05/16] MCKIN-30997 - Gradebook version bump (#2128) --- requirements/edx/custom.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/edx/custom.txt b/requirements/edx/custom.txt index 83162cb91af1..088eed0db603 100644 --- a/requirements/edx/custom.txt +++ b/requirements/edx/custom.txt @@ -21,7 +21,7 @@ git+https://github.com/edx-solutions/xblock-group-project.git@1.0.0#egg=xblock-g -e git+https://github.com/open-craft/xblock-virtualreality.git@v0.1.5#egg=xblock-virtualreality==0.1.5 -e git+https://github.com/edx/edx-notifications.git@3.0.0#egg=edx-notifications==3.0.0 -git+https://github.com/edx-solutions/gradebook-edx-platform-extensions.git@3.0.2#egg=gradebook-edx-platform-extensions==3.0.2 +git+https://github.com/edx-solutions/gradebook-edx-platform-extensions.git@3.0.3#egg=gradebook-edx-platform-extensions==3.0.3 git+https://github.com/edx-solutions/mobileapps-edx-platform-extensions.git@3.0.0#egg=mobileapps-edx-platform-extensions==3.0.0 git+https://github.com/edx-solutions/discussion-edx-platform-extensions.git@3.0.1#egg=discussion-edx-platform-extensions==3.0.1 git+https://github.com/edx-solutions/organizations-edx-platform-extensions.git@v3.0.0#egg=organizations-edx-platform-extensions==3.0.0 From 5501c8cb10a3d894c83ae847a1fc2c06117839e3 Mon Sep 17 00:00:00 2001 From: ahmed-zubair12 <74174850+ahmed-zubair12@users.noreply.github.com> Date: Wed, 17 Mar 2021 02:57:59 +0500 Subject: [PATCH 06/16] MCKIN-31000 - Adding course staff to default_cohort when viewing cohorted discussion (#2120) * MCKIN-31000 - Adding course staff to default_cohort when viewing cohorted discussion * MCKIN-31000 - Improved code, Added test case * formatting --- openedx/core/djangoapps/course_groups/cohorts.py | 13 +++++++++++-- .../djangoapps/course_groups/tests/test_cohorts.py | 11 ++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/openedx/core/djangoapps/course_groups/cohorts.py b/openedx/core/djangoapps/course_groups/cohorts.py index 73beca72ee19..504e12291ce5 100644 --- a/openedx/core/djangoapps/course_groups/cohorts.py +++ b/openedx/core/djangoapps/course_groups/cohorts.py @@ -21,7 +21,7 @@ from lms.djangoapps.courseware import courses from openedx.core.lib.cache_utils import request_cached -from student.models import get_user_by_username_or_email +from student.models import get_user_by_username_or_email, CourseAccessRole from .models import ( CohortMembership, @@ -264,7 +264,8 @@ def get_cohort(user, course_key, assign=True, use_cached=False): assignment.delete() break else: - course_user_group = get_random_cohort(course_key) + is_user_staff = CourseAccessRole.objects.filter(user=user, course_id=course_key, role='staff').exists() + course_user_group = get_default_cohort(course_key) if is_user_staff else get_random_cohort(course_key) add_user_to_cohort(course_user_group, user) return course_user_group except ValueError: @@ -281,6 +282,14 @@ def get_cohort(user, course_key, assign=True, use_cached=False): return get_cohort(user, course_key, assign, use_cached) +def get_default_cohort(course_key): + try: + cohort = get_cohort_by_name(course_key, CourseUserGroup.default_cohort_name) + except CourseUserGroup.DoesNotExist: + cohort = add_cohort(course_key, CourseUserGroup.default_cohort_name, CourseCohort.MANUAL) + return cohort + + def get_random_cohort(course_key): """ Helper method to get a cohort for random assignment. diff --git a/openedx/core/djangoapps/course_groups/tests/test_cohorts.py b/openedx/core/djangoapps/course_groups/tests/test_cohorts.py index 8a26f72b3d29..d582191f85ad 100644 --- a/openedx/core/djangoapps/course_groups/tests/test_cohorts.py +++ b/openedx/core/djangoapps/course_groups/tests/test_cohorts.py @@ -15,7 +15,7 @@ from six import text_type from six.moves import range -from student.models import CourseEnrollment +from student.models import CourseEnrollment, CourseAccessRole from student.tests.factories import UserFactory from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import TEST_DATA_MIXED_MODULESTORE, ModuleStoreTestCase @@ -235,10 +235,13 @@ def test_get_cohort(self): user = UserFactory(username="test", email="a@b.com") other_user = UserFactory(username="test2", email="a2@b.com") + staff_user = UserFactory(username="test3", email="a3@b.com") self.assertIsNone(cohorts.get_cohort(user, course.id), "No cohort created yet") cohort = CohortFactory(course_id=course.id, name="TestCohort", users=[user]) + role = CourseAccessRole(user=staff_user, role="staff", course_id=course.id, org=course.org) + role.save() self.assertIsNone( cohorts.get_cohort(user, course.id), @@ -260,6 +263,12 @@ def test_get_cohort(self): "other_user should be assigned to the default cohort" ) + self.assertEqual( + cohorts.get_cohort(staff_user, course.id).id, + cohorts.get_cohort_by_name(course.id, CourseUserGroup.default_cohort_name).id, + "staff_user should be assigned to default cohort" + ) + def test_get_cohort_preassigned_user(self): """ When an email address is added to a cohort and a user signs up for the course with that email address, From c6cda47f3ce80f88ad499fcc2d4530135dad2a0e Mon Sep 17 00:00:00 2001 From: ahmed-zubair12 <74174850+ahmed-zubair12@users.noreply.github.com> Date: Wed, 17 Mar 2021 09:08:35 +0500 Subject: [PATCH 07/16] MCKIN-24491 - Cherry pick commit into development (#2130) * MCKIN-24491 - Generating poll/survey reports batch wise * Pylint errors fix --- lms/djangoapps/instructor_analytics/basic.py | 7 +- lms/djangoapps/instructor_task/models.py | 17 +- .../instructor_task/tasks_helper/grades.py | 189 ++++++++++-------- .../instructor_task/tasks_helper/utils.py | 28 ++- .../tests/test_tasks_helper.py | 44 ++-- 5 files changed, 173 insertions(+), 112 deletions(-) diff --git a/lms/djangoapps/instructor_analytics/basic.py b/lms/djangoapps/instructor_analytics/basic.py index 0ed2c3def033..dc87bec41b57 100644 --- a/lms/djangoapps/instructor_analytics/basic.py +++ b/lms/djangoapps/instructor_analytics/basic.py @@ -433,7 +433,7 @@ def extract_coupon(coupon, features): return [extract_coupon(coupon, features) for coupon in coupons_list] -def list_problem_responses(course_key, problem_location, limit_responses=None): +def list_problem_responses(course_key, problem_location, limit_responses=None, batch_no=None, batch_size=None): """ Return responses to a given problem as a dict. @@ -464,8 +464,11 @@ def list_problem_responses(course_key, problem_location, limit_responses=None): module_state_key=problem_key ) smdat = smdat.order_by('student') - if limit_responses is not None: + + if limit_responses: smdat = smdat[:limit_responses] + if batch_no: + smdat = smdat[(batch_no - 1) * batch_size:batch_no * batch_size] return [ {'username': response.student.username, 'state': get_response_state(response)} diff --git a/lms/djangoapps/instructor_task/models.py b/lms/djangoapps/instructor_task/models.py index ec5920d34d60..27c9e2825aa7 100644 --- a/lms/djangoapps/instructor_task/models.py +++ b/lms/djangoapps/instructor_task/models.py @@ -217,7 +217,7 @@ def from_config(cls, config_name): 'bucket': config['BUCKET'], 'location': config['ROOT_PATH'], 'custom_domain': config.get("CUSTOM_DOMAIN", None), - 'querystring_expire': 1200, + 'querystring_expire': 5400, 'gzip': True, }, ) @@ -272,6 +272,21 @@ def from_config(cls, config_name): getattr(settings, config_name).get('STORAGE_KWARGS'), ) + def add_rows(self, rows, output_buffer=None): + """ + Given an output buffer and rows (each row is an iterable of + strings), add rows to output buffer in csv format and return it. + """ + if not output_buffer: + output_buffer = ContentFile('') + # Adding unicode signature (BOM) for MS Excel 2013 compatibility + if six.PY2: + output_buffer.write(codecs.BOM_UTF8) + + csvwriter = csv.writer(output_buffer) + csvwriter.writerows(self._get_utf8_encoded_rows(rows)) + return output_buffer + def store(self, course_id, filename, buff): """ Store the contents of `buff` in a directory determined by hashing diff --git a/lms/djangoapps/instructor_task/tasks_helper/grades.py b/lms/djangoapps/instructor_task/tasks_helper/grades.py index 4e2de1fa0332..4df2d4b7cd81 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/grades.py +++ b/lms/djangoapps/instructor_task/tasks_helper/grades.py @@ -45,7 +45,7 @@ from xmodule.split_test_module import get_split_user_partitions from .runner import TaskProgress -from .utils import upload_csv_to_report_store +from .utils import upload_csv_to_report_store, get_report_info, tracker_emit TASK_LOG = logging.getLogger('edx.celery.task') @@ -880,7 +880,7 @@ def _build_student_data( filter_types (List[str]): The report generator will only include data for block types in this list. Returns: - Tuple[List[Dict], List[str]]: Returns a list of dictionaries + Tuple[List[Dict], List[str]]: Yields a list of dictionaries containing the student data which will be included in the final csv, and the features/keys to include in that CSV. """ @@ -890,81 +890,88 @@ def _build_student_data( ] user = get_user_model().objects.get(pk=user_id) - student_data = [] max_count = settings.FEATURES.get('MAX_PROBLEM_RESPONSES_COUNT') - store = modulestore() user_state_client = DjangoXBlockUserStateClient() student_data_keys = set() - with store.bulk_operations(course_key): - for usage_key in usage_keys: - if max_count is not None and max_count <= 0: - break - course_blocks = get_course_blocks(user, usage_key) - base_path = cls._build_block_base_path(store.get_item(usage_key)) - for title, path, block_key in cls._build_problem_list(course_blocks, usage_key): - # Chapter and sequential blocks are filtered out since they include state - # which isn't useful for this report. - if block_key.block_type in ('sequential', 'chapter'): - continue - - if filter_types is not None and block_key.block_type not in filter_types: - continue - - block = store.get_item(block_key) - generated_report_data = defaultdict(list) - - # Blocks can implement the generate_report_data method to provide their own - # human-readable formatting for user state. - if hasattr(block, 'generate_report_data'): - try: - user_state_iterator = user_state_client.iter_all_for_block(block_key) - for username, state in block.generate_report_data(user_state_iterator, max_count): - generated_report_data[username].append(state) - except NotImplementedError: - pass - - responses = [] - - for response in list_problem_responses(course_key, block_key, max_count): - response['title'] = title - # A human-readable location for the current block - response['location'] = ' > '.join(base_path + path) - # A machine-friendly location for the current block - response['block_key'] = str(block_key) - # A block that has a single state per user can contain multiple responses - # within the same state. - user_states = generated_report_data.get(response['username'], []) - if user_states: - # For each response in the block, copy over the basic data like the - # title, location, block_key and state, and add in the responses - for user_state in user_states: - user_response = response.copy() - user_response.update(user_state) - student_data_keys = student_data_keys.union(user_state.keys()) - responses.append(user_response) - else: - responses.append(response) - - student_data += responses - - if max_count is not None: - max_count -= len(responses) - if max_count <= 0: - break - - # Keep the keys in a useful order, starting with username, title and location, - # then the columns returned by the xblock report generator in sorted order and - # finally end with the more machine friendly block_key and state. - student_data_keys_list = ( - ['username', 'title', 'location'] + - sorted(student_data_keys) + - ['block_key', 'state'] - ) + batch_size = 10000 + batch_no = 1 + + while True: + student_data = [] + with store.bulk_operations(course_key): + for usage_key in usage_keys: + if max_count is not None and max_count <= 0: + break + course_blocks = get_course_blocks(user, usage_key) + base_path = cls._build_block_base_path(store.get_item(usage_key)) + for title, path, block_key in cls._build_problem_list(course_blocks, usage_key): + # Chapter and sequential blocks are filtered out since they include state + # which isn't useful for this report. + if block_key.block_type in ('sequential', 'chapter'): + continue + + if filter_types is not None and block_key.block_type not in filter_types: + continue + + block = store.get_item(block_key) + generated_report_data = defaultdict(list) + + # Blocks can implement the generate_report_data method to provide their own + # human-readable formatting for user state. + if hasattr(block, 'generate_report_data'): + try: + user_state_iterator = user_state_client.iter_all_for_block(block_key) + for username, state in block.generate_report_data(user_state_iterator, max_count): + generated_report_data[username].append(state) + except NotImplementedError: + pass + + responses = [] + + for response in list_problem_responses(course_key, block_key, max_count, batch_no, batch_size): + response['title'] = title + # A human-readable location for the current block + response['location'] = ' > '.join(base_path + path) + # A machine-friendly location for the current block + response['block_key'] = str(block_key) + # A block that has a single state per user can contain multiple responses + # within the same state. + user_states = generated_report_data.get(response['username'], []) + if user_states: + # For each response in the block, copy over the basic data like the + # title, location, block_key and state, and add in the responses + for user_state in user_states: + user_response = response.copy() + user_response.update(user_state) + student_data_keys = student_data_keys.union(user_state.keys()) + responses.append(user_response) + else: + responses.append(response) + + student_data += responses + + if max_count is not None: + max_count -= len(responses) + if max_count <= 0: + break + + if not student_data: + break + + # Keep the keys in a useful order, starting with username, title and location, + # then the columns returned by the xblock report generator in sorted order and + # finally end with the more machine friendly block_key and state. + student_data_keys_list = ( + ['username', 'title', 'location'] + + sorted(student_data_keys) + + ['block_key', 'state'] + ) - return student_data, student_data_keys_list + yield student_data, student_data_keys_list, batch_no + batch_no += 1 @classmethod def generate(cls, _xmodule_instance_args, _entry_id, course_id, task_input, action_name): @@ -985,34 +992,42 @@ def generate(cls, _xmodule_instance_args, _entry_id, course_id, task_input, acti if problem_types_filter: filter_types = problem_types_filter.split(',') + output_buffer = None + # Compute result table and format it - student_data, student_data_keys = cls._build_student_data( + for student_data, student_data_keys, batch_no in cls._build_student_data( user_id=task_input.get('user_id'), course_key=course_id, usage_key_str_list=problem_locations.split(','), filter_types=filter_types, - ) + ): - for data in student_data: - for key in student_data_keys: - data.setdefault(key, '') + for data in student_data: + for key in student_data_keys: + data.setdefault(key, '') - header, rows = format_dictlist(student_data, student_data_keys) + header, rows = format_dictlist(student_data, student_data_keys) - task_progress.attempted = task_progress.succeeded = len(rows) - task_progress.skipped = task_progress.total - task_progress.attempted + task_progress.attempted = task_progress.succeeded = len(rows) - rows.insert(0, header) + if batch_no == 1: + rows.insert(0, header) - current_step = {'step': 'Uploading CSV'} - task_progress.update_task_state(extra_meta=current_step) + current_step = {'step': 'Uploading CSV'} + task_progress.update_task_state(extra_meta=current_step) + + # Perform the upload + # Limit problem locations string to 200 characters in case a large number of + # problem locations are selected. + problem_location = re.sub(r'[:/]', '_', problem_locations)[:200] + csv_name = 'student_state_from_{}'.format(problem_location) + + report_store, report_name = get_report_info(csv_name, course_id, start_date) + output_buffer = report_store.add_rows(rows, output_buffer) - # Perform the upload - # Limit problem locations string to 200 characters in case a large number of - # problem locations are selected. - problem_location = re.sub(r'[:/]', '_', problem_locations)[:200] - csv_name = 'student_state_from_{}'.format(problem_location) - report_name = upload_csv_to_report_store(rows, csv_name, course_id, start_date) + output_buffer.seek(0) + report_store.store(course_id, report_name, output_buffer) + tracker_emit(report_name) current_step = {'step': 'CSV uploaded', 'report_name': report_name} return task_progress.update_task_state(extra_meta=current_step) diff --git a/lms/djangoapps/instructor_task/tasks_helper/utils.py b/lms/djangoapps/instructor_task/tasks_helper/utils.py index b5552029adac..0a90a53f89e0 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/utils.py +++ b/lms/djangoapps/instructor_task/tasks_helper/utils.py @@ -19,6 +19,27 @@ UPDATE_STATUS_SKIPPED = 'skipped' +def get_report_info(csv_name, course_id, timestamp, config_name='GRADES_DOWNLOAD'): + """ + Returns ReportStore and Report Name. + + Arguments: + csv_name: Name of the resulting CSV + course_id: ID of the course + + Returns: + report_stroe: ReportStore - Instance of report store + report_name: string - Name of the generated report + """ + report_store = ReportStore.from_config(config_name) + report_name = u"{course_prefix}_{csv_name}_{timestamp_str}.csv".format( + course_prefix=course_filename_prefix_generator(course_id), + csv_name=csv_name, + timestamp_str=timestamp.strftime("%Y-%m-%d-%H%M") + ) + return report_store, report_name + + def upload_csv_to_report_store(rows, csv_name, course_id, timestamp, config_name='GRADES_DOWNLOAD'): """ Upload data as a CSV using ReportStore. @@ -36,12 +57,7 @@ def upload_csv_to_report_store(rows, csv_name, course_id, timestamp, config_name Returns: report_name: string - Name of the generated report """ - report_store = ReportStore.from_config(config_name) - report_name = u"{course_prefix}_{csv_name}_{timestamp_str}.csv".format( - course_prefix=course_filename_prefix_generator(course_id), - csv_name=csv_name, - timestamp_str=timestamp.strftime("%Y-%m-%d-%H%M") - ) + report_store, report_name = get_report_info(csv_name, course_id, timestamp, config_name) report_store.store_rows(course_id, report_name, rows) tracker_emit(csv_name) diff --git a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py index 3797d41f80c9..fe48aa4a7030 100644 --- a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py +++ b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py @@ -522,11 +522,13 @@ def test_build_student_data_limit(self): student = self.create_student('student{}'.format(ctr)) self.submit_student_answer(student.username, u'Problem1', ['Option 1']) - student_data, _ = ProblemResponses._build_student_data( + student_data = [] + for student_data_batch, _, _ in ProblemResponses._build_student_data( user_id=self.instructor.id, course_key=self.course.id, usage_key_str_list=[str(self.course.location)], - ) + ): + student_data += student_data_batch self.assertEqual(len(student_data), 4) @@ -542,11 +544,14 @@ def test_build_student_data_for_block_without_generate_report_data(self, mock_li problem = self.define_option_problem(u'Problem1') self.submit_student_answer(self.student.username, u'Problem1', ['Option 1']) with self._remove_capa_report_generator(): - student_data, _ = ProblemResponses._build_student_data( + student_data = [] + for student_data_batch, _, _ in ProblemResponses._build_student_data( user_id=self.instructor.id, course_key=self.course.id, usage_key_str_list=[str(problem.location)], - ) + ): + student_data += student_data_batch + self.assertEqual(len(student_data), 1) self.assertDictContainsSubset({ 'username': 'student', @@ -555,7 +560,7 @@ def test_build_student_data_for_block_without_generate_report_data(self, mock_li 'title': 'Problem1', }, student_data[0]) self.assertIn('state', student_data[0]) - mock_list_problem_responses.assert_called_with(self.course.id, ANY, ANY) + mock_list_problem_responses.assert_called_with(self.course.id, ANY, ANY, ANY, ANY) @patch('xmodule.capa_module.ProblemBlock.generate_report_data', create=True) def test_build_student_data_for_block_with_mock_generate_report_data(self, mock_generate_report_data): @@ -571,11 +576,14 @@ def test_build_student_data_for_block_with_mock_generate_report_data(self, mock_ ('student', state1), ('student', state2), ]) - student_data, _ = ProblemResponses._build_student_data( + student_data = [] + for student_data_batch, _, _ in ProblemResponses._build_student_data( user_id=self.instructor.id, course_key=self.course.id, usage_key_str_list=[str(self.course.location)], - ) + ): + student_data += student_data_batch + self.assertEqual(len(student_data), 2) self.assertDictContainsSubset({ 'username': 'student', @@ -602,11 +610,14 @@ def test_build_student_data_for_block_with_real_generate_report_data(self): """ self.define_option_problem(u'Problem1') self.submit_student_answer(self.student.username, u'Problem1', ['Option 1']) - student_data, _ = ProblemResponses._build_student_data( + student_data = [] + for student_data_batch, _, _ in ProblemResponses._build_student_data( user_id=self.instructor.id, course_key=self.course.id, usage_key_str_list=[str(self.course.location)], - ) + ): + student_data += student_data_batch + self.assertEqual(len(student_data), 1) self.assertDictContainsSubset({ 'username': 'student', @@ -633,13 +644,13 @@ def test_build_student_data_for_block_with_generate_report_data_not_implemented( """ problem = self.define_option_problem(u'Problem1') mock_generate_report_data.side_effect = NotImplementedError - ProblemResponses._build_student_data( + list(ProblemResponses._build_student_data( user_id=self.instructor.id, course_key=self.course.id, usage_key_str_list=[str(problem.location)], - ) - mock_generate_report_data.assert_called_with(ANY, ANY) - mock_list_problem_responses.assert_called_with(self.course.id, ANY, ANY) + )) + mock_generate_report_data.assert_called() + mock_list_problem_responses.assert_called() def test_success(self): task_input = { @@ -649,14 +660,15 @@ def test_success(self): with patch('lms.djangoapps.instructor_task.tasks_helper.runner._get_current_task'): with patch('lms.djangoapps.instructor_task.tasks_helper.grades' '.ProblemResponses._build_student_data') as mock_build_student_data: - mock_build_student_data.return_value = ( + mock_build_student_data.return_value = iter([( [ {'username': 'user0', 'state': u'state0'}, {'username': 'user1', 'state': u'state1'}, {'username': 'user2', 'state': u'state2'}, ], - ['username', 'state'] - ) + ['username', 'state'], + 1 + )]) result = ProblemResponses.generate( None, None, self.course.id, task_input, 'calculated' ) From 2cf357afc2f82757acb9851fa7034b6f8b4ef58b Mon Sep 17 00:00:00 2001 From: Mudassir Hafeez Date: Wed, 17 Mar 2021 15:00:16 +0500 Subject: [PATCH 08/16] MCKIN-30999 api-integration version bump (#2132) --- requirements/edx/custom.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/edx/custom.txt b/requirements/edx/custom.txt index 088eed0db603..a85a4c9751c1 100644 --- a/requirements/edx/custom.txt +++ b/requirements/edx/custom.txt @@ -27,7 +27,7 @@ git+https://github.com/edx-solutions/discussion-edx-platform-extensions.git@3.0. git+https://github.com/edx-solutions/organizations-edx-platform-extensions.git@v3.0.0#egg=organizations-edx-platform-extensions==3.0.0 git+https://github.com/edx-solutions/course-edx-platform-extensions.git@v4.0.0#egg=course-edx-platform-extensions==4.0.0 git+https://github.com/edx-solutions/projects-edx-platform-extensions.git@v4.0.0#egg=projects-edx-platform-extensions==4.0.0 -git+https://github.com/edx-solutions/api-integration.git@v5.0.2#egg=api-integration==5.0.2 +git+https://github.com/edx-solutions/api-integration.git@v5.0.3#egg=api-integration==5.0.3 git+https://github.com/mckinseyacademy/openedx-user-manager-api@v2.0.1#egg=openedx-user-manager-api==2.0.1 openedx-completion-aggregator==3.0.3 From f60046f783d33b163acdd32bb0c37a6a9ce1f113 Mon Sep 17 00:00:00 2001 From: ahmed-zubair12 <74174850+ahmed-zubair12@users.noreply.github.com> Date: Mon, 22 Mar 2021 19:19:13 +0500 Subject: [PATCH 09/16] MCKIN-24991 - Improving performance by not generating redundant data (#2135) --- lms/djangoapps/courseware/user_state_client.py | 9 ++++++--- lms/djangoapps/instructor_task/tasks_helper/grades.py | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lms/djangoapps/courseware/user_state_client.py b/lms/djangoapps/courseware/user_state_client.py index d842e18a31db..467f4e3d493e 100644 --- a/lms/djangoapps/courseware/user_state_client.py +++ b/lms/djangoapps/courseware/user_state_client.py @@ -395,11 +395,11 @@ def get_history(self, username, block_key, scope=Scope.user_state): yield XBlockUserState(username, block_key, state, history_entry.created, scope) - def iter_all_for_block(self, block_key, scope=Scope.user_state): + def iter_all_for_block(self, block_key, batch_no=None, batch_size=None, scope=Scope.user_state): """ Return an iterator over the data stored in the block (e.g. a problem block). - You get no ordering guarantees.If you're using this method, you should be running in an + Results are ordered by student id. If you're using this method, you should be running in an async task. Arguments: @@ -413,7 +413,10 @@ def iter_all_for_block(self, block_key, scope=Scope.user_state): if scope != Scope.user_state: raise ValueError("Only Scope.user_state is supported") - results = StudentModule.objects.order_by('id').filter(module_state_key=block_key) + results = StudentModule.objects.filter(module_state_key=block_key).order_by('student') + if batch_no: + results = results[(batch_no - 1) * batch_size:batch_no * batch_size] + p = Paginator(results, settings.USER_STATE_BATCH_SIZE) for page_number in p.page_range: diff --git a/lms/djangoapps/instructor_task/tasks_helper/grades.py b/lms/djangoapps/instructor_task/tasks_helper/grades.py index 4df2d4b7cd81..49a787f5b628 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/grades.py +++ b/lms/djangoapps/instructor_task/tasks_helper/grades.py @@ -923,7 +923,7 @@ def _build_student_data( # human-readable formatting for user state. if hasattr(block, 'generate_report_data'): try: - user_state_iterator = user_state_client.iter_all_for_block(block_key) + user_state_iterator = user_state_client.iter_all_for_block(block_key, batch_no, batch_size) for username, state in block.generate_report_data(user_state_iterator, max_count): generated_report_data[username].append(state) except NotImplementedError: @@ -993,6 +993,7 @@ def generate(cls, _xmodule_instance_args, _entry_id, course_id, task_input, acti filter_types = problem_types_filter.split(',') output_buffer = None + total_rows = 0 # Compute result table and format it for student_data, student_data_keys, batch_no in cls._build_student_data( @@ -1008,7 +1009,8 @@ def generate(cls, _xmodule_instance_args, _entry_id, course_id, task_input, acti header, rows = format_dictlist(student_data, student_data_keys) - task_progress.attempted = task_progress.succeeded = len(rows) + total_rows += len(rows) + task_progress.attempted = task_progress.succeeded = total_rows if batch_no == 1: rows.insert(0, header) From a5b394c1d4a315700221ff37bd4ae25c81b6c0d5 Mon Sep 17 00:00:00 2001 From: Mudassir Hafeez Date: Mon, 22 Feb 2021 13:36:22 +0500 Subject: [PATCH 10/16] MCKIN-29542 xblock eoc journal vb #2115 --- requirements/edx/custom.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/edx/custom.txt b/requirements/edx/custom.txt index a85a4c9751c1..038f383e75bb 100644 --- a/requirements/edx/custom.txt +++ b/requirements/edx/custom.txt @@ -13,7 +13,7 @@ git+https://github.com/edx-solutions/xblock-group-project.git@1.0.0#egg=xblock-g -e git+https://github.com/open-craft/problem-builder.git@v4.1.9#egg=xblock-problem-builder==4.1.9 #-e git+https://github.com/OfficeDev/xblock-officemix.git@86238f5968a08db005717dbddc1.9.6346808f1ed3716#egg=xblock-officemix -e git+https://github.com/open-craft/xblock-chat.git@v0.3.0#egg=chat-xblock==0.3.0 --e git+https://github.com/open-craft/xblock-eoc-journal.git@v0.9.4#egg=xblock-eoc-journal==0.9.4 +-e git+https://github.com/open-craft/xblock-eoc-journal.git@v0.10.0#egg=xblock-eoc-journal==0.10.0 -e git+https://github.com/mckinseyacademy/xblock-scorm.git@v3.2.2#egg=xblock-scorm==3.2.2 -e git+https://github.com/mckinseyacademy/xblock-diagnosticfeedback.git@v0.4.1#egg=xblock-diagnostic-feedback==0.4.1 From f289c245d54d98d71c9490c27216eb0132260683 Mon Sep 17 00:00:00 2001 From: ahmed-zubair12 <74174850+ahmed-zubair12@users.noreply.github.com> Date: Wed, 17 Mar 2021 21:14:39 +0500 Subject: [PATCH 11/16] MCKIN-31645 - Version bump for adventure and api-integration (#2133) --- requirements/edx/custom.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/edx/custom.txt b/requirements/edx/custom.txt index 038f383e75bb..36a19b775eaa 100644 --- a/requirements/edx/custom.txt +++ b/requirements/edx/custom.txt @@ -8,7 +8,7 @@ -e git+https://github.com/edx-solutions/xblock-ooyala.git@v5.0.0#egg=xblock-ooyala==5.0.0 git+https://github.com/edx-solutions/xblock-group-project.git@1.0.0#egg=xblock-group-project==1.0.0 --e git+https://github.com/edx-solutions/xblock-adventure.git@1.0.0#egg=xblock-adventure==1.0.0 +-e git+https://github.com/edx-solutions/xblock-adventure.git@1.0.1#egg=xblock-adventure==1.0.1 -e git+https://github.com/open-craft/xblock-poll.git@v1.10.1#egg=xblock-poll==1.10.1 -e git+https://github.com/open-craft/problem-builder.git@v4.1.9#egg=xblock-problem-builder==4.1.9 #-e git+https://github.com/OfficeDev/xblock-officemix.git@86238f5968a08db005717dbddc1.9.6346808f1ed3716#egg=xblock-officemix @@ -27,7 +27,7 @@ git+https://github.com/edx-solutions/discussion-edx-platform-extensions.git@3.0. git+https://github.com/edx-solutions/organizations-edx-platform-extensions.git@v3.0.0#egg=organizations-edx-platform-extensions==3.0.0 git+https://github.com/edx-solutions/course-edx-platform-extensions.git@v4.0.0#egg=course-edx-platform-extensions==4.0.0 git+https://github.com/edx-solutions/projects-edx-platform-extensions.git@v4.0.0#egg=projects-edx-platform-extensions==4.0.0 -git+https://github.com/edx-solutions/api-integration.git@v5.0.3#egg=api-integration==5.0.3 +git+https://github.com/edx-solutions/api-integration.git@v5.0.4#egg=api-integration==5.0.4 git+https://github.com/mckinseyacademy/openedx-user-manager-api@v2.0.1#egg=openedx-user-manager-api==2.0.1 openedx-completion-aggregator==3.0.3 From 70c3d3332fa4ef02b5994660da3094c3cabb89ec Mon Sep 17 00:00:00 2001 From: Sohaib Aslam Date: Tue, 30 Mar 2021 18:31:49 +0500 Subject: [PATCH 12/16] Resolved pylint violations for commits in release v1.62.0 --- cms/djangoapps/course_creators/admin.py | 3 ++- cms/djangoapps/course_creators/tests/test_admin.py | 6 ++++-- lms/djangoapps/instructor_task/tasks_helper/grades.py | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cms/djangoapps/course_creators/admin.py b/cms/djangoapps/course_creators/admin.py index aa3e8827d0be..8f5945617f0c 100644 --- a/cms/djangoapps/course_creators/admin.py +++ b/cms/djangoapps/course_creators/admin.py @@ -11,7 +11,8 @@ from django.core.mail import send_mail from django.dispatch import receiver -from cms.djangoapps.course_creators.models import CourseCreator, send_admin_notification, send_user_notification, update_creator_state +from cms.djangoapps.course_creators.models import (CourseCreator, send_admin_notification, + send_user_notification, update_creator_state) from cms.djangoapps.course_creators.views import update_course_creator_group from edxmako.shortcuts import render_to_string diff --git a/cms/djangoapps/course_creators/tests/test_admin.py b/cms/djangoapps/course_creators/tests/test_admin.py index 042e793af83a..c02a8f1e0812 100644 --- a/cms/djangoapps/course_creators/tests/test_admin.py +++ b/cms/djangoapps/course_creators/tests/test_admin.py @@ -48,7 +48,8 @@ def setUp(self): "STUDIO_REQUEST_EMAIL": self.studio_request_email } - @mock.patch('cms.djangoapps.course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) + @mock.patch('cms.djangoapps.course_creators.admin.render_to_string', + mock.Mock(side_effect=mock_render_to_string, autospec=True)) @mock.patch('django.contrib.auth.models.User.email_user') def test_change_status(self, email_user): """ @@ -92,7 +93,8 @@ def change_state_and_verify_email(state, is_creator): change_state_and_verify_email(CourseCreator.DENIED, False) - @mock.patch('cms.djangoapps.course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) + @mock.patch('cms.djangoapps.course_creators.admin.render_to_string', + mock.Mock(side_effect=mock_render_to_string, autospec=True)) def test_mail_admin_on_pending(self): """ Tests that the admin account is notified when a user is in the 'pending' state. diff --git a/lms/djangoapps/instructor_task/tasks_helper/grades.py b/lms/djangoapps/instructor_task/tasks_helper/grades.py index 49a787f5b628..5f4da33c58f6 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/grades.py +++ b/lms/djangoapps/instructor_task/tasks_helper/grades.py @@ -923,7 +923,8 @@ def _build_student_data( # human-readable formatting for user state. if hasattr(block, 'generate_report_data'): try: - user_state_iterator = user_state_client.iter_all_for_block(block_key, batch_no, batch_size) + user_state_iterator = user_state_client.iter_all_for_block(block_key, batch_no, + batch_size) for username, state in block.generate_report_data(user_state_iterator, max_count): generated_report_data[username].append(state) except NotImplementedError: From 228d3fe4bc7c521d7ef3f3a011926b44f3fe2ca4 Mon Sep 17 00:00:00 2001 From: Sohaib Aslam Date: Wed, 31 Mar 2021 13:01:27 +0500 Subject: [PATCH 13/16] Specidied newrelic version to manage python3.5 support. --- requirements/edx/base.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/edx/base.in b/requirements/edx/base.in index c3df1e4e9a27..4cd46ad10a12 100644 --- a/requirements/edx/base.in +++ b/requirements/edx/base.in @@ -105,7 +105,7 @@ mako==1.0.2 # Primary template language used for server- Markdown # Convert text markup to HTML; used in capa problems, forums, and course wikis mongoengine==0.10.0 # Object-document mapper for MongoDB, used in the LMS dashboard mysqlclient # Driver for the default production relational database -newrelic # New Relic agent for performance monitoring +newrelic==5.12.1.141 # New Relic agent for performance monitoring nodeenv # Utility for managing Node.js environments; we use this for deployments and testing oauthlib # OAuth specification support for authenticating via LTI or other Open edX services openedx-calc # Library supporting mathematical calculations for Open edX From 474f9206d0210e2413e42860422cd06783b7543e Mon Sep 17 00:00:00 2001 From: Sohaib Aslam Date: Wed, 31 Mar 2021 15:24:51 +0500 Subject: [PATCH 14/16] Installed py2neo via github and bumped down version for xblock-eoc-journal --- requirements/edx/base.in | 2 +- requirements/edx/base.txt | 2 +- requirements/edx/custom.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements/edx/base.in b/requirements/edx/base.in index 4cd46ad10a12..a9134ed189f9 100644 --- a/requirements/edx/base.in +++ b/requirements/edx/base.in @@ -113,7 +113,7 @@ pa11ycrawler # Python crawler (using Scrapy) that uses Pa pdfminer.six # Used in shoppingcart for extracting/parsing pdf text piexif # Exif image metadata manipulation, used in the profile_images app Pillow # Image manipulation library; used for course assets, profile images, invoice PDFs, etc. -py2neo<4.0.0 # Used to communicate with Neo4j, which is used internally for modulestore inspection + PyContracts pycountry pycryptodomex diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index c37141615ce2..c2e657206f70 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -183,7 +183,7 @@ pillow==7.1.2 # via -r requirements/edx/base.in, edx-enterprise, edx pkgconfig==1.5.1 # via xmlsec polib==1.1.0 # via edx-i18n-tools psutil==1.2.1 # via -r requirements/edx/paver.txt, edx-django-utils -py2neo==3.1.2 # via -r requirements/edx/base.in +-e git+https://github.com/technige/py2neo.git@py2neo-3.1.2#egg=py2neo==3.1.2 # via -r requirements/edx/github.in pycontracts==1.8.12 # via -r requirements/edx/base.in, edx-user-state-client pycountry==19.8.18 # via -r requirements/edx/base.in pycparser==2.20 # via -r requirements/edx/../edx-sandbox/shared.txt, cffi diff --git a/requirements/edx/custom.txt b/requirements/edx/custom.txt index 36a19b775eaa..b83803058ce6 100644 --- a/requirements/edx/custom.txt +++ b/requirements/edx/custom.txt @@ -13,7 +13,7 @@ git+https://github.com/edx-solutions/xblock-group-project.git@1.0.0#egg=xblock-g -e git+https://github.com/open-craft/problem-builder.git@v4.1.9#egg=xblock-problem-builder==4.1.9 #-e git+https://github.com/OfficeDev/xblock-officemix.git@86238f5968a08db005717dbddc1.9.6346808f1ed3716#egg=xblock-officemix -e git+https://github.com/open-craft/xblock-chat.git@v0.3.0#egg=chat-xblock==0.3.0 --e git+https://github.com/open-craft/xblock-eoc-journal.git@v0.10.0#egg=xblock-eoc-journal==0.10.0 +-e git+https://github.com/open-craft/xblock-eoc-journal.git@v0.9.4#egg=xblock-eoc-journal==0.9.4 -e git+https://github.com/mckinseyacademy/xblock-scorm.git@v3.2.2#egg=xblock-scorm==3.2.2 -e git+https://github.com/mckinseyacademy/xblock-diagnosticfeedback.git@v0.4.1#egg=xblock-diagnostic-feedback==0.4.1 diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 9b728ccac92f..275396b0b40a 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -219,7 +219,7 @@ pkgconfig==1.5.1 # via -r requirements/edx/testing.txt, xmlsec pluggy==0.13.1 # via -r requirements/edx/testing.txt, diff-cover, pytest, tox polib==1.1.0 # via -r requirements/edx/testing.txt, edx-i18n-tools psutil==1.2.1 # via -r requirements/edx/testing.txt, edx-django-utils -py2neo==3.1.2 # via -r requirements/edx/testing.txt +-e git+https://github.com/technige/py2neo.git@py2neo-3.1.2#egg=py2neo==3.1.2 # via -r requirements/edx/github.in py==1.8.1 # via -r requirements/edx/testing.txt, pytest, tox pycodestyle==2.6.0 # via -r requirements/edx/testing.txt, flake8 pycontracts==1.8.12 # via -r requirements/edx/testing.txt, edx-user-state-client diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index be4a4f82aa79..e4ee591df038 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -210,7 +210,7 @@ pkgconfig==1.5.1 # via -r requirements/edx/base.txt, xmlsec pluggy==0.13.1 # via -r requirements/edx/coverage.txt, diff-cover, pytest, tox polib==1.1.0 # via -r requirements/edx/base.txt, -r requirements/edx/testing.in, edx-i18n-tools psutil==1.2.1 # via -r requirements/edx/base.txt, edx-django-utils -py2neo==3.1.2 # via -r requirements/edx/base.txt +-e git+https://github.com/technige/py2neo.git@py2neo-3.1.2#egg=py2neo==3.1.2 # via -r requirements/edx/github.in py==1.8.1 # via pytest, tox pycodestyle==2.6.0 # via -r requirements/edx/testing.in, flake8 pycontracts==1.8.12 # via -r requirements/edx/base.txt, edx-user-state-client From 4d1512f7356caba9febdb6f710dcf750cd1efad6 Mon Sep 17 00:00:00 2001 From: Sohaib Aslam Date: Wed, 31 Mar 2021 17:39:00 +0500 Subject: [PATCH 15/16] Installed py2neo from github --- requirements/edx/base.in | 2 +- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/testing.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements/edx/base.in b/requirements/edx/base.in index c3df1e4e9a27..91ec2db45b4c 100644 --- a/requirements/edx/base.in +++ b/requirements/edx/base.in @@ -113,7 +113,7 @@ pa11ycrawler # Python crawler (using Scrapy) that uses Pa pdfminer.six # Used in shoppingcart for extracting/parsing pdf text piexif # Exif image metadata manipulation, used in the profile_images app Pillow # Image manipulation library; used for course assets, profile images, invoice PDFs, etc. -py2neo<4.0.0 # Used to communicate with Neo4j, which is used internally for modulestore inspection + PyContracts pycountry pycryptodomex diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index c37141615ce2..f4396007226b 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -183,7 +183,7 @@ pillow==7.1.2 # via -r requirements/edx/base.in, edx-enterprise, edx pkgconfig==1.5.1 # via xmlsec polib==1.1.0 # via edx-i18n-tools psutil==1.2.1 # via -r requirements/edx/paver.txt, edx-django-utils -py2neo==3.1.2 # via -r requirements/edx/base.in +-e git+https://github.com/technige/py2neo.git@py2neo-3.1.2#egg=py2neo==3.1.2 # via -r requirements/edx/github.in pycontracts==1.8.12 # via -r requirements/edx/base.in, edx-user-state-client pycountry==19.8.18 # via -r requirements/edx/base.in pycparser==2.20 # via -r requirements/edx/../edx-sandbox/shared.txt, cffi diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 9b728ccac92f..8321a754efbc 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -219,7 +219,7 @@ pkgconfig==1.5.1 # via -r requirements/edx/testing.txt, xmlsec pluggy==0.13.1 # via -r requirements/edx/testing.txt, diff-cover, pytest, tox polib==1.1.0 # via -r requirements/edx/testing.txt, edx-i18n-tools psutil==1.2.1 # via -r requirements/edx/testing.txt, edx-django-utils -py2neo==3.1.2 # via -r requirements/edx/testing.txt +-e git+https://github.com/technige/py2neo.git@py2neo-3.1.2#egg=py2neo==3.1.2 py==1.8.1 # via -r requirements/edx/testing.txt, pytest, tox pycodestyle==2.6.0 # via -r requirements/edx/testing.txt, flake8 pycontracts==1.8.12 # via -r requirements/edx/testing.txt, edx-user-state-client diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index be4a4f82aa79..2077f8e9cafe 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -210,7 +210,7 @@ pkgconfig==1.5.1 # via -r requirements/edx/base.txt, xmlsec pluggy==0.13.1 # via -r requirements/edx/coverage.txt, diff-cover, pytest, tox polib==1.1.0 # via -r requirements/edx/base.txt, -r requirements/edx/testing.in, edx-i18n-tools psutil==1.2.1 # via -r requirements/edx/base.txt, edx-django-utils -py2neo==3.1.2 # via -r requirements/edx/base.txt +-e git+https://github.com/technige/py2neo.git@py2neo-3.1.2#egg=py2neo==3.1.2 py==1.8.1 # via pytest, tox pycodestyle==2.6.0 # via -r requirements/edx/testing.in, flake8 pycontracts==1.8.12 # via -r requirements/edx/base.txt, edx-user-state-client From a81192e44e4334c9b2d18fe8b2cc7c554c0d36fb Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 1 Apr 2021 17:23:27 +0500 Subject: [PATCH 16/16] MCKIN-32454: bump xblock eoc journal version to 0.10.1 (#2138) --- requirements/edx/custom.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/edx/custom.txt b/requirements/edx/custom.txt index b83803058ce6..ac2ad0d25025 100644 --- a/requirements/edx/custom.txt +++ b/requirements/edx/custom.txt @@ -13,7 +13,7 @@ git+https://github.com/edx-solutions/xblock-group-project.git@1.0.0#egg=xblock-g -e git+https://github.com/open-craft/problem-builder.git@v4.1.9#egg=xblock-problem-builder==4.1.9 #-e git+https://github.com/OfficeDev/xblock-officemix.git@86238f5968a08db005717dbddc1.9.6346808f1ed3716#egg=xblock-officemix -e git+https://github.com/open-craft/xblock-chat.git@v0.3.0#egg=chat-xblock==0.3.0 --e git+https://github.com/open-craft/xblock-eoc-journal.git@v0.9.4#egg=xblock-eoc-journal==0.9.4 +-e git+https://github.com/open-craft/xblock-eoc-journal.git@v0.10.1#egg=xblock-eoc-journal==0.10.1 -e git+https://github.com/mckinseyacademy/xblock-scorm.git@v3.2.2#egg=xblock-scorm==3.2.2 -e git+https://github.com/mckinseyacademy/xblock-diagnosticfeedback.git@v0.4.1#egg=xblock-diagnostic-feedback==0.4.1