Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/vik/studio oe #160

Merged
merged 16 commits into from
Jul 19, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/tests/test_contentstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def test_advanced_components_in_edit_unit(self):
self.check_components_on_page(ADVANCED_COMPONENT_TYPES, ['Video Alpha',
'Word cloud',
'Annotation',
'Open Ended Grading',
'Open Response Assessment',
'Peer Grading Interface'])

def test_advanced_components_require_two_clicks(self):
Expand Down
297 changes: 210 additions & 87 deletions common/lib/xmodule/xmodule/combined_open_ended_module.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ Write a persuasive essay to a newspaper reflecting your vies on censorship in li
mode: null
})
@setCurrentEditor(@markdown_editor)
selection = @markdown_editor.getSelection()
#Auto-add in the needed template if it isn't already in there.
if(@markdown_editor.getValue() == "")
@markdown_editor.setValue(OpenEndedMarkdownEditingDescriptor.promptTemplate + "\n" + OpenEndedMarkdownEditingDescriptor.rubricTemplate + "\n" + OpenEndedMarkdownEditingDescriptor.tasksTemplate)
# Add listeners for toolbar buttons (only present for markdown editor)
@element.on('click', '.xml-tab', @onShowXMLButton)
@element.on('click', '.format-buttons a', @onToolbarButton)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,37 +78,7 @@ def __init__(self, system, location, definition, descriptor,
instance_state=None, shared_state=None, metadata=None, static_data=None, **kwargs):

"""
Definition file should have one or many task blocks, a rubric block, and a prompt block:

Sample file:
<combinedopenended attempts="10000">
<rubric>
Blah blah rubric.
</rubric>
<prompt>
Some prompt.
</prompt>
<task>
<selfassessment>
<hintprompt>
What hint about this problem would you give to someone?
</hintprompt>
<submitmessage>
Save Succcesful. Thanks for participating!
</submitmessage>
</selfassessment>
</task>
<task>
<openended min_score_to_attempt="1" max_score_to_attempt="1">
<openendedparam>
<initial_display>Enter essay here.</initial_display>
<answer_display>This is the answer.</answer_display>
<grader_payload>{"grader_settings" : "ml_grading.conf",
"problem_id" : "6.002x/Welcome/OETest"}</grader_payload>
</openendedparam>
</openended>
</task>
</combinedopenended>
Definition file should have one or many task blocks, a rubric block, and a prompt block. See DEFAULT_DATA in combined_open_ended_module for a sample.

"""

Expand All @@ -131,14 +101,14 @@ def __init__(self, system, location, definition, descriptor,

# Allow reset is true if student has failed the criteria to move to the next child task
self.ready_to_reset = instance_state.get('ready_to_reset', False)
self.attempts = self.instance_state.get('attempts', MAX_ATTEMPTS)
self.is_scored = self.instance_state.get('is_graded', IS_SCORED) in TRUE_DICT
self.accept_file_upload = self.instance_state.get('accept_file_upload', ACCEPT_FILE_UPLOAD) in TRUE_DICT
self.skip_basic_checks = self.instance_state.get('skip_spelling_checks', SKIP_BASIC_CHECKS) in TRUE_DICT
self.max_attempts = instance_state.get('max_attempts', MAX_ATTEMPTS)
self.is_scored = instance_state.get('graded', IS_SCORED) in TRUE_DICT
self.accept_file_upload = instance_state.get('accept_file_upload', ACCEPT_FILE_UPLOAD) in TRUE_DICT
self.skip_basic_checks = instance_state.get('skip_spelling_checks', SKIP_BASIC_CHECKS) in TRUE_DICT

due_date = self.instance_state.get('due', None)
due_date = instance_state.get('due', None)

grace_period_string = self.instance_state.get('graceperiod', None)
grace_period_string = instance_state.get('graceperiod', None)
try:
self.timeinfo = TimeInfo(due_date, grace_period_string)
except Exception:
Expand All @@ -153,7 +123,7 @@ def __init__(self, system, location, definition, descriptor,
# Static data is passed to the child modules to render
self.static_data = {
'max_score': self._max_score,
'max_attempts': self.attempts,
'max_attempts': self.max_attempts,
'prompt': definition['prompt'],
'rubric': definition['rubric'],
'display_name': self.display_name,
Expand Down Expand Up @@ -643,15 +613,18 @@ def reset(self, data):
if not self.ready_to_reset:
return self.out_of_sync_error(data)

if self.student_attempts > self.attempts:
if self.student_attempts >= self.max_attempts-1:
if self.student_attempts==self.max_attempts-1:
self.student_attempts +=1
return {
'success': False,
# This is a student_facing_error
'error': (
'You have attempted this question {0} times. '
'You are only allowed to attempt it {1} times.'
).format(self.student_attempts, self.attempts)
).format(self.student_attempts, self.max_attempts)
}
self.student_attempts +=1
self.state = self.INITIAL
self.ready_to_reset = False
for i in xrange(0, len(self.task_xml)):
Expand Down Expand Up @@ -726,7 +699,12 @@ def get_score(self):
"""
max_score = None
score = None
if self.is_scored and self.weight is not None:

#The old default was None, so set to 1 if it is the old default weight
weight = self.weight
if weight is None:
weight = 1
if self.is_scored:
# Finds the maximum score of all student attempts and keeps it.
score_mat = []
for i in xrange(0, len(self.task_states)):
Expand All @@ -739,7 +717,7 @@ def get_score(self):
for z in xrange(0, len(score)):
if score[z] is None:
score[z] = 0
score[z] *= float(self.weight)
score[z] *= float(weight)
score_mat.append(score)

if len(score_mat) > 0:
Expand All @@ -753,7 +731,7 @@ def get_score(self):

if max_score is not None:
# Weight the max score if it is not None
max_score *= float(self.weight)
max_score *= float(weight)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Float guarantees that the value is either a float or None; so, no need to cast.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually isn't an xmodule, and self.weight isn't an xblock field, so it could theoretically be anything.

else:
# Without a max_score, we cannot have a score!
score = None
Expand Down
80 changes: 44 additions & 36 deletions common/lib/xmodule/xmodule/peer_grading_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .x_module import XModule
from xmodule.raw_module import RawDescriptor
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from .timeinfo import TimeInfo
from xblock.core import Dict, String, Scope, Boolean, Integer, Float
from xmodule.fields import Date
Expand All @@ -19,36 +20,37 @@

log = logging.getLogger(__name__)

USE_FOR_SINGLE_LOCATION = False
LINK_TO_LOCATION = ""
MAX_SCORE = 1
IS_GRADED = False

EXTERNAL_GRADER_NO_CONTACT_ERROR = "Failed to contact external graders. Please notify course staff."


class PeerGradingFields(object):
use_for_single_location = Boolean(
display_name="Show Single Problem",
help='When True, only the single problem specified by "Link to Problem Location" is shown. '
'When False, a panel is displayed with all problems available for peer grading.',
default=USE_FOR_SINGLE_LOCATION, scope=Scope.settings
default=False,
scope=Scope.settings
)
link_to_location = String(
display_name="Link to Problem Location",
help='The location of the problem being graded. Only used when "Show Single Problem" is True.',
default=LINK_TO_LOCATION, scope=Scope.settings
default="",
scope=Scope.settings
)
is_graded = Boolean(
graded = Boolean(
display_name="Graded",
help='Defines whether the student gets credit for grading this problem. Only used when "Show Single Problem" is True.',
default=IS_GRADED, scope=Scope.settings
default=False,
scope=Scope.settings
)
due_date = Date(help="Due date that should be displayed.", default=None, scope=Scope.settings)
grace_period_string = String(help="Amount of grace to give on the due date.", default=None, scope=Scope.settings)
max_grade = Integer(
help="The maximum grade that a student can receive for this problem.", default=MAX_SCORE,
scope=Scope.settings, values={"min": 0}
due = Date(
help="Due date that should be displayed.",
default=None,
scope=Scope.settings)
grace_period_string = String(
help="Amount of grace to give on the due date.",
default=None,
scope=Scope.settings
)
student_data_for_location = Dict(
help="Student data for a given peer grading problem.",
Expand All @@ -57,7 +59,8 @@ class PeerGradingFields(object):
weight = Float(
display_name="Problem Weight",
help="Defines the number of points each problem is worth. If the value is not set, each problem is worth one point.",
scope=Scope.settings, values={"min": 0, "step": ".1"}
scope=Scope.settings, values={"min": 0, "step": ".1"},
default=1
)
display_name = String(
display_name="Display Name",
Expand Down Expand Up @@ -98,35 +101,31 @@ def __init__(self, *args, **kwargs):
if self.use_for_single_location:
try:
self.linked_problem = modulestore().get_instance(self.system.course_id, self.link_to_location)
except:
except ItemNotFoundError:
log.error("Linked location {0} for peer grading module {1} does not exist".format(
self.link_to_location, self.location))
raise
due_date = self.linked_problem._model_data.get('peer_grading_due', None)
due_date = self.linked_problem._model_data.get('due', None)
if due_date:
self._model_data['due'] = due_date

try:
self.timeinfo = TimeInfo(self.due_date, self.grace_period_string)
except:
log.error("Error parsing due date information in location {0}".format(location))
self.timeinfo = TimeInfo(self.due, self.grace_period_string)
except Exception:
log.error("Error parsing due date information in location {0}".format(self.location))
raise

self.display_due_date = self.timeinfo.display_due_date

try:
self.student_data_for_location = json.loads(self.student_data_for_location)
except:
except Exception:
pass

self.ajax_url = self.system.ajax_url
if not self.ajax_url.endswith("/"):
self.ajax_url = self.ajax_url + "/"

# Integer could return None, so keep this check.
if not isinstance(self.max_grade, int):
raise TypeError("max_grade needs to be an integer.")

def closed(self):
return self._closed(self.timeinfo)

Expand Down Expand Up @@ -210,11 +209,16 @@ def get_progress(self):
def get_score(self):
max_score = None
score = None
weight = self.weight

#The old default was None, so set to 1 if it is the old default weight
if weight is None:
weight = 1
score_dict = {
'score': score,
'total': max_score,
}
if not self.use_for_single_location or not self.is_graded:
if not self.use_for_single_location or not self.graded:
return score_dict

try:
Expand All @@ -234,11 +238,10 @@ def get_score(self):
# Ensures that once a student receives a final score for peer grading, that it does not change.
self.student_data_for_location = response

if self.weight is not None:
score = int(count_graded >= count_required and count_graded > 0) * float(self.weight)
total = self.max_grade * float(self.weight)
score_dict['score'] = score
score_dict['total'] = total
score = int(count_graded >= count_required and count_graded > 0) * float(weight)
total = float(weight)
score_dict['score'] = score
score_dict['total'] = total

return score_dict

Expand All @@ -249,8 +252,8 @@ def max_score(self):
randomization, and 5/7 on another
'''
max_grade = None
if self.use_for_single_location and self.is_graded:
max_grade = self.max_grade
if self.use_for_single_location and self.graded:
max_grade = self.weight
return max_grade

def get_next_submission(self, data):
Expand Down Expand Up @@ -530,7 +533,7 @@ def _find_corresponding_module_for_location(location):
problem_location = problem['location']
descriptor = _find_corresponding_module_for_location(problem_location)
if descriptor:
problem['due'] = descriptor._model_data.get('peer_grading_due', None)
problem['due'] = descriptor._model_data.get('due', None)
grace_period_string = descriptor._model_data.get('graceperiod', None)
try:
problem_timeinfo = TimeInfo(problem['due'], grace_period_string)
Expand Down Expand Up @@ -617,9 +620,14 @@ class PeerGradingDescriptor(PeerGradingFields, RawDescriptor):
#Specify whether or not to pass in open ended interface
needs_open_ended_interface = True

metadata_translations = {
'is_graded': 'graded',
'attempts': 'max_attempts',
'due_data' : 'due'
}

@property
def non_editable_metadata_fields(self):
non_editable_fields = super(PeerGradingDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([PeerGradingFields.due_date, PeerGradingFields.grace_period_string,
PeerGradingFields.max_grade])
non_editable_fields.extend([PeerGradingFields.due, PeerGradingFields.grace_period_string])
return non_editable_fields
Loading