Commit f5d12a3a by Victor Shnayder

initial draft of def/instance/module proposal

parent 06aec16f
# A way to specify what parameters a type of definition can accept.
# name -- str
# default -- default value, or None if there isn't one. If there is no default value,
# the parameter is required.
# is_valid -- str -> bool A general way of specifying parameter
# types, though perhaps a more restricted but explicit
# structure is better instead/in addition.
# is_inherited -- should settings for this param inherit?
ModuleParameter = NamedTuple('ModuleParameter', 'name default is_valid is_inherited')
class XModuleDefinition(object):
@property
def accepted_parameters(self):
"""
Return a list of ModuleParameters.
"""
pass
@property
def required_capabilities(self):
"""
Returns a list of methods that the FOOSystem must support in
order to be used with this kind of definition. If this is a
container module, needs to be a union of all the descendents,
and so may change as children get added, removed, or changed.
"""
pass
@property
def child_locations(self):
"""Return a list of child locations for this definition"""
pass
class DefinitionStore(object):
"""
Store definitions.
"""
def get(self, location):
"""
Return an XModuleDefinition
"""
pass
class XModuleInstance(object):
"""
An instance of a particular XModuleDefinition, with particular
policy, in the context of a course.
"""
def __init__(self, id, definition):
"""
id -- an instance_id : name for this instance; unique within a course.
definition -- a XModuleDefinition
"""
self.id = id
self._definition = definition
@property
def definition(self):
"""
Return the XModuleDefinition for this instance
"""
pass
def get_parameter(self, param_name):
"""
Return the value of param_name for this instance. param_name must be
in for self.definition.accepted_parameters.
"""
pass
def get_children_ids(self):
"""
return the list of module_ids for the children of this instance.
"""
pass
@property
def parent_id(self):
"""
Return the instance_id of this instance's parent, or None if
this is the course root.
Always well defined because instances only appear once in the
course tree structure.
"""
pass
@property
def stores_state(self):
"""
Does this instance store state? The reasoning behind putting
this on the instance rather than the definition is that I can
imagine cases where some problems may store state in some
cases, and not in others. This may not be worth supporting.
"""
pass
def get_sample_state(self):
"""
Needed for cms.
"""
pass
def max_score(self):
"""
Can we move this to instance-level from module level? Would
make various grading things easier if grades were consistent
accross all students for any given instance. Perhaps not...
"""
pass
## To / from storage ########################################################
# all an instance is in storage is a tuple (instance_id, definition_location, children_ids)
# The definition is looked up via definition_location, the policy
# via instance_id. The children_ids are pointers to instances,
# which must correspond to the definition's children. [do not like?]
# ??? to/from xml, json?
class InstanceStore(object):
"""
Interface for accessing instances
"""
def __init__(self, instance_db, definition_store, policy_store):
"""
Needs to know where to find the instance stubs, and how to look up
definitions and policies
"""
# Lazy version:
# - if thing instance_db actually stores parent pointers
# directly, doesn't have to do anything. Mongo should.
# - if not, need to load at least a course at a time to get inheritance right.
pass
def get(course_id, instance_id):
"""
Return the specified instance. Makes sure policy inheritance is done right.
"""
class PolicyStore(object):
def get(course_id, instance_id):
"""
Return the policy set directly on this instance. Does _not_ handle inheritance.
"""
pass
# Revamping the module system
## Goals:
* Enable CMS
* Allow reuse of content definitions between courses and within a course
* Allow contributed modules that don't have to be trusted.
## Big picture:
We a notion of a module definition (`XModuleDescriptor`) and a module in the context of a particular student (`XModule`). We need to separate the first into a definition (e.g. for a problem: "what is 2+2?") and an instance of this definition ("What is 2+2?", in vert 3 of seq 1 of ch 4, with 3 attempts allowed, and worth 3 points). We already have this mental distinction, but the code isn't quite there yet.
## Proposal:
3 kinds of things: XModuleDefinition ("2+2=?"), XModuleInstance (a particular use of "2+2=?"), and XModule (an instantiation of XModuleInstance for a student).
A course consists of instances. Each instance in the course occurs exactly once, and has an instance_id which is unique within that course. An instance_id identifies a definition, a policy, and a list of child instance_ids, which should correspond to the children of the definition.
See `common/lib/xmodule/xmodule/proposal/` for code interface sketches.
An instance is uniquely identified by "org/course/run/instance_id". Since instance_id is trailing, it can slashes or any other chars we like.
### LMS integration
* Internally, all the code that works with modules doesn't really need to change much.
* Code that works with descriptors will primarily work with instances instead.
* urls -- the jump_to urls should now take an instance_id instead of a location. Those urls are not publically exposed, but if anyone does have them bookmarked for some reason, they would break unless we put in special support. I don't think we need to maintain them.
### CMS integration
* User chooses a definition, gets an instance of it, with some default instance id ('category/url_name', perhaps). Can customize parameters, put it somewhere in the course, etc. All the magic happens behind the scenes.
### XML integration
* file format/layout -- can support existing (new) format, interpret 'category/url_name' as the instance_id for everything by default.
* Can enable reuse of definitions, by separating out instance_ids from url_names. So a pointer tag would become
<category url_name='this_is_the_definition_pointer' instance_id='this_is_the_id'/>
### Modularity support....
* This set of changes doesn't have a big effect on our ability to put various things on different servers. We should still be able to implement:
* Definitions can live in various different places (e.g. partitioned by org or course)
* We can (and should) design an service API that would let us run individual module code in separate processes/servers, and sandbox as necessary.
* The required_capabilities flag may allow us to pass less stuff to modules that don't need extra things. Since I think the current plan is that edX folks will be the only ones to write container modules for now, this means that we can lock down the access modules get.
## Transition plan.
* How do we get there from here?
* We should be able to maintain backwards compatibility throughout--I would suggest writing the new stuff straighforwardly, and having a separate import-from-old-format module that does a conversion before loading (possibly even during the build).
* capa_module/problem needs to be refactored to separate definition from student state.
## Design plan:
* I've been finding myself adding convenience methods to the XModule and XModuleDefinition interfaces over the last few months (e.g. get_child_by_url_name, compute_inherited_metadata). It may be better to keep the core interface small, and add those to a separate util class.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment