xmodule_modifiers.py 5.9 KB
Newer Older
1
import re
2
import json
3
import logging
ichuang committed
4
import time
5

6 7 8 9
from django.conf import settings
from functools import wraps
from static_replace import replace_urls
from mitxmako.shortcuts import render_to_string
10 11
from xmodule.seq_module import SequenceModule
from xmodule.vertical_module import VerticalModule
12

13
log = logging.getLogger("mitx.xmodule_modifiers")
14

15 16 17 18 19 20 21 22 23
def wrap_xmodule(get_html, module, template):
    """
    Wraps the results of get_html in a standard <section> with identifying
    data so that the appropriate javascript module can be loaded onto it.

    get_html: An XModule.get_html method or an XModuleDescriptor.get_html method
    module: An XModule
    template: A template that takes the variables:
        content: the results of get_html,
24
        class_: the module class name
25 26 27 28 29 30 31
        module_name: the js_module_name of the module
    """

    @wraps(get_html)
    def _get_html():
        return render_to_string(template, {
            'content': get_html(),
32
            'class_': module.__class__.__name__,
33 34 35 36 37
            'module_name': module.js_module_name
        })
    return _get_html


38
def replace_course_urls(get_html, course_id):
39 40 41 42 43 44 45 46 47 48
    """
    Updates the supplied module with a new get_html function that wraps
    the old get_html function and substitutes urls of the form /course/...
    with urls that are /courses/<course_id>/...
    """
    @wraps(get_html)
    def _get_html():
        return replace_urls(get_html(), staticfiles_prefix='/courses/'+course_id, replace_prefix='/course/')
    return _get_html

49
def replace_static_urls(get_html, prefix):
50 51 52 53 54 55
    """
    Updates the supplied module with a new get_html function that wraps
    the old get_html function and substitutes urls of the form /static/...
    with urls that are /static/<prefix>/...
    """

56 57 58 59
    @wraps(get_html)
    def _get_html():
        return replace_urls(get_html(), staticfiles_prefix=prefix)
    return _get_html
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78


def grade_histogram(module_id):
    ''' Print out a histogram of grades on a given problem.
        Part of staff member debug info.
    '''
    from django.db import connection
    cursor = connection.cursor()

    q = """SELECT courseware_studentmodule.grade,
                  COUNT(courseware_studentmodule.student_id)
    FROM courseware_studentmodule
    WHERE courseware_studentmodule.module_id=%s
    GROUP BY courseware_studentmodule.grade"""
    # Passing module_id this way prevents sql-injection.
    cursor.execute(q, [module_id])

    grades = list(cursor.fetchall())
    grades.sort(key=lambda x: x[0])          # Add ORDER BY to sql query?
79
    if len(grades) >= 1 and grades[0][0] is None:
80 81 82 83
        return []
    return grades


84
def add_histogram(get_html, module, user):
85 86 87 88 89
    """
    Updates the supplied module with a new get_html function that wraps
    the output of the old get_html function with additional information
    for admin users only, including a histogram of student answers and the
    definition of the xmodule
90

91
    Does nothing if module is a SequenceModule or a VerticalModule.
92
    """
93 94
    @wraps(get_html)
    def _get_html():
95 96 97 98

        if type(module) in [SequenceModule, VerticalModule]:	# TODO: make this more general, eg use an XModule attribute instead
            return get_html()

99 100 101 102
        module_id = module.id
        histogram = grade_histogram(module_id)
        render_histogram = len(histogram) > 0

103 104
        # TODO (ichuang): Remove after fall 2012 LMS migration done
        if settings.MITX_FEATURES.get('ENABLE_LMS_MIGRATION'):
Victor Shnayder committed
105
            [filepath, filename] = module.definition.get('filename', ['', None])
106
            osfs = module.system.filestore
107
            if filename is not None and osfs.exists(filename):
Victor Shnayder committed
108 109 110
                # if original, unmangled filename exists then use it (github
                # doesn't like symlinks)
                filepath = filename
111
            data_dir = osfs.root_path.rsplit('/')[-1]
112 113
            giturl = module.metadata.get('giturl','https://github.com/MITx')
            edit_link = "%s/%s/tree/master/%s" % (giturl,data_dir,filepath)
114 115
        else:
            edit_link = False
116 117 118
            # Need to define all the variables that are about to be used
            giturl = ""
            data_dir = ""
119
        source_file = module.metadata.get('source_file','')	# source used to generate the problem XML, eg latex or word
120

ichuang committed
121
        # useful to indicate to staff if problem has been released or not
122
        # TODO (ichuang): use _has_access_descriptor.can_load in lms.courseware.access, instead of now>mstart comparison here
ichuang committed
123 124
        now = time.gmtime()
        is_released = "unknown"
125 126 127
        mstart = getattr(module.descriptor,'start')
        if mstart is not None:
            is_released = "<font color='red'>Yes!</font>" if (now > mstart) else "<font color='green'>Not yet</font>"
128

129
        staff_context = {'definition': module.definition.get('data'),
130
                         'metadata': json.dumps(module.metadata, indent=4),
131 132
                         'location': module.location,
                         'xqa_key': module.metadata.get('xqa_key',''),
133 134
                         'source_file' : source_file,
                         'source_url': '%s/%s/tree/master/%s' % (giturl,data_dir,source_file),
135
                         'category': str(module.__class__.__name__),
136
                         # Template uses element_id in js function names, so can't allow dashes
137
                         'element_id': module.location.html_id().replace('-','_'),
138
                         'edit_link': edit_link,
139
                         'user': user,
140
                         'xqa_server' : settings.MITX_FEATURES.get('USE_XQA_SERVER','http://xqa:server@content-qa.mitx.mit.edu/xqa'),
141 142
                         'histogram': json.dumps(histogram),
                         'render_histogram': render_histogram,
ichuang committed
143 144 145
                         'module_content': get_html(),
                         'is_released': is_released,
                         }
146 147
        return render_to_string("staff_problem_info.html", staff_context)

148
    return _get_html
149