diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 1445c47c23..8205a1ee4d 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -14,6 +14,7 @@ from static_replace import replace_urls from mitxmako.shortcuts import render_to_response, render_to_string from xmodule.modulestore.django import modulestore +from xmodule_modifiers import replace_static_urls # ==== Public views ================================================== @@ -167,8 +168,21 @@ def get_sample_module(location): """ descriptor = modulestore().get_item(location) instance_state, shared_state = descriptor.get_sample_state()[0] + return load_sample_module(descriptor, instance_state, shared_state) + + +def load_sample_module(descriptor, instance_state, shared_state): + """ + Return a sample XModule instantiated from the supplied descriptor, instance_state, and shared_state + + descriptor: An XModuleDescriptor + instance_state: An instance state string + shared_state: A shared state string + """ system = sample_module_system(descriptor) module = descriptor.xmodule_constructor(system)(instance_state, shared_state) + module.get_html = replace_static_urls(module.get_html, module.metadata['data_dir']) + return module @@ -180,9 +194,8 @@ def get_module_previews(descriptor): descriptor: An XModuleDescriptor """ preview_html = [] - system = sample_module_system(descriptor) for instance_state, shared_state in descriptor.get_sample_state(): - module = descriptor.xmodule_constructor(system)(instance_state, shared_state) + module = load_sample_module(descriptor, instance_state, shared_state) preview_html.append(module.get_html()) return preview_html diff --git a/cms/envs/common.py b/cms/envs/common.py index 60d47a7a4f..148a1205f7 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -155,6 +155,13 @@ STATICFILES_DIRS = [ # This is how you would use the textbook images locally # ("book", ENV_ROOT / "book_images") ] +if os.path.isdir(GITHUB_REPO_ROOT): + STATICFILES_DIRS += [ + # TODO (cpennington): When courses aren't loaded from github, remove this + (course_dir, GITHUB_REPO_ROOT / course_dir) + for course_dir in os.listdir(GITHUB_REPO_ROOT) + if os.path.isdir(GITHUB_REPO_ROOT / course_dir) + ] # Locale/Internationalization TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name diff --git a/common/djangoapps/xmodule_modifiers.py b/common/djangoapps/xmodule_modifiers.py new file mode 100644 index 0000000000..a8ef8b5452 --- /dev/null +++ b/common/djangoapps/xmodule_modifiers.py @@ -0,0 +1,79 @@ +import json +from django.conf import settings +from functools import wraps +from static_replace import replace_urls +from mitxmako.shortcuts import render_to_string + + +def replace_static_urls(original, prefix): + """ + 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//... + """ + + @wraps(original) + def get_html(): + return replace_urls(original(), staticfiles_prefix=prefix) + return get_html + + +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? + if len(grades) == 1 and grades[0][0] is None: + return [] + return grades + + +def add_histogram(original, module): + """ + 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 + """ + @wraps(original) + def get_html(): + module_id = module.id + histogram = grade_histogram(module_id) + render_histogram = len(histogram) > 0 + + # TODO: fixme - no filename in module.xml in general (this code block for edx4edx) + # the following if block is for summer 2012 edX course development; it will change when the CMS comes online + if settings.MITX_FEATURES.get('DISPLAY_EDIT_LINK') and settings.DEBUG and module_xml.get('filename') is not None: + coursename = multicourse_settings.get_coursename_from_request(request) + github_url = multicourse_settings.get_course_github_url(coursename) + fn = module_xml.get('filename') + if module_xml.tag=='problem': fn = 'problems/' + fn # grrr + edit_link = (github_url + '/tree/master/' + fn) if github_url is not None else None + if module_xml.tag=='problem': edit_link += '.xml' # grrr + else: + edit_link = False + + # Cast module.definition and module.metadata to dicts so that json can dump them + # even though they are lazily loaded + staff_context = {'definition': json.dumps(dict(module.definition), indent=4), + 'metadata': json.dumps(dict(module.metadata), indent=4), + 'element_id': module.location.html_id(), + 'edit_link': edit_link, + 'histogram': json.dumps(histogram), + 'render_histogram': render_histogram, + 'module_content': original()} + return render_to_string("staff_problem_info.html", staff_context) + + return get_html diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 982c9bde97..cb462d98e1 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -5,7 +5,6 @@ from django.conf import settings from django.http import Http404 from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt -from functools import wraps from django.contrib.auth.models import User from xmodule.modulestore.django import modulestore @@ -14,6 +13,7 @@ from models import StudentModule, StudentModuleCache from static_replace import replace_urls from xmodule.exceptions import NotFoundError from xmodule.x_module import ModuleSystem +from xmodule_modifiers import replace_static_urls, add_histogram log = logging.getLogger("mitx.courseware") @@ -30,28 +30,6 @@ def make_track_function(request): return f -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? - if len(grades) == 1 and grades[0][0] is None: - return [] - return grades - - def toc_for_course(user, request, course, active_chapter, active_section): ''' Create a table of contents from the module store @@ -180,11 +158,10 @@ def get_module(user, request, location, student_module_cache, position=None): module = descriptor.xmodule_constructor(system)(instance_state, shared_state) - replace_prefix = module.metadata['data_dir'] - module = replace_static_urls(module, replace_prefix) + module.get_html = replace_static_urls(module.get_html, module.metadata['data_dir']) if settings.MITX_FEATURES.get('DISPLAY_HISTOGRAMS_TO_STAFF') and user.is_staff: - module = add_histogram(module) + module.get_html = add_histogram(module.get_html) # If StudentModule for this instance wasn't already in the database, # and this isn't a guest user, create it. @@ -212,64 +189,6 @@ def get_module(user, request, location, student_module_cache, position=None): return (module, instance_module, shared_module, descriptor.category) -def replace_static_urls(module, prefix): - """ - 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//... - """ - original_get_html = module.get_html - - @wraps(original_get_html) - def get_html(): - return replace_urls(original_get_html(), staticfiles_prefix=prefix) - - module.get_html = get_html - return module - - -def add_histogram(module): - """ - 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 - """ - original_get_html = module.get_html - - @wraps(original_get_html) - def get_html(): - module_id = module.id - histogram = grade_histogram(module_id) - render_histogram = len(histogram) > 0 - - # TODO: fixme - no filename in module.xml in general (this code block for edx4edx) - # the following if block is for summer 2012 edX course development; it will change when the CMS comes online - if settings.MITX_FEATURES.get('DISPLAY_EDIT_LINK') and settings.DEBUG and module_xml.get('filename') is not None: - coursename = multicourse_settings.get_coursename_from_request(request) - github_url = multicourse_settings.get_course_github_url(coursename) - fn = module_xml.get('filename') - if module_xml.tag == 'problem': fn = 'problems/' + fn # grrr - edit_link = (github_url + '/tree/master/' + fn) if github_url is not None else None - if module_xml.tag == 'problem': edit_link += '.xml' # grrr - else: - edit_link = False - - # Cast module.definition and module.metadata to dicts so that json can dump them - # even though they are lazily loaded - staff_context = {'definition': json.dumps(dict(module.definition), indent=4), - 'metadata': json.dumps(dict(module.metadata), indent=4), - 'element_id': module.location.html_id(), - 'edit_link': edit_link, - 'histogram': json.dumps(histogram), - 'render_histogram': render_histogram, - 'module_content': original_get_html()} - return render_to_string("staff_problem_info.html", staff_context) - - module.get_html = get_html - return module - - # TODO: TEMPORARY BYPASS OF AUTH! @csrf_exempt def xqueue_callback(request, userid, id, dispatch):