Factor out init modules, add in status display
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
<selfassessment attempts="1" max_score="1">
|
||||
<prompt>
|
||||
Insert prompt text here. (arbitrary html)
|
||||
</prompt>
|
||||
<rubric>
|
||||
Insert grading rubric here. (arbitrary html)
|
||||
</rubric>
|
||||
<hintprompt>
|
||||
Please enter a hint below: (arbitrary html)
|
||||
</hintprompt>
|
||||
<submitmessage>
|
||||
Thanks for submitting! (arbitrary html)
|
||||
</submitmessage>
|
||||
</selfassessment>
|
||||
"""
|
||||
|
||||
# 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,
|
||||
|
||||
130
common/lib/xmodule/xmodule/openendedchild.py
Normal file
130
common/lib/xmodule/xmodule/openendedchild.py
Normal file
@@ -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:
|
||||
|
||||
<selfassessment attempts="1" max_score="1">
|
||||
<prompt>
|
||||
Insert prompt text here. (arbitrary html)
|
||||
</prompt>
|
||||
<rubric>
|
||||
Insert grading rubric here. (arbitrary html)
|
||||
</rubric>
|
||||
<hintprompt>
|
||||
Please enter a hint below: (arbitrary html)
|
||||
</hintprompt>
|
||||
<submitmessage>
|
||||
Thanks for submitting! (arbitrary html)
|
||||
</submitmessage>
|
||||
</selfassessment>
|
||||
"""
|
||||
|
||||
# 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
|
||||
@@ -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:
|
||||
|
||||
<selfassessment attempts="1" max_score="1">
|
||||
<prompt>
|
||||
Insert prompt text here. (arbitrary html)
|
||||
</prompt>
|
||||
<rubric>
|
||||
Insert grading rubric here. (arbitrary html)
|
||||
</rubric>
|
||||
<hintprompt>
|
||||
Please enter a hint below: (arbitrary html)
|
||||
</hintprompt>
|
||||
<submitmessage>
|
||||
Thanks for submitting! (arbitrary html)
|
||||
</submitmessage>
|
||||
</selfassessment>
|
||||
"""
|
||||
|
||||
# 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:
|
||||
|
||||
@@ -6,5 +6,8 @@
|
||||
|
||||
<input type="button" value="Reset" class="reset-button" name="reset"/>
|
||||
<input type="button" value="Next Step" class="next-step-button" name="reset"/>
|
||||
|
||||
<div class="status">${status | n}</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
3
lms/templates/combined_open_ended_status.html
Normal file
3
lms/templates/combined_open_ended_status.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<section id="combined-open-ended-status" class="combined-open-ended-status">
|
||||
|
||||
</section>
|
||||
Reference in New Issue
Block a user