diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py index dde19152d7..514b89d419 100644 --- a/cms/djangoapps/contentstore/views/component.py +++ b/cms/djangoapps/contentstore/views/component.py @@ -41,7 +41,7 @@ __all__ = [ log = logging.getLogger(__name__) # NOTE: This list is disjoint from ADVANCED_COMPONENT_TYPES -COMPONENT_TYPES = ['discussion', 'html', 'problem', 'video'] +COMPONENT_TYPES = ['discussion', 'html', 'openassessment', 'problem', 'video'] ADVANCED_COMPONENT_TYPES = sorted(set(name for name, class_ in XBlock.load_classes()) - set(COMPONENT_TYPES)) @@ -260,7 +260,8 @@ def get_component_templates(courselike, library=False): 'discussion': _("Discussion"), 'html': _("HTML"), 'problem': _("Problem"), - 'video': _("Video") + 'video': _("Video"), + 'openassessment': _("Open Response") } component_templates = [] @@ -269,9 +270,11 @@ def get_component_templates(courselike, library=False): # by the components in the order listed in COMPONENT_TYPES. component_types = COMPONENT_TYPES[:] - # Libraries do not support discussions + # Libraries do not support discussions and openassessment + component_not_supported_by_library = ['discussion', 'openassessment'] if library: - component_types = [component for component in component_types if component != 'discussion'] + component_types = [component for component in component_types + if component not in set(component_not_supported_by_library)] component_types = _filter_disabled_blocks(component_types) @@ -288,9 +291,14 @@ def get_component_templates(courselike, library=False): # add the default template with localized display name # TODO: Once mixins are defined per-application, rather than per-runtime, # this should use a cms mixed-in class. (cpennington) + template_id = None display_name = xblock_type_display_name(category, _('Blank')) # this is the Blank Advanced problem + # The first template that is given should be Blank Assessment Template + if category == 'openassessment': + display_name = _("Blank Open Response Assessment") + template_id = "blank-assessment" templates_for_category.append( - create_template_dict(display_name, category, support_level_without_template, None, 'advanced') + create_template_dict(display_name, category, support_level_without_template, template_id, 'advanced') ) categories.add(category) diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py index ddf1c8b64d..f07e3dd9da 100644 --- a/cms/djangoapps/contentstore/views/tests/test_item.py +++ b/cms/djangoapps/contentstore/views/tests/test_item.py @@ -2256,9 +2256,11 @@ class TestComponentTemplates(CourseTestCase): XBlockStudioConfiguration.objects.create(name='discussion', enabled=True, support_level="ps") XBlockStudioConfiguration.objects.create(name='problem', enabled=True, support_level="us") XBlockStudioConfiguration.objects.create(name='video', enabled=True, support_level="us") - # XBlock masquerading as a problem + # ORA Block has it's own category. XBlockStudioConfiguration.objects.create(name='openassessment', enabled=True, support_level="us") + # XBlock masquerading as a problem XBlockStudioConfiguration.objects.create(name='drag-and-drop-v2', enabled=True, support_level="fs") + XBlockStudioConfiguration.objects.create(name='staffgradedxblock', enabled=True, support_level="us") self.templates = get_component_templates(self.course) @@ -2266,9 +2268,22 @@ class TestComponentTemplates(CourseTestCase): """ Returns the templates for the specified type, or None if none is found. """ - template_dict = next((template for template in self.templates if template.get('type') == template_type), None) + template_dict = self._get_template_dict_of_type(template_type) return template_dict.get('templates') if template_dict else None + def get_display_name_of_type(self, template_type): + """ + Returns the display name for the specified type, or None if none found. + """ + template_dict = self._get_template_dict_of_type(template_type) + return template_dict.get('display_name') if template_dict else None + + def _get_template_dict_of_type(self, template_type): + """ + Returns a dictionary of values for a category type. + """ + return next((template for template in self.templates if template.get('type') == template_type), None) + def get_template(self, templates, display_name): """ Returns the template which has the specified display name. @@ -2281,6 +2296,10 @@ class TestComponentTemplates(CourseTestCase): """ self._verify_basic_component("discussion", "Discussion") self._verify_basic_component("video", "Video") + self._verify_basic_component("openassessment", "Blank Open Response Assessment", True, 6) + self._verify_basic_component_display_name("discussion", "Discussion") + self._verify_basic_component_display_name("video", "Video") + self._verify_basic_component_display_name("openassessment", "Open Response") self.assertGreater(len(self.get_templates_of_type('html')), 0) self.assertGreater(len(self.get_templates_of_type('problem')), 0) self.assertIsNone(self.get_templates_of_type('advanced')) @@ -2338,13 +2357,13 @@ class TestComponentTemplates(CourseTestCase): # Verify that non-advanced components are not added twice self.course.advanced_modules.append('video') - self.course.advanced_modules.append('openassessment') + self.course.advanced_modules.append('drag-and-drop-v2') self.templates = get_component_templates(self.course) advanced_templates = self.get_templates_of_type('advanced') self.assertEqual(len(advanced_templates), 1) only_template = advanced_templates[0] self.assertNotEqual(only_template.get('category'), 'video') - self.assertNotEqual(only_template.get('category'), 'openassessment') + self.assertNotEqual(only_template.get('category'), 'drag-and-drop-v2') # Now fully disable word_cloud through XBlockConfiguration XBlockConfiguration.objects.create(name='word_cloud', enabled=False) @@ -2415,12 +2434,14 @@ class TestComponentTemplates(CourseTestCase): problem_templates = self.get_templates_of_type('problem') return self.get_template(problem_templates, label) - def verify_openassessment_present(support_level): - """ Helper method to verify that openassessment template is present """ - openassessment = get_xblock_problem('Open Response Assessment') - self.assertIsNotNone(openassessment) - self.assertEqual(openassessment.get('category'), 'openassessment') - self.assertEqual(openassessment.get('support_level'), support_level) + def verify_staffgradedxblock_present(support_level): + """ + Helper method to verify that staffgradedxblock template is present + """ + sgp = get_xblock_problem('Staff Graded Points') + self.assertIsNotNone(sgp) + self.assertEqual(sgp.get('category'), 'staffgradedxblock') + self.assertEqual(sgp.get('support_level'), support_level) def verify_dndv2_present(support_level): """ @@ -2431,24 +2452,24 @@ class TestComponentTemplates(CourseTestCase): self.assertEqual(dndv2.get('category'), 'drag-and-drop-v2') self.assertEqual(dndv2.get('support_level'), support_level) - verify_openassessment_present(True) verify_dndv2_present(True) + verify_staffgradedxblock_present(True) - # Now enable XBlockStudioConfigurationFlag. The openassessment block is marked + # Now enable XBlockStudioConfigurationFlag. The staffgradedxblock block is marked # unsupported, so will no longer show up, but DnDv2 will continue to appear. XBlockStudioConfigurationFlag.objects.create(enabled=True) - self.assertIsNone(get_xblock_problem('Peer Assessment')) + self.assertIsNone(get_xblock_problem('Staff Graded Points')) self.assertIsNotNone(get_xblock_problem('Drag and Drop')) # Now allow unsupported components. self.course.allow_unsupported_xblocks = True - verify_openassessment_present('us') + verify_staffgradedxblock_present('us') verify_dndv2_present('fs') # Now disable the blocks completely through XBlockConfiguration - XBlockConfiguration.objects.create(name='openassessment', enabled=False) + XBlockConfiguration.objects.create(name='staffgradedxblock', enabled=False) XBlockConfiguration.objects.create(name='drag-and-drop-v2', enabled=False) - self.assertIsNone(get_xblock_problem('Peer Assessment')) + self.assertIsNone(get_xblock_problem('Staff Graded Points')) self.assertIsNone(get_xblock_problem('Drag and Drop')) def _verify_advanced_xblocks(self, expected_xblocks, expected_support_levels): @@ -2464,15 +2485,22 @@ class TestComponentTemplates(CourseTestCase): template_support_levels = [template['support_level'] for template in templates[0]['templates']] self.assertEqual(template_support_levels, expected_support_levels) - def _verify_basic_component(self, component_type, display_name, support_level=True): + def _verify_basic_component(self, component_type, display_name, support_level=True, no_of_templates=1): """ Verify the display name and support level of basic components (that have no boilerplates). """ templates = self.get_templates_of_type(component_type) - self.assertEqual(1, len(templates)) + self.assertEqual(no_of_templates, len(templates)) self.assertEqual(display_name, templates[0]['display_name']) self.assertEqual(support_level, templates[0]['support_level']) + def _verify_basic_component_display_name(self, component_type, display_name): + """ + Verify the display name of basic components. + """ + component_display_name = self.get_display_name_of_type(component_type) + self.assertEqual(display_name, component_display_name) + @ddt.ddt class TestXBlockInfo(ItemTest): diff --git a/cms/djangoapps/contentstore/views/tests/test_library.py b/cms/djangoapps/contentstore/views/tests/test_library.py index 3f3dfd5768..c7ea9cbfdb 100644 --- a/cms/djangoapps/contentstore/views/tests/test_library.py +++ b/cms/djangoapps/contentstore/views/tests/test_library.py @@ -337,6 +337,7 @@ class UnitTestLibraries(CourseTestCase): self.assertIn('problem', templates) self.assertNotIn('discussion', templates) self.assertNotIn('advanced', templates) + self.assertNotIn('openassessment', templates) def test_advanced_problem_types(self): """ diff --git a/cms/envs/common.py b/cms/envs/common.py index b1640914b1..2b5b556def 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1707,10 +1707,6 @@ DEFAULT_COURSE_LANGUAGE = "en" # None to omit. # ADVANCED_PROBLEM_TYPES = [ - { - 'component': 'openassessment', - 'boilerplate_name': None, - }, { 'component': 'drag-and-drop-v2', 'boilerplate_name': None diff --git a/cms/static/images/large-openassessment-icon.png b/cms/static/images/large-openassessment-icon.png new file mode 100644 index 0000000000..2e7a44a68e Binary files /dev/null and b/cms/static/images/large-openassessment-icon.png differ diff --git a/cms/static/sass/assets/_graphics.scss b/cms/static/sass/assets/_graphics.scss index 18d59c313f..e4642724ca 100644 --- a/cms/static/sass/assets/_graphics.scss +++ b/cms/static/sass/assets/_graphics.scss @@ -45,3 +45,10 @@ height: ($baseline*3); background: url('#{$static-path}/images/large-video-icon.png') center no-repeat; } + +.large-openassessment-icon { + display: inline-block; + width: ($baseline*3); + height: ($baseline*3); + background: url('#{$static-path}/images/large-openassessment-icon.png') center no-repeat; +} diff --git a/cms/static/sass/elements/_modules.scss b/cms/static/sass/elements/_modules.scss index d3898968ea..ecb239d223 100644 --- a/cms/static/sass/elements/_modules.scss +++ b/cms/static/sass/elements/_modules.scss @@ -139,7 +139,7 @@ position: relative; display: inline-block; - width: ($baseline*5); + width: ($baseline*6.25); height: ($baseline*5); margin-bottom: ($baseline/2); box-shadow: 0 1px 1px $shadow, 0 1px 0 rgba(255, 255, 255, 0.4) inset; diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 68d2f2aca4..646bfde0b4 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -163,7 +163,7 @@ nodeenv==1.5.0 # via -r requirements/edx/base.in numpy==1.18.5 # via -c requirements/edx/../constraints.txt, chem, openedx-calc, scipy oauthlib==3.0.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in, django-oauth-toolkit, lti-consumer-xblock, requests-oauthlib, social-auth-core openedx-calc==1.0.9 # via -r requirements/edx/base.in -ora2==2.12.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.in +ora2==2.13.3 # via -r requirements/edx/base.in packaging==20.7 # via bleach, drf-yasg path.py==12.5.0 # via edx-enterprise, edx-i18n-tools, ora2, staff-graded-xblock, xmodule path==13.1.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/paver.txt, path.py diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index d1dad11e00..4a72cc3019 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -196,7 +196,7 @@ nodeenv==1.5.0 # via -r requirements/edx/testing.txt numpy==1.18.5 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, chem, openedx-calc, scipy oauthlib==3.0.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, django-oauth-toolkit, lti-consumer-xblock, requests-oauthlib, social-auth-core openedx-calc==1.0.9 # via -r requirements/edx/testing.txt -ora2==2.12.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt +ora2==2.13.3 # via -r requirements/edx/testing.txt packaging==20.7 # via -r requirements/edx/testing.txt, bleach, drf-yasg, pytest, sphinx, tox path.py==12.5.0 # via -r requirements/edx/testing.txt, edx-enterprise, edx-i18n-tools, ora2, staff-graded-xblock, xmodule path==13.1.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/testing.txt, path.py diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 8f36180255..5479ff351f 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -188,7 +188,7 @@ nodeenv==1.5.0 # via -r requirements/edx/base.txt numpy==1.18.5 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, chem, openedx-calc, scipy oauthlib==3.0.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, django-oauth-toolkit, lti-consumer-xblock, requests-oauthlib, social-auth-core openedx-calc==1.0.9 # via -r requirements/edx/base.txt -ora2==2.12.1 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt +ora2==2.13.3 # via -r requirements/edx/base.txt packaging==20.7 # via -r requirements/edx/base.txt, bleach, drf-yasg, pytest, tox path.py==12.5.0 # via -r requirements/edx/base.txt, edx-enterprise, edx-i18n-tools, ora2, staff-graded-xblock, xmodule path==13.1.0 # via -c requirements/edx/../constraints.txt, -r requirements/edx/base.txt, path.py