From 4e75a04ffacfee9cb056aacfedca86964b399b9c Mon Sep 17 00:00:00 2001 From: Piotr Mitros Date: Wed, 18 Jan 2012 14:17:06 -0500 Subject: [PATCH] Problem tracking appears to work --- courseware/capa_module.py | 62 +++++++++++++++++++++++++++---------- courseware/html_module.py | 4 +-- courseware/module_render.py | 12 +++++-- courseware/video_module.py | 4 +-- courseware/x_module.py | 6 +++- 5 files changed, 65 insertions(+), 23 deletions(-) diff --git a/courseware/capa_module.py b/courseware/capa_module.py index 1286b63b8d..e13ee453fe 100644 --- a/courseware/capa_module.py +++ b/courseware/capa_module.py @@ -108,8 +108,8 @@ class LoncapaModule(XModule): return html - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state) + def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None): + XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function) self.attempts = 0 self.max_attempts = None @@ -222,22 +222,32 @@ class LoncapaModule(XModule): def check_problem(self, get): ''' Checks whether answers to a problem are correct, and returns a map of correct/incorrect answers''' + event_info = dict() + event_info['state'] = self.lcp.get_state() + event_info['filename'] = self.filename + + answers=dict() + # input_resistor_1 ==> resistor_1 + for key in get: + answers['_'.join(key.split('_')[1:])]=get[key] + + event_info['answers']=answers + # Too late. Cannot submit if self.closed(): + event_info['failure']='closed' + self.tracker('save_problem_check_fail', event_info) print "cp" raise Http404 # Problem submitted. Student should reset before checking # again. if self.lcp.done and self.rerandomize: + event_info['failure']='unreset' + self.tracker('save_problem_check_fail', event_info) print "cpdr" raise Http404 - answers=dict() - # input_resistor_1 ==> resistor_1 - for key in get: - answers['_'.join(key.split('_')[1:])]=get[key] - try: old_state = self.lcp.get_state() lcp_id = self.lcp.problem_id @@ -262,35 +272,56 @@ class LoncapaModule(XModule): js=json.dumps({'correct_map' : correct_map, 'success' : success}) + event_info['correct_map']=correct_map + event_info['success']=success + + self.tracker('save_problem_check', event_info) + return js def save_problem(self, get): + event_info = dict() + event_info['state'] = self.lcp.get_state() + event_info['filename'] = self.filename + + answers=dict() + for key in get: + answers['_'.join(key.split('_')[1:])]=get[key] + event_info['answers'] = answers + # Too late. Cannot submit if self.closed(): - print "sp" + event_info['failure']='closed' + self.tracker('save_problem_fail', event_info) return "Problem is closed" # Problem submitted. Student should reset before saving # again. if self.lcp.done and self.rerandomize: - print "spdr" + event_info['failure']='done' + self.tracker('save_problem_fail', event_info) return "Problem needs to be reset prior to save." - answers=dict() - for key in get: - answers['_'.join(key.split('_')[1:])]=get[key] - self.lcp.student_answers=answers + self.tracker('save_problem_fail', event_info) return json.dumps({'success':True}) def reset_problem(self, get): ''' Changes problem state to unfinished -- removes student answers, and causes problem to rerender itself. ''' + event_info = dict() + event_info['old_state']=self.lcp.get_state() + event_info['filename']=self.filename + if self.closed(): + event_info['failure']='closed' + self.tracker('reset_problem_fail', event_info) return "Problem is closed" if not self.lcp.done: + event_info['failure']='not_done' + self.tracker('reset_problem_fail', event_info) return "Refresh the page and make an attempt before resetting." self.lcp.done=False @@ -307,8 +338,7 @@ class LoncapaModule(XModule): filename=settings.DATA_DIR+"problems/"+self.filename+".xml" self.lcp=LoncapaProblem(filename, self.item_id, self.lcp.get_state()) - event_info = self.lcp.get_state() - event_info.update({'filename':filename}) - #server_track(request, 'reset_problem', event_info) + event_info['new_state']=self.lcp.get_state() + self.tracker('reset_problem', event_info) return json.dumps(self.get_problem_html(encapsulate=False)) diff --git a/courseware/html_module.py b/courseware/html_module.py index ceadb2fd8b..853a322236 100644 --- a/courseware/html_module.py +++ b/courseware/html_module.py @@ -25,8 +25,8 @@ class HtmlModule(XModule): textlist=[i for i in textlist if type(i)==str] return "".join(textlist) - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state) + def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None): + XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function) xmltree=etree.fromstring(xml) self.filename = None filename_l=xmltree.xpath("/html/@filename") diff --git a/courseware/module_render.py b/courseware/module_render.py index 97ca31c0c1..58574df190 100644 --- a/courseware/module_render.py +++ b/courseware/module_render.py @@ -10,6 +10,7 @@ from auth.models import UserProfile from django.shortcuts import redirect import StringIO +import track.views from django.http import Http404 @@ -35,6 +36,11 @@ modx_modules={'problem':capa_module.LoncapaModule, 'html':html_module.HtmlModule, 'schematic':schematic_module.SchematicModule} +def make_track_function(request): + def f(event_type, event): + return track.views.server_track(request, event_type, event, page='x_module') + return f + def modx_dispatch(request, module=None, dispatch=None, id=None): ''' Generic view for extensions. ''' # Grab the student information for the module from the database @@ -60,7 +66,8 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): instance=modx_modules[module](xml, s.module_id, ajax_url=ajax_url, - state=s.state) + state=s.state, + track_function = make_track_function(request)) # Let the module handle the AJAX ajax_return=instance.handle_ajax(dispatch, request.POST) # Save the state back to the database @@ -153,7 +160,8 @@ def render_x_module(request, xml_module): instance=module_class(xml_module.toxml(), module_id, ajax_url=ajax_url, - state=state) + state=state, + track_function = make_track_function(request)) # If instance wasn't already in the database, create it if len(s) == 0: diff --git a/courseware/video_module.py b/courseware/video_module.py index e94edb428b..0fdb54f119 100644 --- a/courseware/video_module.py +++ b/courseware/video_module.py @@ -46,8 +46,8 @@ class VideoModule(XModule): def get_destroy_js(self): return "videoDestroy();" - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None): - XModule.__init__(self, xml, item_id, ajax_url, track_url, state) + def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None): + XModule.__init__(self, xml, item_id, ajax_url, track_url, state, track_function) print state if state!=None and "time" not in json.loads(state): self.video_time = 0 diff --git a/courseware/x_module.py b/courseware/x_module.py index c9ece2816f..2b79519d20 100644 --- a/courseware/x_module.py +++ b/courseware/x_module.py @@ -1,3 +1,6 @@ +def dummy_track(event_type, event): + pass + class XModule(object): ''' Implements a generic learning module. Initialized on access with __init__, first time with state=None, and @@ -36,10 +39,11 @@ class XModule(object): get is a dictionary-like object ''' return "" - def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None): + def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None, track_function=None): ''' In most cases, you must pass state or xml''' self.xml=xml self.item_id=item_id self.ajax_url=ajax_url self.track_url=track_url self.state=state + self.tracker=track_function