Source code for devilry.apps.gradeeditors.registry
"""
.. attribute:: gradeeditor_registry
A :class:`Registry`-object.
"""
import json
from django.conf import settings
from django.core.exceptions import ValidationError
[docs]class ConfigValidationError(ValidationError):
"""
Raised when :meth:`RegistryItem.validate_config` fails to validate the
configstring.
"""
[docs]class DraftValidationError(ValidationError):
"""
Raised when :meth:`RegistryItem.validate_draft` fails to validate the
draftstring.
"""
[docs]class RegistryItem(object):
"""
Information about a grade plugin.
The attributes documented below are required.
.. attribute:: gradeeditorid
A unique string for this editor. If two editors with the same
gradeeditorid is registered, an exception will be raised on load time.
.. attribute:: title
A short title for the grade editor.
.. attribute:: description
A longer description of the grade editor.
.. attribute:: config_editor_url
The URL to the config editor.
.. attribute:: draft_editor_url::
The URL to the draft editor.
"""
def __str__(self):
return self.title
@classmethod
[docs] def validate_config(cls, configstring):
"""
Validate ``configstring`` and raise :exc:`ConfigValidationError` if it
does not validate.
"""
raise NotImplementedError('This grade plugin does not support configuration')
@classmethod
[docs] def validate_draft(cls, draftstring):
"""
Validate ``draftstring`` and raise :exc:`DraftValidationError` if the validation fails.
"""
raise NotImplementedError()
@classmethod
[docs] def draft_to_staticfeedback_kwargs(cls, draftstring):
"""
Convert ``draftstring`` into a dictionary of keyword arguments for StaticFeedback.
The returned dict should only contain the following keys:
- is_passing_grade
- grade
- points
- rendered_view
"""
raise NotImplementedError()
@classmethod
def asinfodict(cls):
return dict(gradeeditorid = cls.gradeeditorid,
title = cls.title,
description = cls.description,
config_editor_url = cls.config_editor_url,
draft_editor_url = cls.draft_editor_url)
[docs]class JsonRegistryItem(RegistryItem):
""" RegistryItem with extra utility functions for use with JSON config and draft strings """
@classmethod
[docs] def decode_configstring(cls, configstring):
""" Decode ``configstring`` using ``json.loads`` and return the result.
Raise ConfigValidationError if it fails. """
try:
return json.loads(configstring)
except ValueError, e:
raise ConfigValidationError('Could not decode config string as JSON.')
@classmethod
[docs] def decode_draftstring(cls, draftstring):
""" Decode ``draftstring`` using ``json.loads`` and return the result.
Raise DraftValidationError if it fails. """
try:
return json.loads(draftstring)
except ValueError, e:
raise DraftValidationError('Could not decode config string as JSON.')
@classmethod
def validate_gradeeditor_key(cls, draftdct, expectedid):
if not 'gradeeditor' in draftdct:
raise DraftValidationError('The draftdct must contain the "gradeeditor" key.')
gradeeditor = draftdct['gradeeditor']
if not 'id' in gradeeditor or not 'version' in gradeeditor:
raise DraftValidationError('The gradeeditor key must contain an object with id and version as keys.')
if gradeeditor['id'] != expectedid:
raise DraftValidationError('The gradeeditor id must be "{0}"'.format(expectedid))
return gradeeditor
@classmethod
[docs] def validate_dict(cls, valuedict, exceptioncls, typedict):
"""
Validate that each key in ``typedict`` is in ``valuedict``, and that
the type of the values in ``valuedict`` reflects the types in
``typedict``.
Raise ``exceptioncls`` with an error message if any validation fails.
"""
if not isinstance(valuedict, dict):
raise DraftValidationError('The draft string must contain a json map/object/dict.')
for key, typecls in typedict.iteritems():
if not key in valuedict:
raise exceptioncls('{0} is required.'.format(key))
if not isinstance(valuedict[key], typecls):
raise exceptioncls('{0} must be of type: {1}.'.format(key, str(typecls)[7:-2]))
[docs]class Registry(object):
"""
Grade editor registry. You **should not** create a object of this class.
It is already available as :attr:`gradeeditor_registry`.
"""
def __init__(self):
self._registry = {}
[docs] def register(self, registryitem):
"""
Add a :class:`RegistryItem` to the registry.
"""
if registryitem.gradeeditorid in self._registry:
raise ValueError('More than one gradeeditor with gradeeditorid: {0}'.format(registryitem.gradeeditorid))
self._registry[registryitem.gradeeditorid] = registryitem
def __getitem__(self, gradeeditorid):
return self._registry[gradeeditorid]
[docs] def getdefaultkey(self):
"""
Get the default key (the key defined in the DEVILRY_DEFAULT_GRADEEDITOR setting).
"""
return settings.DEVILRY_DEFAULT_GRADEEDITOR
[docs] def itertitles(self):
""" Iterate over the registry yielding (key, title). """
for key, item in self._registry.iteritems():
yield key, item.title
def iterinfordicts(self):
for item in self._registry.itervalues():
yield item.asinfodict()
gradeeditor_registry = Registry()