From 32a5fc0e5459f891ac1c52c7fe60b349b5c52329 Mon Sep 17 00:00:00 2001 From: Chris Rossi Date: Wed, 8 Jan 2014 15:39:35 -0500 Subject: [PATCH] Make problem types pluggable. This patch adds the ability for arbitrary packages to register themselves with xmodule.x_module.ResourceTemplates as being providers of boiler plate templates used for defining the types of problems instructors can add to a course. This allows third party add-ons to define new problem types that can then be available to instructors to use. This patch is the result of discussion with Ned Batchelder on the right way to approach this problem. The solution provided here is, admittedly, a little bit of a hack. But the focus was on making a least invasive fix to make something work, in anticipation that when problems get moved into the newere XBlock architecture something a little nicer will be in place. --- .../tests/templates/test/announcement.yaml | 23 ++++++++ .../tests/templates/test/anon_user_id.yaml | 19 +++++++ .../tests/templates/test/latex_html.yaml | 21 +++++++ .../tests/templates/test/somefile.notyaml | 0 .../tests/templates/test/zooming_image.yaml | 26 +++++++++ .../xmodule/tests/test_resource_templates.py | 56 +++++++++++++++++++ common/lib/xmodule/xmodule/x_module.py | 44 +++++++++------ 7 files changed, 171 insertions(+), 18 deletions(-) create mode 100644 common/lib/xmodule/xmodule/tests/templates/test/announcement.yaml create mode 100644 common/lib/xmodule/xmodule/tests/templates/test/anon_user_id.yaml create mode 100644 common/lib/xmodule/xmodule/tests/templates/test/latex_html.yaml create mode 100644 common/lib/xmodule/xmodule/tests/templates/test/somefile.notyaml create mode 100644 common/lib/xmodule/xmodule/tests/templates/test/zooming_image.yaml create mode 100644 common/lib/xmodule/xmodule/tests/test_resource_templates.py diff --git a/common/lib/xmodule/xmodule/tests/templates/test/announcement.yaml b/common/lib/xmodule/xmodule/tests/templates/test/announcement.yaml new file mode 100644 index 0000000000..25855c013a --- /dev/null +++ b/common/lib/xmodule/xmodule/tests/templates/test/announcement.yaml @@ -0,0 +1,23 @@ +--- +metadata: + display_name: Announcement +data: | +
    +
  1. +

    September 21

    +
    +
    +

    Words of encouragement! This is a short note that most students will read.

    +

    Anant Agarwal (6.002x Principal Instructor)

    +
    +

    Primary versus Secondary Updates:

    Unfortunately, the internet throws a lot of text at students, and they + do not read everything that they are given. However, many students do read all that they are + given, and so detailed explainations in this section will benefit the most concientious. + Any essential information should be extremely quickly summarized in the primary section for skimmers.

    +

    Star Forum Poster

    + Students appreciate knowing that the course staff is reading what they post, and one of several ways + that you can do this is by acknowledging the star posters in your announcements. +

    +
    +
  2. +
diff --git a/common/lib/xmodule/xmodule/tests/templates/test/anon_user_id.yaml b/common/lib/xmodule/xmodule/tests/templates/test/anon_user_id.yaml new file mode 100644 index 0000000000..cfba26a6b4 --- /dev/null +++ b/common/lib/xmodule/xmodule/tests/templates/test/anon_user_id.yaml @@ -0,0 +1,19 @@ +--- +metadata: + display_name: Anonymous User ID +data: | +

Your explanatory text here.

+

YOUR_LINK_TEXT

+ + diff --git a/common/lib/xmodule/xmodule/tests/templates/test/latex_html.yaml b/common/lib/xmodule/xmodule/tests/templates/test/latex_html.yaml new file mode 100644 index 0000000000..9163b3e8b9 --- /dev/null +++ b/common/lib/xmodule/xmodule/tests/templates/test/latex_html.yaml @@ -0,0 +1,21 @@ +--- +metadata: + display_name: E-text Written in LaTeX + source_code: | + \subsection{Example of E-text in LaTeX} + + It is very convenient to write complex equations in LaTeX. + + \begin{equation} + x = \frac{-b\pm\sqrt{b^2-4*a*c}}{2a} + \end{equation} + + Seize the moment. + +data: | + +

Example: E-text page

+

+ It is very convenient to write complex equations in LaTeX. +

+ diff --git a/common/lib/xmodule/xmodule/tests/templates/test/somefile.notyaml b/common/lib/xmodule/xmodule/tests/templates/test/somefile.notyaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/lib/xmodule/xmodule/tests/templates/test/zooming_image.yaml b/common/lib/xmodule/xmodule/tests/templates/test/zooming_image.yaml new file mode 100644 index 0000000000..dc79c75fff --- /dev/null +++ b/common/lib/xmodule/xmodule/tests/templates/test/zooming_image.yaml @@ -0,0 +1,26 @@ +--- +metadata: + display_name: Zooming Image +data: | +

