From 20b04c9bf20bdd9be4e57ce7a1b9e0033b2ea325 Mon Sep 17 00:00:00 2001 From: Will Daly Date: Wed, 6 Mar 2013 13:06:15 -0500 Subject: [PATCH 01/15] Fixed broken calculator test in lms coffee scripts --- lms/static/coffee/spec/calculator_spec.coffee | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lms/static/coffee/spec/calculator_spec.coffee b/lms/static/coffee/spec/calculator_spec.coffee index 072d220a44..a8210ce68b 100644 --- a/lms/static/coffee/spec/calculator_spec.coffee +++ b/lms/static/coffee/spec/calculator_spec.coffee @@ -31,12 +31,19 @@ describe 'Calculator', -> $('form#calculator').submit() describe 'toggle', -> - it 'toggle the calculator and focus the input', -> - spyOn $.fn, 'focus' - @calculator.toggle(jQuery.Event("click")) + it 'focuses the input when toggled', -> - expect($('li.calc-main')).toHaveClass('open') - expect($('#calculator_wrapper #calculator_input').focus).toHaveBeenCalled() + # Since the focus is called asynchronously, we need to + # wait until focus() is called. + didFocus = false + runs -> + spyOn($.fn, 'focus').andCallFake (elementName) -> didFocus = true + @calculator.toggle(jQuery.Event("click")) + + waitsFor (-> didFocus), "focus() should have been called on the input", 1000 + + runs -> + expect($('#calculator_wrapper #calculator_input').focus).toHaveBeenCalled() it 'toggle the close button on the calculator button', -> @calculator.toggle(jQuery.Event("click")) From 618de5df25642c4962b5870438aa13586b2ffd36 Mon Sep 17 00:00:00 2001 From: Will Daly Date: Wed, 6 Mar 2013 17:08:12 -0500 Subject: [PATCH 02/15] Fixed import error for histogram.coffee --- common/templates/jasmine/base.html | 7 ++++--- lms/static/coffee/files.json | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/common/templates/jasmine/base.html b/common/templates/jasmine/base.html index 96507bdebf..40308c7bbe 100644 --- a/common/templates/jasmine/base.html +++ b/common/templates/jasmine/base.html @@ -13,14 +13,15 @@ + {% load compressed %} + {# static files #} + {% compressed_js 'js-test-source' %} + {# source files #} {% for url in suite.js_files %} {% endfor %} - {% load compressed %} - {# static files #} - {% compressed_js 'js-test-source' %} {# spec files #} {% compressed_js 'spec' %} diff --git a/lms/static/coffee/files.json b/lms/static/coffee/files.json index 5dc03613b9..0efe488dd9 100644 --- a/lms/static/coffee/files.json +++ b/lms/static/coffee/files.json @@ -5,8 +5,5 @@ "/static/js/vendor/jquery-ui.min.js", "/static/js/vendor/jquery.leanModal.min.js", "/static/js/vendor/flot/jquery.flot.js" - ], - "static_files": [ - "js/application.js" ] } From 6b94090e673ab80723de67d3d95be819714ede44 Mon Sep 17 00:00:00 2001 From: Will Daly Date: Wed, 6 Mar 2013 17:41:44 -0500 Subject: [PATCH 03/15] Fixed jasmine test issue with cms caused by previous change to order of file import. Introduced static_files list in files.json (supported in original django_jasmine implementation) that loads static files before any compiled files. --- cms/static/coffee/files.json | 18 +++++++++--------- common/templates/jasmine/base.html | 4 ++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cms/static/coffee/files.json b/cms/static/coffee/files.json index 2249813b04..e7a66b5bc0 100644 --- a/cms/static/coffee/files.json +++ b/cms/static/coffee/files.json @@ -1,12 +1,12 @@ { - "js_files": [ - "/static/js/vendor/RequireJS.js", - "/static/js/vendor/jquery.min.js", - "/static/js/vendor/jquery-ui.min.js", - "/static/js/vendor/jquery.ui.draggable.js", - "/static/js/vendor/jquery.cookie.js", - "/static/js/vendor/json2.js", - "/static/js/vendor/underscore-min.js", - "/static/js/vendor/backbone-min.js" + "static_files": [ + "js/vendor/RequireJS.js", + "js/vendor/jquery.min.js", + "js/vendor/jquery-ui.min.js", + "js/vendor/jquery.ui.draggable.js", + "js/vendor/jquery.cookie.js", + "js/vendor/json2.js", + "js/vendor/underscore-min.js", + "js/vendor/backbone-min.js" ] } diff --git a/common/templates/jasmine/base.html b/common/templates/jasmine/base.html index 40308c7bbe..9a1b3bed92 100644 --- a/common/templates/jasmine/base.html +++ b/common/templates/jasmine/base.html @@ -15,6 +15,10 @@ {% load compressed %} {# static files #} + {% for url in suite.static_files %} + + {% endfor %} + {% compressed_js 'js-test-source' %} {# source files #} From a777a9e6e3673d9c049e2c3ad7cba6d7ec756c62 Mon Sep 17 00:00:00 2001 From: Will Daly Date: Mon, 11 Mar 2013 11:20:20 -0400 Subject: [PATCH 04/15] Updated broken tabs.js Jasmine tests. --- lms/static/coffee/spec/modules/tab_spec.coffee | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lms/static/coffee/spec/modules/tab_spec.coffee b/lms/static/coffee/spec/modules/tab_spec.coffee index 909f0d7cda..6fba470974 100644 --- a/lms/static/coffee/spec/modules/tab_spec.coffee +++ b/lms/static/coffee/spec/modules/tab_spec.coffee @@ -22,18 +22,23 @@ describe 'Tab', -> it 'bind the tabs', -> expect($.fn.tabs).toHaveBeenCalledWith show: @tab.onShow + # As of jQuery 1.9, the onShow callback is deprecated + # http://jqueryui.com/upgrade-guide/1.9/#deprecated-show-event-renamed-to-activate + # The code below tests that onShow does what is expected, + # but note that onShow will NOT be called when the user + # clicks on the tab if we're using jQuery version >= 1.9 describe 'onShow', -> beforeEach -> @tab = new Tab 1, @items - $('[href="#tab-1-0"]').click() + @tab.onShow($('#tab-1-0'), {'index': 1}) it 'replace content in the container', -> - $('[href="#tab-1-1"]').click() + @tab.onShow($('#tab-1-1'), {'index': 1}) expect($('#tab-1-0').html()).toEqual '' expect($('#tab-1-1').html()).toEqual 'Video 2' expect($('#tab-1-2').html()).toEqual '' it 'trigger contentChanged event on the element', -> spyOnEvent @tab.el, 'contentChanged' - $('[href="#tab-1-1"]').click() + @tab.onShow($('#tab-1-1'), {'index': 1}) expect('contentChanged').toHaveBeenTriggeredOn @tab.el From 9d8c023acb05271a71e040236df5db7bbddb530b Mon Sep 17 00:00:00 2001 From: Will Daly Date: Mon, 11 Mar 2013 15:58:31 -0400 Subject: [PATCH 05/15] Removed calls to Navigation.bind() and Calculator.bind() that were causing phantomjs interpreter to fail with undefined method error. --- lms/static/coffee/spec/calculator_spec.coffee | 3 --- lms/static/coffee/spec/navigation_spec.coffee | 3 --- 2 files changed, 6 deletions(-) diff --git a/lms/static/coffee/spec/calculator_spec.coffee b/lms/static/coffee/spec/calculator_spec.coffee index a8210ce68b..8258d8965a 100644 --- a/lms/static/coffee/spec/calculator_spec.coffee +++ b/lms/static/coffee/spec/calculator_spec.coffee @@ -4,9 +4,6 @@ describe 'Calculator', -> @calculator = new Calculator describe 'bind', -> - beforeEach -> - Calculator.bind() - it 'bind the calculator button', -> expect($('.calc')).toHandleWith 'click', @calculator.toggle diff --git a/lms/static/coffee/spec/navigation_spec.coffee b/lms/static/coffee/spec/navigation_spec.coffee index 1340984e52..b351164b63 100644 --- a/lms/static/coffee/spec/navigation_spec.coffee +++ b/lms/static/coffee/spec/navigation_spec.coffee @@ -32,11 +32,9 @@ describe 'Navigation', -> heightStyle: 'content' it 'binds the accordionchange event', -> - Navigation.bind() expect($('#accordion')).toHandleWith 'accordionchange', @navigation.log it 'bind the navigation toggle', -> - Navigation.bind() expect($('#open_close_accordion a')).toHandleWith 'click', @navigation.toggle describe 'when the #accordion does not exists', -> @@ -45,7 +43,6 @@ describe 'Navigation', -> it 'does not activate the accordion', -> spyOn $.fn, 'accordion' - Navigation.bind() expect($('#accordion').accordion).wasNotCalled() describe 'toggle', -> From c34666af80706f329f36aa75f134178cc4b77a34 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 14:20:55 -0400 Subject: [PATCH 06/15] Fix version handling --- .../lib/xmodule/xmodule/combined_open_ended_module.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index f05f419a03..db0208ba95 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -8,6 +8,7 @@ from xmodule.raw_module import RawDescriptor from .x_module import XModule from xblock.core import Integer, Scope, BlockScope, ModelType, String, Boolean, Object, Float, List from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor +import copy log = logging.getLogger("mitx.courseware") @@ -137,13 +138,14 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): student_attributes = [i[4] for i in VERSION_TUPLES] version_error_string = "Could not find version {0}, using version {1} instead" + version_to_use = copy.copy(self.version) try: - version_index = versions.index(self.version) + version_index = versions.index(version_to_use) except: #This is a dev_facing_error - log.error(version_error_string.format(self.version, DEFAULT_VERSION)) - self.version = DEFAULT_VERSION - version_index = versions.index(self.version) + log.error(version_error_string.format(version_to_use, DEFAULT_VERSION)) + version_to_use = DEFAULT_VERSION + version_index = versions.index(version_to_use) self.student_attributes = student_attributes[version_index] self.settings_attributes = settings_attributes[version_index] From 97839d9de5ee003aa6644c6b4d35e2166b1e4f98 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 14:52:55 -0400 Subject: [PATCH 07/15] Fix default version string integer issues --- common/lib/xmodule/xmodule/combined_open_ended_module.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index db0208ba95..eab091b1aa 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -22,11 +22,10 @@ V1_STUDENT_ATTRIBUTES = ["current_task_number", "task_states", "state", V1_ATTRIBUTES = V1_SETTINGS_ATTRIBUTES + V1_STUDENT_ATTRIBUTES VERSION_TUPLES = ( - ('1', CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, V1_STUDENT_ATTRIBUTES), + (1, CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, V1_STUDENT_ATTRIBUTES), ) DEFAULT_VERSION = 1 -DEFAULT_VERSION = str(DEFAULT_VERSION) class CombinedOpenEndedFields(object): From 405ea8d67515e990b286f6c51efc7996e402c215 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 14:57:48 -0400 Subject: [PATCH 08/15] Remove copy.copy --- common/lib/xmodule/xmodule/combined_open_ended_module.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index eab091b1aa..63d77b5efe 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -8,7 +8,6 @@ from xmodule.raw_module import RawDescriptor from .x_module import XModule from xblock.core import Integer, Scope, BlockScope, ModelType, String, Boolean, Object, Float, List from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor -import copy log = logging.getLogger("mitx.courseware") @@ -137,7 +136,7 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): student_attributes = [i[4] for i in VERSION_TUPLES] version_error_string = "Could not find version {0}, using version {1} instead" - version_to_use = copy.copy(self.version) + version_to_use = self.version try: version_index = versions.index(version_to_use) except: From b609ab0e6abd1f3123189647569d83ef9d8b0272 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 15:05:47 -0400 Subject: [PATCH 09/15] Convert to using versioninteger class --- .../xmodule/combined_open_ended_module.py | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index 63d77b5efe..b7632ccffa 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -26,6 +26,24 @@ VERSION_TUPLES = ( DEFAULT_VERSION = 1 +class VersionInteger(Integer): + """ + A model type that converts from strings to integers when reading from json. + Also does error checking to see if version is correct or not. + """ + def from_json(self, value): + try: + value = int(value) + versions = [i[0] for i in VERSION_TUPLES] + try: + versions.index(value) + except: + version_error_string = "Could not find version {0}, using version {1} instead" + log.error(version_error_string.format(value, DEFAULT_VERSION)) + value = DEFAULT_VERSION + except: + value = DEFAULT_VERSION + return value class CombinedOpenEndedFields(object): display_name = String(help="Display name for this module", default="Open Ended Grading", scope=Scope.settings) @@ -41,7 +59,7 @@ class CombinedOpenEndedFields(object): due = String(help="Date that this problem is due by", default=None, scope=Scope.settings) graceperiod = String(help="Amount of time after the due date that submissions will be accepted", default=None, scope=Scope.settings) max_score = Integer(help="Maximum score for the problem.", default=1, scope=Scope.settings) - version = Integer(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings) + version = VersionInteger(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings) data = String(help="XML data for the problem", scope=Scope.content) @@ -134,16 +152,8 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): modules = [i[2] for i in VERSION_TUPLES] settings_attributes = [i[3] for i in VERSION_TUPLES] student_attributes = [i[4] for i in VERSION_TUPLES] - version_error_string = "Could not find version {0}, using version {1} instead" - version_to_use = self.version - try: - version_index = versions.index(version_to_use) - except: - #This is a dev_facing_error - log.error(version_error_string.format(version_to_use, DEFAULT_VERSION)) - version_to_use = DEFAULT_VERSION - version_index = versions.index(version_to_use) + version_index = versions.index(self.version) self.student_attributes = student_attributes[version_index] self.settings_attributes = settings_attributes[version_index] From 01b1974f5077b15f6b4da1467fa8da1a3a943466 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 15:15:23 -0400 Subject: [PATCH 10/15] Address review comment --- common/lib/xmodule/xmodule/combined_open_ended_module.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index b7632ccffa..cd3f66efbc 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -35,9 +35,7 @@ class VersionInteger(Integer): try: value = int(value) versions = [i[0] for i in VERSION_TUPLES] - try: - versions.index(value) - except: + if value not in versions: version_error_string = "Could not find version {0}, using version {1} instead" log.error(version_error_string.format(value, DEFAULT_VERSION)) value = DEFAULT_VERSION From 500c4ab86ea86d9e2c17f5161305bbd8ec099c14 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 15:40:04 -0400 Subject: [PATCH 11/15] Convert versions to tuples --- .../xmodule/combined_open_ended_module.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index cd3f66efbc..981d647d4f 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -8,6 +8,7 @@ from xmodule.raw_module import RawDescriptor from .x_module import XModule from xblock.core import Integer, Scope, BlockScope, ModelType, String, Boolean, Object, Float, List from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor +from collections import namedtuple log = logging.getLogger("mitx.courseware") @@ -20,8 +21,9 @@ V1_STUDENT_ATTRIBUTES = ["current_task_number", "task_states", "state", V1_ATTRIBUTES = V1_SETTINGS_ATTRIBUTES + V1_STUDENT_ATTRIBUTES +VersionTuple= namedtuple('VersionTuple', ['version', 'descriptor', 'module', 'settings_attributes', 'student_attributes']) VERSION_TUPLES = ( - (1, CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, V1_STUDENT_ATTRIBUTES), + VersionTuple(1, CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, V1_STUDENT_ATTRIBUTES), ) DEFAULT_VERSION = 1 @@ -34,7 +36,7 @@ class VersionInteger(Integer): def from_json(self, value): try: value = int(value) - versions = [i[0] for i in VERSION_TUPLES] + versions = [i.version for i in VERSION_TUPLES] if value not in versions: version_error_string = "Could not find version {0}, using version {1} instead" log.error(version_error_string.format(value, DEFAULT_VERSION)) @@ -145,16 +147,14 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): if self.task_states is None: self.task_states = [] - versions = [i[0] for i in VERSION_TUPLES] - descriptors = [i[1] for i in VERSION_TUPLES] - modules = [i[2] for i in VERSION_TUPLES] - settings_attributes = [i[3] for i in VERSION_TUPLES] - student_attributes = [i[4] for i in VERSION_TUPLES] + versions = [i.version for i in VERSION_TUPLES] version_index = versions.index(self.version) - self.student_attributes = student_attributes[version_index] - self.settings_attributes = settings_attributes[version_index] + version_tuple = VERSION_TUPLES[version_index] + + self.student_attributes = version_tuple.student_attributes + self.settings_attributes = version_tuple.settings_attributes attributes = self.student_attributes + self.settings_attributes @@ -162,9 +162,9 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): 'rewrite_content_links': self.rewrite_content_links, } instance_state = {k: getattr(self, k) for k in attributes} - self.child_descriptor = descriptors[version_index](self.system) - self.child_definition = descriptors[version_index].definition_from_xml(etree.fromstring(self.data), self.system) - self.child_module = modules[version_index](self.system, location, self.child_definition, self.child_descriptor, + self.child_descriptor = version_tuple.descriptor(self.system) + self.child_definition = version_tuple.descriptor.definition_from_xml(etree.fromstring(self.data), self.system) + self.child_module = version_tuple.module(self.system, location, self.child_definition, self.child_descriptor, instance_state=instance_state, static_data=static_data, attributes=attributes) self.save_instance_data() From cd3e88fdd5ea21d2da84000f256151da3d220d97 Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 16:05:38 -0400 Subject: [PATCH 12/15] Fix version tuples to be a dictionary --- .../xmodule/combined_open_ended_module.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index 981d647d4f..8379d9a7e6 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -21,10 +21,10 @@ V1_STUDENT_ATTRIBUTES = ["current_task_number", "task_states", "state", V1_ATTRIBUTES = V1_SETTINGS_ATTRIBUTES + V1_STUDENT_ATTRIBUTES -VersionTuple= namedtuple('VersionTuple', ['version', 'descriptor', 'module', 'settings_attributes', 'student_attributes']) -VERSION_TUPLES = ( - VersionTuple(1, CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, V1_STUDENT_ATTRIBUTES), -) +VersionTuple= namedtuple('VersionTuple', ['descriptor', 'module', 'settings_attributes', 'student_attributes']) +VERSION_TUPLES = { + 1: VersionTuple(CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, V1_STUDENT_ATTRIBUTES), +} DEFAULT_VERSION = 1 @@ -36,8 +36,7 @@ class VersionInteger(Integer): def from_json(self, value): try: value = int(value) - versions = [i.version for i in VERSION_TUPLES] - if value not in versions: + if value not in VERSION_TUPLES: version_error_string = "Could not find version {0}, using version {1} instead" log.error(version_error_string.format(value, DEFAULT_VERSION)) value = DEFAULT_VERSION @@ -147,11 +146,7 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): if self.task_states is None: self.task_states = [] - versions = [i.version for i in VERSION_TUPLES] - - version_index = versions.index(self.version) - - version_tuple = VERSION_TUPLES[version_index] + version_tuple = VERSION_TUPLES[self.version] self.student_attributes = version_tuple.student_attributes self.settings_attributes = version_tuple.settings_attributes From e4efda9a475abe5c5e68f4bbcfd5bd9d6cf6550b Mon Sep 17 00:00:00 2001 From: Vik Paruchuri Date: Fri, 15 Mar 2013 16:21:36 -0400 Subject: [PATCH 13/15] Reformat code --- .../xmodule/combined_open_ended_module.py | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index 8379d9a7e6..48fbfcced1 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -12,27 +12,29 @@ from collections import namedtuple log = logging.getLogger("mitx.courseware") - V1_SETTINGS_ATTRIBUTES = ["display_name", "attempts", "is_graded", "accept_file_upload", - "skip_spelling_checks", "due", "graceperiod", "max_score"] + "skip_spelling_checks", "due", "graceperiod", "max_score"] V1_STUDENT_ATTRIBUTES = ["current_task_number", "task_states", "state", - "student_attempts", "ready_to_reset"] + "student_attempts", "ready_to_reset"] V1_ATTRIBUTES = V1_SETTINGS_ATTRIBUTES + V1_STUDENT_ATTRIBUTES -VersionTuple= namedtuple('VersionTuple', ['descriptor', 'module', 'settings_attributes', 'student_attributes']) +VersionTuple = namedtuple('VersionTuple', ['descriptor', 'module', 'settings_attributes', 'student_attributes']) VERSION_TUPLES = { - 1: VersionTuple(CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, V1_STUDENT_ATTRIBUTES), + 1: VersionTuple(CombinedOpenEndedV1Descriptor, CombinedOpenEndedV1Module, V1_SETTINGS_ATTRIBUTES, + V1_STUDENT_ATTRIBUTES), } DEFAULT_VERSION = 1 + class VersionInteger(Integer): """ A model type that converts from strings to integers when reading from json. Also does error checking to see if version is correct or not. """ + def from_json(self, value): try: value = int(value) @@ -44,19 +46,26 @@ class VersionInteger(Integer): value = DEFAULT_VERSION return value + class CombinedOpenEndedFields(object): display_name = String(help="Display name for this module", default="Open Ended Grading", scope=Scope.settings) current_task_number = Integer(help="Current task that the student is on.", default=0, scope=Scope.student_state) task_states = List(help="List of state dictionaries of each task within this module.", scope=Scope.student_state) - state = String(help="Which step within the current task that the student is on.", default="initial", scope=Scope.student_state) - student_attempts = Integer(help="Number of attempts taken by the student on this problem", default=0, scope=Scope.student_state) - ready_to_reset = Boolean(help="If the problem is ready to be reset or not.", default=False, scope=Scope.student_state) + state = String(help="Which step within the current task that the student is on.", default="initial", + scope=Scope.student_state) + student_attempts = Integer(help="Number of attempts taken by the student on this problem", default=0, + scope=Scope.student_state) + ready_to_reset = Boolean(help="If the problem is ready to be reset or not.", default=False, + scope=Scope.student_state) attempts = Integer(help="Maximum number of attempts that a student is allowed.", default=1, scope=Scope.settings) - is_graded = Boolean(help="Whether or not the problem is graded.", default=False, scope=Scope.settings) - accept_file_upload = Boolean(help="Whether or not the problem accepts file uploads.", default=False, scope=Scope.settings) - skip_spelling_checks = Boolean(help="Whether or not to skip initial spelling checks.", default=True, scope=Scope.settings) + is_graded = Boolean(help="Whether or not the problem is graded.", default=False, scope=Scope.settings) + accept_file_upload = Boolean(help="Whether or not the problem accepts file uploads.", default=False, + scope=Scope.settings) + skip_spelling_checks = Boolean(help="Whether or not to skip initial spelling checks.", default=True, + scope=Scope.settings) due = String(help="Date that this problem is due by", default=None, scope=Scope.settings) - graceperiod = String(help="Amount of time after the due date that submissions will be accepted", default=None, scope=Scope.settings) + graceperiod = String(help="Amount of time after the due date that submissions will be accepted", default=None, + scope=Scope.settings) max_score = Integer(help="Maximum score for the problem.", default=1, scope=Scope.settings) version = VersionInteger(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings) data = String(help="XML data for the problem", scope=Scope.content) @@ -160,7 +169,8 @@ class CombinedOpenEndedModule(CombinedOpenEndedFields, XModule): self.child_descriptor = version_tuple.descriptor(self.system) self.child_definition = version_tuple.descriptor.definition_from_xml(etree.fromstring(self.data), self.system) self.child_module = version_tuple.module(self.system, location, self.child_definition, self.child_descriptor, - instance_state=instance_state, static_data=static_data, attributes=attributes) + instance_state=instance_state, static_data=static_data, + attributes=attributes) self.save_instance_data() def get_html(self): From a4f10bfdafdfcc7e0619a9364e2706c18da1fd22 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Mon, 18 Mar 2013 12:45:46 -0400 Subject: [PATCH 14/15] Small pylint cleanup --- cms/xmodule_namespace.py | 16 ++++++++++++++++ lms/xmodule_namespace.py | 31 +++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/cms/xmodule_namespace.py b/cms/xmodule_namespace.py index 391cac8eca..cad3110574 100644 --- a/cms/xmodule_namespace.py +++ b/cms/xmodule_namespace.py @@ -1,14 +1,27 @@ +""" +Namespace defining common fields used by Studio for all blocks +""" + import datetime from xblock.core import Namespace, Boolean, Scope, ModelType, String class StringyBoolean(Boolean): + """ + Reads strings from JSON as booleans. + + If the string is 'true' (case insensitive), then return True, + otherwise False. + + JSON values that aren't strings are returned as is + """ def from_json(self, value): if isinstance(value, basestring): return value.lower() == 'true' return value + class DateTuple(ModelType): """ ModelType that stores datetime objects as time tuples @@ -24,6 +37,9 @@ class DateTuple(ModelType): class CmsNamespace(Namespace): + """ + Namespace with fields common to all blocks in Studio + """ is_draft = Boolean(help="Whether this module is a draft", default=False, scope=Scope.settings) published_date = DateTuple(help="Date when the module was published", scope=Scope.settings) published_by = String(help="Id of the user who published this module", scope=Scope.settings) diff --git a/lms/xmodule_namespace.py b/lms/xmodule_namespace.py index 4c04700a31..423c0eb0ec 100644 --- a/lms/xmodule_namespace.py +++ b/lms/xmodule_namespace.py @@ -1,14 +1,28 @@ -from xblock.core import Namespace, Boolean, Scope, String, List, Float +""" +Namespace that defines fields common to all blocks used in the LMS +""" +from xblock.core import Namespace, Boolean, Scope, String, Float from xmodule.fields import Date, Timedelta class StringyBoolean(Boolean): + """ + Reads strings from JSON as booleans. + + 'true' (case insensitive) return True, other strings return False + Other types are returned unchanged + """ def from_json(self, value): if isinstance(value, basestring): return value.lower() == 'true' return value + class StringyFloat(Float): + """ + Reads values as floats. If the value parses as a float, returns + that, otherwise returns None + """ def from_json(self, value): try: return float(value) @@ -17,6 +31,9 @@ class StringyFloat(Float): class LmsNamespace(Namespace): + """ + Namespace that defines fields common to all blocks used in the LMS + """ hide_from_toc = StringyBoolean( help="Whether to display this module in the table of contents", default=False, @@ -38,8 +55,14 @@ class LmsNamespace(Namespace): source_file = String(help="DO NOT USE", scope=Scope.settings) xqa_key = String(help="DO NOT USE", scope=Scope.settings) ispublic = Boolean(help="Whether this course is open to the public, or only to admins", scope=Scope.settings) - graceperiod = Timedelta(help="Amount of time after the due date that submissions will be accepted", scope=Scope.settings) + graceperiod = Timedelta( + help="Amount of time after the due date that submissions will be accepted", + scope=Scope.settings + ) showanswer = String(help="When to show the problem answer to the student", scope=Scope.settings, default="closed") rerandomize = String(help="When to rerandomize the problem", default="always", scope=Scope.settings) - days_early_for_beta = StringyFloat(help="Number of days early to show content to beta users", default=None, scope=Scope.settings) - + days_early_for_beta = StringyFloat( + help="Number of days early to show content to beta users", + default=None, + scope=Scope.settings + ) From 3fbde821dd26f99a818d751089308ddf41a17179 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Mon, 18 Mar 2013 13:09:10 -0400 Subject: [PATCH 15/15] Skip over fields that are scoped to students when looping through the CourseModule fields in the instructor dashboard --- lms/djangoapps/instructor/views.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lms/djangoapps/instructor/views.py b/lms/djangoapps/instructor/views.py index 0be5724365..671283db9f 100644 --- a/lms/djangoapps/instructor/views.py +++ b/lms/djangoapps/instructor/views.py @@ -92,9 +92,15 @@ def instructor_dashboard(request, course_id): data += compute_course_stats(course).items() if request.user.is_staff: for field in course.fields: + if getattr(field.scope, 'student', False): + continue + data.append([field.name, json.dumps(field.read_json(course))]) for namespace in course.namespaces: for field in getattr(course, namespace).fields: + if getattr(field.scope, 'student', False): + continue + data.append(["{}.{}".format(namespace, field.name), json.dumps(field.read_json(course))]) datatable['data'] = data