module_render.py 5.85 KB
Newer Older
1
import logging
2

3 4 5
from lxml import etree

from django.http import Http404
6 7
from django.http import HttpResponse
from django.shortcuts import redirect
8
from django.template import Context
9
from django.template import Context, loader
Lyla Fischer committed
10 11 12

from fs.osfs import OSFS

13
from django.conf import settings
Calen Pennington committed
14
from mitxmako.shortcuts import render_to_string
15

16
from models import StudentModule
17
from multicourse import multicourse_settings
18

19
import courseware.modules
20 21 22

log = logging.getLogger("mitx.courseware")

Lyla Fischer committed
23
class I4xSystem(object):
24 25 26 27 28
    '''
    This is an abstraction such that x_modules can function independent 
    of the courseware (e.g. import into other types of courseware, LMS, 
    or if we want to have a sandbox server for user-contributed content)
    '''
Lyla Fischer committed
29 30 31
    def __init__(self, ajax_url, track_function, render_function, filestore=None):
        self.ajax_url = ajax_url
        self.track_function = track_function
32 33
        if not filestore: 
            self.filestore = OSFS(settings.DATA_DIR)
34 35
        else:
            self.filestore = filestore
Lyla Fischer committed
36 37
        self.render_function = render_function
        self.exception404 = Http404
38 39 40 41
    def __repr__(self):
        return repr(self.__dict__)
    def __str__(self):
        return str(self.__dict__)
Lyla Fischer committed
42

43 44 45 46 47 48 49 50 51 52
def object_cache(cache, user, module_type, module_id):
    # We don't look up on user -- all queries include user
    # Additional lookup would require a DB hit the way Django 
    # is broken. 
    for o in cache: 
        if o.module_type == module_type and \
                o.module_id == module_id:
            return o
    return None

53
def make_track_function(request):
Piotr Mitros committed
54 55 56 57 58 59
    ''' We want the capa problem (and other modules) to be able to
    track/log what happens inside them without adding dependencies on
    Django or the rest of the codebase. We do this by passing a
    tracking function to them. This generates a closure for each request 
    that gives a clean interface on both sides. 
    '''
60 61
    import track.views

62 63 64
    def f(event_type, event):
        return track.views.server_track(request, event_type, event, page='x_module')
    return f
65

Piotr Mitros committed
66
def grade_histogram(module_id):
Piotr Mitros committed
67 68 69
    ''' Print out a histogram of grades on a given problem. 
        Part of staff member debug info. 
    '''
70
    from django.db import connection
Piotr Mitros committed
71 72 73 74 75 76
    cursor = connection.cursor()

    cursor.execute("select courseware_studentmodule.grade,COUNT(courseware_studentmodule.student_id) from courseware_studentmodule where courseware_studentmodule.module_id=%s group by courseware_studentmodule.grade", [module_id])

    grades = list(cursor.fetchall())
    grades.sort(key=lambda x:x[0]) # Probably not necessary
77 78
    if (len(grades) == 1 and grades[0][0] == None):
        return []
Piotr Mitros committed
79 80
    return grades

81
def render_x_module(user, request, xml_module, module_object_preload):
82 83
    ''' Generic module for extensions. This renders to HTML. '''
    # Check if problem has an instance in DB
84
    module_type=xml_module.tag
85
    module_class=courseware.modules.get_module_class(module_type)
86
    module_id=xml_module.get('id') #module_class.id_attribute) or "" 
87 88

    # Grab state from database
89 90 91 92
    smod = object_cache(module_object_preload, 
                        user, 
                        module_type, 
                        module_id)
93

94
    if not smod: # If nothing in the database...
95 96 97 98
        state=None
    else:
        state = smod.state

99
    # get coursename if stored
100
    coursename = multicourse_settings.get_coursename_from_request(request)
101 102 103 104 105 106

    if coursename and settings.ENABLE_MULTICOURSE:
        xp = multicourse_settings.get_course_xmlpath(coursename)	# path to XML for the course
        data_root = settings.DATA_DIR + xp
    else:
        data_root = settings.DATA_DIR
107

108
    # Create a new instance
109
    ajax_url = settings.MITX_ROOT_URL + '/modx/'+module_type+'/'+module_id+'/'
110
    
Lyla Fischer committed
111 112 113
    system = I4xSystem(track_function = make_track_function(request), 
                       render_function = lambda x: render_module(user, request, x, module_object_preload), 
                       ajax_url = ajax_url,
114
                       filestore = OSFS(data_root),
Lyla Fischer committed
115 116 117
                       )
    instance=module_class(system, 
                          etree.tostring(xml_module), 
118
                          module_id, 
Lyla Fischer committed
119
                          state=state)
120
    
121 122 123
    # If instance wasn't already in the database, and this
    # isn't a guest user, create it
    if not smod and user.is_authenticated():
124
        smod=StudentModule(student=user, 
125 126
                           module_type = module_type,
                           module_id=module_id, 
127
                           state=instance.get_state())
128
        smod.save()
129
        module_object_preload.append(smod)
130

131
    # Grab content
Piotr Mitros committed
132
    content = instance.get_html()
133 134
    init_js = instance.get_init_js()
    destory_js = instance.get_destroy_js()
135 136

    # special extra information about each problem, only for users who are staff 
Piotr Mitros committed
137
    if user.is_staff:
138 139
        histogram = grade_histogram(module_id)
        render_histogram = len(histogram) > 0
Piotr Mitros committed
140
        content=content+render_to_string("staff_problem_info.html", {'xml':etree.tostring(xml_module), 
141 142 143 144 145
                                                                     'module_id' : module_id,
                                                                     'render_histogram' : render_histogram})
        if render_histogram:
            init_js = init_js+render_to_string("staff_problem_histogram.js", {'histogram' : histogram,
                                                                              'module_id' : module_id})
146
        
Piotr Mitros committed
147
    content = {'content':content, 
148 149
               "destroy_js":destory_js,
               'init_js':init_js, 
150
               'type':module_type}
151 152 153

    return content

154
def render_module(user, request, module, module_object_preload):
155
    ''' Generic dispatch for internal modules. '''
156
    if module==None :
157
        return {"content":""}
158
    return render_x_module(user, request, module, module_object_preload)