ZOOMING DIAGRAMS

+

Some edX classes use extremely large, extremely detailed graphics. To make it easier to understand we can offer two versions of those graphics, with the zoomed section showing when you click on the main view.

+

The example below is from 7.00x: Introduction to Biology and shows a subset of the biochemical reactions that cells carry out.

+

You can view the chemical structures of the molecules by clicking on them. The magnified view also lists the enzymes involved in each step.

+ +
+ + magnify + +
+
+ +
+ diff --git a/common/lib/xmodule/xmodule/tests/test_resource_templates.py b/common/lib/xmodule/xmodule/tests/test_resource_templates.py new file mode 100644 index 0000000000..80d21d98ac --- /dev/null +++ b/common/lib/xmodule/xmodule/tests/test_resource_templates.py @@ -0,0 +1,56 @@ +""" +Tests for xmodule.x_module.ResourceTemplates +""" +import unittest + +from xmodule.x_module import ResourceTemplates + + +class ResourceTemplatesTests(unittest.TestCase): + """ + Tests for xmodule.x_module.ResourceTemplates + """ + def test_templates(self): + expected = set([ + 'latex_html.yaml', + 'zooming_image.yaml', + 'announcement.yaml', + 'anon_user_id.yaml']) + got = set((t['template_id'] for t in TestClass.templates())) + self.assertEqual(expected, got) + + def test_templates_no_suchdir(self): + self.assertEqual(len(TestClass2.templates()), 0) + + def test_get_template(self): + self.assertEqual( + TestClass.get_template('latex_html.yaml')['template_id'], + 'latex_html.yaml') + + +class TestClass(ResourceTemplates): + """ + Derives from the class under test for testing purposes. + + Since `ResourceTemplates` is intended to be used as a mixin, we need to + derive a class from it in order to fill in some data it's expecting to find + in its mro. + """ + template_packages = [__name__] + + @classmethod + def get_template_dir(cls): + return 'templates/test' + + +class TestClass2(TestClass): + """ + Like TestClass, but `get_template_dir` returns a directory that doesn't + exist. + + See `TestClass`. + """ + + @classmethod + def get_template_dir(cls): + return 'foo' diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index e7bc5c47fb..ac91ecb119 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -6,7 +6,12 @@ import yaml from functools import partial from lxml import etree from collections import namedtuple -from pkg_resources import resource_listdir, resource_string, resource_isdir +from pkg_resources import ( + resource_exists, + resource_listdir, + resource_string, + resource_isdir, +) from webob import Response from webob.multidict import MultiDict @@ -14,7 +19,7 @@ from xblock.core import XBlock from xblock.fields import Scope, Integer, Float, List, XBlockMixin, String from xblock.fragment import Fragment from xblock.plugin import default_select -from xblock.runtime import Runtime, MemoryIdManager +from xblock.runtime import Runtime from xmodule.fields import RelativeTime from xmodule.errortracker import exc_info_to_str @@ -508,6 +513,8 @@ class ResourceTemplates(object): Gets the templates associated w/ a containing cls. The cls must have a 'template_dir_name' attribute. It finds the templates as directly in this directory under 'templates'. """ + template_packages = [__name__] + @classmethod def templates(cls): """ @@ -520,14 +527,17 @@ class ResourceTemplates(object): templates = [] dirname = cls.get_template_dir() if dirname is not None: - for template_file in resource_listdir(__name__, dirname): - if not template_file.endswith('.yaml'): - log.warning("Skipping unknown template file %s", template_file) + for pkg in cls.template_packages: + if not resource_isdir(pkg, dirname): continue - template_content = resource_string(__name__, os.path.join(dirname, template_file)) - template = yaml.safe_load(template_content) - template['template_id'] = template_file - templates.append(template) + for template_file in resource_listdir(pkg, dirname): + if not template_file.endswith('.yaml'): + log.warning("Skipping unknown template file %s", template_file) + continue + template_content = resource_string(pkg, os.path.join(dirname, template_file)) + template = yaml.safe_load(template_content) + template['template_id'] = template_file + templates.append(template) return templates @@ -555,15 +565,13 @@ class ResourceTemplates(object): """ dirname = cls.get_template_dir() if dirname is not None: - try: - template_content = resource_string(__name__, os.path.join(dirname, template_id)) - except IOError: - return None - template = yaml.safe_load(template_content) - template['template_id'] = template_id - return template - else: - return None + path = os.path.join(dirname, template_id) + for pkg in cls.template_packages: + if resource_exists(pkg, path): + template_content = resource_string(pkg, path) + template = yaml.safe_load(template_content) + template['template_id'] = template_id + return template def prefer_xmodules(identifier, entry_points):