Commit d2970777 by Victor Shnayder

merge in master

parents b0378915 f1542028
...@@ -380,6 +380,10 @@ class FileSubmission(InputTypeBase): ...@@ -380,6 +380,10 @@ class FileSubmission(InputTypeBase):
template = "filesubmission.html" template = "filesubmission.html"
tags = ['filesubmission'] tags = ['filesubmission']
# pulled out for testing
submitted_msg = ("Your file(s) have been submitted; as soon as your submission is"
" graded, this message will be replaced with the grader's feedback.")
def setup(self): def setup(self):
escapedict = {'"': '"'} escapedict = {'"': '"'}
self.allowed_files = json.dumps(self.xml.get('allowed_files', '').split()) self.allowed_files = json.dumps(self.xml.get('allowed_files', '').split())
...@@ -393,8 +397,7 @@ class FileSubmission(InputTypeBase): ...@@ -393,8 +397,7 @@ class FileSubmission(InputTypeBase):
if self.status == 'incomplete': if self.status == 'incomplete':
self.status = 'queued' self.status = 'queued'
self.queue_len = self.msg self.queue_len = self.msg
self.msg = 'Submitted to grader.' self.msg = FileSubmission.submitted_msg
def _get_render_context(self): def _get_render_context(self):
......
...@@ -1717,7 +1717,7 @@ class ImageResponse(LoncapaResponse): ...@@ -1717,7 +1717,7 @@ class ImageResponse(LoncapaResponse):
""" """
Handle student response for image input: the input is a click on an image, Handle student response for image input: the input is a click on an image,
which produces an [x,y] coordinate pair. The click is correct if it falls which produces an [x,y] coordinate pair. The click is correct if it falls
within a region specified. This region is nominally a rectangle. within a region specified. This region is a union of rectangles.
Lon-CAPA requires that each <imageresponse> has a <foilgroup> inside it. That Lon-CAPA requires that each <imageresponse> has a <foilgroup> inside it. That
doesn't make sense to me (Ike). Instead, let's have it such that <imageresponse> doesn't make sense to me (Ike). Instead, let's have it such that <imageresponse>
...@@ -1727,6 +1727,7 @@ class ImageResponse(LoncapaResponse): ...@@ -1727,6 +1727,7 @@ class ImageResponse(LoncapaResponse):
snippets = [{'snippet': '''<imageresponse> snippets = [{'snippet': '''<imageresponse>
<imageinput src="image1.jpg" width="200" height="100" rectangle="(10,10)-(20,30)" /> <imageinput src="image1.jpg" width="200" height="100" rectangle="(10,10)-(20,30)" />
<imageinput src="image2.jpg" width="210" height="130" rectangle="(12,12)-(40,60)" /> <imageinput src="image2.jpg" width="210" height="130" rectangle="(12,12)-(40,60)" />
<imageinput src="image2.jpg" width="210" height="130" rectangle="(10,10)-(20,30);(12,12)-(40,60)" />
</imageresponse>'''}] </imageresponse>'''}]
response_tag = 'imageresponse' response_tag = 'imageresponse'
...@@ -1743,20 +1744,10 @@ class ImageResponse(LoncapaResponse): ...@@ -1743,20 +1744,10 @@ class ImageResponse(LoncapaResponse):
for aid in self.answer_ids: # loop through IDs of <imageinput> fields in our stanza for aid in self.answer_ids: # loop through IDs of <imageinput> fields in our stanza
given = student_answers[aid] # this should be a string of the form '[x,y]' given = student_answers[aid] # this should be a string of the form '[x,y]'
correct_map.set(aid, 'incorrect')
if not given: # No answer to parse. Mark as incorrect and move on if not given: # No answer to parse. Mark as incorrect and move on
correct_map.set(aid, 'incorrect')
continue continue
# parse expected answer
# TODO: Compile regexp on file load
m = re.match('[\(\[]([0-9]+),([0-9]+)[\)\]]-[\(\[]([0-9]+),([0-9]+)[\)\]]',
expectedset[aid].strip().replace(' ', ''))
if not m:
msg = 'Error in problem specification! cannot parse rectangle in %s' % (
etree.tostring(self.ielements[aid], pretty_print=True))
raise Exception('[capamodule.capa.responsetypes.imageinput] ' + msg)
(llx, lly, urx, ury) = [int(x) for x in m.groups()]
# parse given answer # parse given answer
m = re.match('\[([0-9]+),([0-9]+)]', given.strip().replace(' ', '')) m = re.match('\[([0-9]+),([0-9]+)]', given.strip().replace(' ', ''))
if not m: if not m:
...@@ -1764,11 +1755,24 @@ class ImageResponse(LoncapaResponse): ...@@ -1764,11 +1755,24 @@ class ImageResponse(LoncapaResponse):
'error grading %s (input=%s)' % (aid, given)) 'error grading %s (input=%s)' % (aid, given))
(gx, gy) = [int(x) for x in m.groups()] (gx, gy) = [int(x) for x in m.groups()]
# answer is correct if (x,y) is within the specified rectangle # Check whether given point lies in any of the solution rectangles
if (llx <= gx <= urx) and (lly <= gy <= ury): solution_rectangles = expectedset[aid].split(';')
correct_map.set(aid, 'correct') for solution_rectangle in solution_rectangles:
else: # parse expected answer
correct_map.set(aid, 'incorrect') # TODO: Compile regexp on file load
m = re.match('[\(\[]([0-9]+),([0-9]+)[\)\]]-[\(\[]([0-9]+),([0-9]+)[\)\]]',
solution_rectangle.strip().replace(' ', ''))
if not m:
msg = 'Error in problem specification! cannot parse rectangle in %s' % (
etree.tostring(self.ielements[aid], pretty_print=True))
raise Exception('[capamodule.capa.responsetypes.imageinput] ' + msg)
(llx, lly, urx, ury) = [int(x) for x in m.groups()]
# answer is correct if (x,y) is within the specified rectangle
if (llx <= gx <= urx) and (lly <= gy <= ury):
correct_map.set(aid, 'correct')
break
return correct_map return correct_map
def get_answers(self): def get_answers(self):
......
...@@ -8,8 +8,14 @@ Hello</p></text> ...@@ -8,8 +8,14 @@ Hello</p></text>
<text>Click on the image where the top skier will stop momentarily if the top skier starts from rest.</text> <text>Click on the image where the top skier will stop momentarily if the top skier starts from rest.</text>
<imageinput src="/static/Physics801/Figures/Skier-conservation of energy.jpg" width="560" height="388" rectangle="(242,202)-(296,276)"/> <imageinput src="/static/Physics801/Figures/Skier-conservation of energy.jpg" width="560" height="388" rectangle="(242,202)-(296,276)"/>
<text>Click on the image where the lower skier will stop momentarily if the lower skier starts from rest.</text> <text>Click on the image where the lower skier will stop momentarily if the lower skier starts from rest.</text>
<imageinput src="/static/Physics801/Figures/Skier-conservation of energy.jpg" width="560" height="388" rectangle="(490,11)-(556,98);(242,202)-(296,276)"/>
<text>Click on either of the two positions as discussed previously.</text>
<imageinput src="/static/Physics801/Figures/Skier-conservation of energy.jpg" width="560" height="388" rectangle="(490,11)-(556,98);(242,202)-(296,276)"/>
<text>Click on either of the two positions as discussed previously.</text>
<imageinput src="/static/Physics801/Figures/Skier-conservation of energy.jpg" width="560" height="388" rectangle="(490,11)-(556,98);(242,202)-(296,276)"/>
<text>Click on either of the two positions as discussed previously.</text>
<hintgroup showoncorrect="no"> <hintgroup showoncorrect="no">
<text><p>Use conservation of energy.</p></text> <text><p>Use conservation of energy.</p></text>
</hintgroup> </hintgroup>
</imageresponse> </imageresponse>
</problem> </problem>
\ No newline at end of file
...@@ -210,13 +210,14 @@ class FileSubmissionTest(unittest.TestCase): ...@@ -210,13 +210,14 @@ class FileSubmissionTest(unittest.TestCase):
state = {'value': 'BumbleBee.py', state = {'value': 'BumbleBee.py',
'status': 'incomplete', 'status': 'incomplete',
'feedback' : {'message': '3'}, } 'feedback' : {'message': '3'}, }
the_input = lookup_tag('filesubmission')(test_system, element, state) input_class = lookup_tag('filesubmission')
the_input = input_class(test_system, element, state)
context = the_input._get_render_context() context = the_input._get_render_context()
expected = {'id': 'prob_1_2', expected = {'id': 'prob_1_2',
'status': 'queued', 'status': 'queued',
'msg': 'Submitted to grader.', 'msg': input_class.submitted_msg,
'value': 'BumbleBee.py', 'value': 'BumbleBee.py',
'queue_len': '3', 'queue_len': '3',
'allowed_files': esc('["runme.py", "nooooo.rb", "ohai.java"]'), 'allowed_files': esc('["runme.py", "nooooo.rb", "ohai.java"]'),
......
...@@ -53,12 +53,22 @@ class ImageResponseTest(unittest.TestCase): ...@@ -53,12 +53,22 @@ class ImageResponseTest(unittest.TestCase):
imageresponse_file = os.path.dirname(__file__) + "/test_files/imageresponse.xml" imageresponse_file = os.path.dirname(__file__) + "/test_files/imageresponse.xml"
test_lcp = lcp.LoncapaProblem(open(imageresponse_file).read(), '1', system=test_system) test_lcp = lcp.LoncapaProblem(open(imageresponse_file).read(), '1', system=test_system)
correct_answers = {'1_2_1': '(490,11)-(556,98)', correct_answers = {'1_2_1': '(490,11)-(556,98)',
'1_2_2': '(242,202)-(296,276)'} '1_2_2': '(242,202)-(296,276)',
'1_2_3': '(490,11)-(556,98);(242,202)-(296,276)',
'1_2_4': '(490,11)-(556,98);(242,202)-(296,276)',
'1_2_5': '(490,11)-(556,98);(242,202)-(296,276)',
}
test_answers = {'1_2_1': '[500,20]', test_answers = {'1_2_1': '[500,20]',
'1_2_2': '[250,300]', '1_2_2': '[250,300]',
'1_2_3': '[500,20]',
'1_2_4': '[250,250]',
'1_2_5': '[10,10]',
} }
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_1'), 'correct') self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_1'), 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_2'), 'incorrect') self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_2'), 'incorrect')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_3'), 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_4'), 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_5'), 'incorrect')
class SymbolicResponseTest(unittest.TestCase): class SymbolicResponseTest(unittest.TestCase):
......
...@@ -120,6 +120,8 @@ class CapaModule(XModule): ...@@ -120,6 +120,8 @@ class CapaModule(XModule):
self.show_answer = self.metadata.get('showanswer', 'closed') self.show_answer = self.metadata.get('showanswer', 'closed')
self.force_save_button = self.metadata.get('force_save_button', 'false')
if self.show_answer == "": if self.show_answer == "":
self.show_answer = "closed" self.show_answer = "closed"
...@@ -320,9 +322,10 @@ class CapaModule(XModule): ...@@ -320,9 +322,10 @@ class CapaModule(XModule):
if not self.lcp.done: if not self.lcp.done:
reset_button = False reset_button = False
# We don't need a "save" button if infinite number of attempts and # We may not need a "save" button if infinite number of attempts and
# non-randomized # non-randomized. The problem author can force it. It's a bit weird for
if self.max_attempts is None and self.rerandomize != "always": # randomization to control this; should perhaps be cleaned up.
if (self.force_save_button == "false") and (self.max_attempts is None and self.rerandomize != "always"):
save_button = False save_button = False
context = {'problem': content, context = {'problem': content,
......
...@@ -190,7 +190,7 @@ case `uname -s` in ...@@ -190,7 +190,7 @@ case `uname -s` in
} }
distro=`lsb_release -cs` distro=`lsb_release -cs`
case $distro in case $distro in
maya|lisa|natty|oneiric|precise) maya|lisa|natty|oneiric|precise|quantal)
output "Installing ubuntu requirements" output "Installing ubuntu requirements"
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install $APT_PKGS sudo apt-get -y install $APT_PKGS
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment