diff --git a/djangoapps/courseware/capa/inputtypes.py b/djangoapps/courseware/capa/inputtypes.py index 710ff04493..09255d14e9 100644 --- a/djangoapps/courseware/capa/inputtypes.py +++ b/djangoapps/courseware/capa/inputtypes.py @@ -253,16 +253,19 @@ def textbox(element, value, status, msg=''): The textbox is used for code input. The message is the return HTML string from evaluating the code, eg error messages, and output from the code tests. - TODO: make this use rows and cols attribs, not size ''' eid=element.get('id') count = int(eid.split('_')[-2])-1 # HACK size = element.get('size') + rows = element.get('rows') or '30' + cols = element.get('cols') or '80' mode = element.get('mode') or 'python' # mode for CodeMirror, eg "python" or "xml" linenumbers = element.get('linenumbers') # for CodeMirror if not value: value = element.text # if no student input yet, then use the default input given by the problem context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size, 'msg':msg, - 'mode':mode, 'linenumbers':linenumbers } + 'mode':mode, 'linenumbers':linenumbers, + 'rows':rows, 'cols':cols, + } html=render_to_string("textbox.html", context) return etree.XML(html) diff --git a/djangoapps/courseware/capa/responsetypes.py b/djangoapps/courseware/capa/responsetypes.py index 748e37f0bd..1fcd7b6625 100644 --- a/djangoapps/courseware/capa/responsetypes.py +++ b/djangoapps/courseware/capa/responsetypes.py @@ -471,13 +471,55 @@ class SymbolicResponse(CustomResponse): #----------------------------------------------------------------------------- class ExternalResponse(GenericResponse): - """ - Grade the student's input using an external server. + ''' + Grade the students input using an external server. Typically used by coding problems. - """ + + Example: + + + + + + + ''' def __init__(self, xml, context, system=None): self.xml = xml + self.url = xml.get('url') or "http://eecs1.mit.edu:8889/pyloncapa" # FIXME - hardcoded URL self.answer_ids = xml.xpath('//*[@id=$id]//textbox/@id|//*[@id=$id]//textline/@id', id=xml.get('id')) self.context = context @@ -490,24 +532,29 @@ class ExternalResponse(GenericResponse): else: self.code = answer.text - self.tests = xml.get('answer') + self.tests = xml.get('tests') def get_score(self, student_answers): - submission = [student_answers[k] for k in sorted(self.answer_ids)] + try: + submission = [student_answers[k] for k in sorted(self.answer_ids)] + except Exception,err: + log.error('Error %s: cannot get student answer for %s; student_answers=%s' % (err,self.answer_ids,student_answers)) + raise Exception,err + self.context.update({'submission':submission}) xmlstr = etree.tostring(self.xml, pretty_print=True) payload = {'xml': xmlstr, - ### Question: Is this correct/what we want? Shouldn't this be a json.dumps? - 'LONCAPA_student_response': ''.join(submission), - 'LONCAPA_correct_answer': self.tests, + 'edX_cmd' : 'get_score', + 'edX_student_response': json.dumps(submission), + 'edX_tests': self.tests, 'processor' : self.code, } - # call external server; TODO: get URL from settings.py - r = requests.post("http://eecs1.mit.edu:8889/pyloncapa",data=payload) + r = requests.post(self.url,data=payload) # call external server + if settings.DEBUG: log.info('response = %s' % r.text) rxml = etree.fromstring(r.text) # response is XML; prase it ad = rxml.find('awarddetail').text admap = {'EXACT_ANS':'correct', # TODO: handle other loncapa responses @@ -520,15 +567,32 @@ class ExternalResponse(GenericResponse): # self.context['correct'] = ['correct','correct'] correct_map = dict(zip(sorted(self.answer_ids), self.context['correct'])) - # TODO: separate message for each answer_id? - correct_map['msg'] = rxml.find('message').text.replace(' ',' ') # store message in correct_map + # store message in correct_map + correct_map['msg_%s' % self.answer_ids[0]] = rxml.find('message').text.replace(' ',' ') return correct_map def get_answers(self): - # Since this is explicitly specified in the problem, this will - # be handled by capa_problem - return {} + ''' + Use external server to get expected answers + ''' + xmlstr = etree.tostring(self.xml, pretty_print=True) + + payload = {'xml': xmlstr, + 'edX_cmd' : 'get_answers', + 'edX_tests': self.tests, + 'processor' : self.code, + } + + r = requests.post(self.url,data=payload) # call external server + + if settings.DEBUG: log.info('response = %s' % r.text) + rxml = etree.fromstring(r.text) # response is XML; prase it + exans = json.loads(rxml.find('expected').text) + if not (len(exans)==len(self.answer_ids)): + log.error('Expected %d answers from external server, only got %d!' % (len(self.answer_ids),len(exans))) + raise Exception,'Short response from external server' + return dict(zip(self.answer_ids,exans)) class StudentInputError(Exception): pass diff --git a/djangoapps/courseware/modules/capa_module.py b/djangoapps/courseware/modules/capa_module.py index 54cb257440..e2ad68a41e 100644 --- a/djangoapps/courseware/modules/capa_module.py +++ b/djangoapps/courseware/modules/capa_module.py @@ -351,8 +351,15 @@ class Module(XModule): self.tracker('save_problem_check', event_info) + try: + html = self.get_problem_html(encapsulate=False) + except Exception,err: + log.error('failed to generate html, error %s' % err) + raise Exception,err + return json.dumps({'success': success, - 'contents': self.get_problem_html(encapsulate=False)}) + 'contents': html, + }) def save_problem(self, get): event_info = dict() diff --git a/templates/textbox.html b/templates/textbox.html index e6c147141c..d2e3bcc455 100644 --- a/templates/textbox.html +++ b/templates/textbox.html @@ -1,5 +1,5 @@
- +