diff --git a/common/lib/xmodule/xmodule/combined_open_ended_module.py b/common/lib/xmodule/xmodule/combined_open_ended_module.py index 4fddb29bf1..ecdc67a120 100644 --- a/common/lib/xmodule/xmodule/combined_open_ended_module.py +++ b/common/lib/xmodule/xmodule/combined_open_ended_module.py @@ -283,6 +283,9 @@ class CombinedOpenEndedModule(XModule): return json.dumps(state) + def get_status(self): + pass + class CombinedOpenEndedDescriptor(XmlDescriptor, EditingDescriptor): """ Module for adding self assessment questions to courses diff --git a/common/lib/xmodule/xmodule/open_ended_module.py b/common/lib/xmodule/xmodule/open_ended_module.py index e0911b6489..174fa5e326 100644 --- a/common/lib/xmodule/xmodule/open_ended_module.py +++ b/common/lib/xmodule/xmodule/open_ended_module.py @@ -28,6 +28,7 @@ from .stringify import stringify_children from .xml_module import XmlDescriptor from xmodule.modulestore import Location from capa.util import * +import openendedchild from datetime import datetime @@ -42,89 +43,9 @@ MAX_ATTEMPTS = 1 # Overriden by max_score specified in xml. MAX_SCORE = 1 -class OpenEndedModule(): - """ - States: - - initial (prompt, textbox shown) - | - assessing (read-only textbox, rubric + assessment input shown) - | - request_hint (read-only textbox, read-only rubric and assessment, hint input box shown) - | - done (submitted msg, green checkmark, everything else read-only. If attempts < max, shows - a reset button that goes back to initial state. Saves previous - submissions too.) - """ - - DEFAULT_QUEUE = 'open-ended' - DEFAULT_MESSAGE_QUEUE = 'open-ended-message' - max_inputfields = 1 - - STATE_VERSION = 1 - - # states - INITIAL = 'initial' - ASSESSING = 'assessing' - POST_ASSESSMENT = 'post_assessment' - DONE = 'done' - - def __init__(self, system, location, definition, descriptor, - instance_state=None, shared_state=None, **kwargs): - """ - Definition file should have 4 blocks -- prompt, rubric, submitmessage, hintprompt, - and two optional attributes: - attempts, which should be an integer that defaults to 1. - If it's > 1, the student will be able to re-submit after they see - the rubric. - max_score, which should be an integer that defaults to 1. - It defines the maximum number of points a student can get. Assumed to be integer scale - from 0 to max_score, with an interval of 1. - - Note: all the submissions are stored. - - Sample file: - - - - Insert prompt text here. (arbitrary html) - - - Insert grading rubric here. (arbitrary html) - - - Please enter a hint below: (arbitrary html) - - - Thanks for submitting! (arbitrary html) - - - """ - - # Load instance state - if instance_state is not None: - log.debug(instance_state) - instance_state = json.loads(instance_state) - else: - instance_state = {} - - # History is a list of tuples of (answer, score, hint), where hint may be - # None for any element, and score and hint can be None for the last (current) - # element. - # Scores are on scale from 0 to max_score - self.history = instance_state.get('history', []) - - self.state = instance_state.get('state', 'initial') - - self.created = instance_state.get('created', "False") - - self.attempts = instance_state.get('attempts', 0) - self.max_attempts = int(instance_state.get('attempts', MAX_ATTEMPTS)) - - # Used for progress / grading. Currently get credit just for - # completion (doesn't matter if you self-assessed correct/incorrect). - self._max_score = int(instance_state.get('max_score', MAX_SCORE)) +class OpenEndedModule(openendedchild.OpenEndedChild): + def setup_response(self, system, location, definition, descriptor): oeparam = definition['oeparam'] prompt = definition['prompt'] rubric = definition['rubric'] @@ -179,7 +100,7 @@ class OpenEndedModule(): self.initial_display = find_with_default(oeparam, 'initial_display', '') self.answer = find_with_default(oeparam, 'answer_display', 'No answer given.') - + parsed_grader_payload.update({ 'location' : system.location.url(), 'course_id' : system.course_id, diff --git a/common/lib/xmodule/xmodule/openendedchild.py b/common/lib/xmodule/xmodule/openendedchild.py new file mode 100644 index 0000000000..b8162800a9 --- /dev/null +++ b/common/lib/xmodule/xmodule/openendedchild.py @@ -0,0 +1,130 @@ +""" +A Self Assessment module that allows students to write open-ended responses, +submit, then see a rubric and rate themselves. Persists student supplied +hints, answers, and assessment judgment (currently only correct/incorrect). +Parses xml definition file--see below for exact format. +""" + +import copy +from fs.errors import ResourceNotFoundError +import itertools +import json +import logging +from lxml import etree +from lxml.html import rewrite_links +from path import path +import os +import sys +import hashlib +import capa.xqueue_interface as xqueue_interface + +from pkg_resources import resource_string + +from .capa_module import only_one, ComplexEncoder +from .editing_module import EditingDescriptor +from .html_checker import check_html +from progress import Progress +from .stringify import stringify_children +from .xml_module import XmlDescriptor +from xmodule.modulestore import Location +from capa.util import * + +from datetime import datetime + +log = logging.getLogger("mitx.courseware") + +# Set the default number of max attempts. Should be 1 for production +# Set higher for debugging/testing +# attempts specified in xml definition overrides this. +MAX_ATTEMPTS = 1 + +# Set maximum available number of points. +# Overriden by max_score specified in xml. +MAX_SCORE = 1 + +class OpenEndedChild(): + """ + States: + + initial (prompt, textbox shown) + | + assessing (read-only textbox, rubric + assessment input shown) + | + request_hint (read-only textbox, read-only rubric and assessment, hint input box shown) + | + done (submitted msg, green checkmark, everything else read-only. If attempts < max, shows + a reset button that goes back to initial state. Saves previous + submissions too.) + """ + + DEFAULT_QUEUE = 'open-ended' + DEFAULT_MESSAGE_QUEUE = 'open-ended-message' + max_inputfields = 1 + + STATE_VERSION = 1 + + # states + INITIAL = 'initial' + ASSESSING = 'assessing' + POST_ASSESSMENT = 'post_assessment' + DONE = 'done' + + def __init__(self, system, location, definition, descriptor, + instance_state=None, shared_state=None, **kwargs): + """ + Definition file should have 4 blocks -- prompt, rubric, submitmessage, hintprompt, + and two optional attributes: + attempts, which should be an integer that defaults to 1. + If it's > 1, the student will be able to re-submit after they see + the rubric. + max_score, which should be an integer that defaults to 1. + It defines the maximum number of points a student can get. Assumed to be integer scale + from 0 to max_score, with an interval of 1. + + Note: all the submissions are stored. + + Sample file: + + + + Insert prompt text here. (arbitrary html) + + + Insert grading rubric here. (arbitrary html) + + + Please enter a hint below: (arbitrary html) + + + Thanks for submitting! (arbitrary html) + + + """ + + # Load instance state + if instance_state is not None: + instance_state = json.loads(instance_state) + else: + instance_state = {} + + # History is a list of tuples of (answer, score, hint), where hint may be + # None for any element, and score and hint can be None for the last (current) + # element. + # Scores are on scale from 0 to max_score + self.history = instance_state.get('history', []) + + self.state = instance_state.get('state', 'initial') + + self.created = instance_state.get('created', "False") + + self.attempts = instance_state.get('attempts', 0) + self.max_attempts = int(instance_state.get('attempts', MAX_ATTEMPTS)) + + # Used for progress / grading. Currently get credit just for + # completion (doesn't matter if you self-assessed correct/incorrect). + self._max_score = int(instance_state.get('max_score', MAX_SCORE)) + + self.setup_response(system, location, definition, descriptor) + + def setup_response(self, system, location, definition, descriptor): + pass \ No newline at end of file diff --git a/common/lib/xmodule/xmodule/self_assessment_module.py b/common/lib/xmodule/xmodule/self_assessment_module.py index 64edb2bb8a..6c064bebf8 100644 --- a/common/lib/xmodule/xmodule/self_assessment_module.py +++ b/common/lib/xmodule/xmodule/self_assessment_module.py @@ -26,6 +26,7 @@ from .stringify import stringify_children from .x_module import XModule from .xml_module import XmlDescriptor from xmodule.modulestore import Location +import openendedchild log = logging.getLogger("mitx.courseware") @@ -38,92 +39,14 @@ MAX_ATTEMPTS = 1 # Overriden by max_score specified in xml. MAX_SCORE = 1 -class SelfAssessmentModule(): - """ - States: - - initial (prompt, textbox shown) - | - assessing (read-only textbox, rubric + assessment input shown) - | - request_hint (read-only textbox, read-only rubric and assessment, hint input box shown) - | - done (submitted msg, green checkmark, everything else read-only. If attempts < max, shows - a reset button that goes back to initial state. Saves previous - submissions too.) - """ - - STATE_VERSION = 1 - - # states - INITIAL = 'initial' - ASSESSING = 'assessing' - REQUEST_HINT = 'post_assessment' - DONE = 'done' - - def __init__(self, system, location, definition, descriptor, - instance_state=None, shared_state=None, **kwargs): - """ - Definition file should have 4 blocks -- prompt, rubric, submitmessage, hintprompt, - and two optional attributes: - attempts, which should be an integer that defaults to 1. - If it's > 1, the student will be able to re-submit after they see - the rubric. - max_score, which should be an integer that defaults to 1. - It defines the maximum number of points a student can get. Assumed to be integer scale - from 0 to max_score, with an interval of 1. - - Note: all the submissions are stored. - - Sample file: - - - - Insert prompt text here. (arbitrary html) - - - Insert grading rubric here. (arbitrary html) - - - Please enter a hint below: (arbitrary html) - - - Thanks for submitting! (arbitrary html) - - - """ - - # Load instance state - if instance_state is not None: - instance_state = json.loads(instance_state) - else: - instance_state = {} - - instance_state = self.convert_state_to_current_format(instance_state) - - # History is a list of tuples of (answer, score, hint), where hint may be - # None for any element, and score and hint can be None for the last (current) - # element. - # Scores are on scale from 0 to max_score - self.history = instance_state.get('history', []) - - self.created = instance_state.get('created', "False") - - self.state = instance_state.get('state', 'initial') - - self.attempts = instance_state.get('attempts', 0) - self.max_attempts = int(instance_state.get('attempts', MAX_ATTEMPTS)) - - # Used for progress / grading. Currently get credit just for - # completion (doesn't matter if you self-assessed correct/incorrect). - self._max_score = int(instance_state.get('max_score', MAX_SCORE)) +class SelfAssessmentModule(openendedchild.OpenEndedChild): + def setup_response(self, system, location, definition, descriptor): self.rubric = definition['rubric'] self.prompt = definition['prompt'] self.submit_message = definition['submitmessage'] self.hint_prompt = definition['hintprompt'] - def latest_answer(self): """None if not available""" if not self.history: diff --git a/lms/templates/combined_open_ended.html b/lms/templates/combined_open_ended.html index 5b6823e809..a050dad906 100644 --- a/lms/templates/combined_open_ended.html +++ b/lms/templates/combined_open_ended.html @@ -6,5 +6,8 @@ + +
${status | n}
+ diff --git a/lms/templates/combined_open_ended_status.html b/lms/templates/combined_open_ended_status.html new file mode 100644 index 0000000000..770870b077 --- /dev/null +++ b/lms/templates/combined_open_ended_status.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file