added cybertools.scorm package
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1711 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
618ea75bb5
commit
9ca359ca67
5 changed files with 272 additions and 0 deletions
40
scorm/README.txt
Normal file
40
scorm/README.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
===================
|
||||
A generic SCORM API
|
||||
===================
|
||||
|
||||
($Id$)
|
||||
|
||||
In order to work with the SCORM API we first need a tracking storage.
|
||||
|
||||
>>> from cybertools.tracking.btree import TrackingStorage
|
||||
>>> tracks = TrackingStorage()
|
||||
|
||||
We can now create a ScormAPI adapter. Note that this adapter is stateless
|
||||
as it is usually created anew upon each request.
|
||||
|
||||
>>> from cybertools.scorm.base import ScormAPI
|
||||
>>> api = ScormAPI(tracks, 'a001', 0, 'user1')
|
||||
|
||||
The first step is always the initialize() call - though in our case it
|
||||
does not do anything.
|
||||
|
||||
>>> api.initialize()
|
||||
'0'
|
||||
|
||||
Then we can set some values.
|
||||
|
||||
>>> rc = api.setValue('cmi.interactions.0.id', 'q007')
|
||||
>>> rc = api.setValue('cmi.interactions.0.result', 'correct')
|
||||
>>> rc = api.setValue('cmi.comments_from_learner', 'Hello SCORM')
|
||||
>>> rc = api.setValue('cmi.interactions.1.id', 'q009')
|
||||
>>> rc = api.setValue('cmi.interactions.1.result', 'incorrect')
|
||||
|
||||
Depending on the data elements the values entered are kept together in
|
||||
one track or stored in separate track objects. So there is a separate
|
||||
track for each interaction and one track for all the other elements.
|
||||
|
||||
>>> for t in sorted(tracks.values(), key=lambda x: x.timeStamp):
|
||||
... print t.data
|
||||
{'id': 'q007', 'key_prefix': 'cmi.interactions.0', 'result': 'correct'}
|
||||
{'cmi.comments_from_learner': 'Hello SCORM', 'key_prefix': ''}
|
||||
{'id': 'q009', 'key_prefix': 'cmi.interactions.1', 'result': 'incorrect'}
|
4
scorm/__init__.py
Normal file
4
scorm/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
"""
|
||||
$Id$
|
||||
"""
|
||||
|
110
scorm/base.py
Normal file
110
scorm/base.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
#
|
||||
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
Base classes for providing a generic SCORM-compliant API.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope import interface
|
||||
from zope.interface import implements
|
||||
|
||||
from cybertools.scorm.interfaces import IScormAPI
|
||||
from cybertools.tracking.btree import TrackingStorage
|
||||
|
||||
|
||||
OK = '0'
|
||||
|
||||
|
||||
class ScormAPI(object):
|
||||
""" ScormAPI objects are temporary adapters created by
|
||||
browser or XML-RPC views.
|
||||
"""
|
||||
|
||||
implements(IScormAPI)
|
||||
|
||||
def __init__(self, storage, taskId, runId, userId):
|
||||
self.taskId = taskId
|
||||
self.runId = runId
|
||||
self.userId = userId
|
||||
self.storage = storage
|
||||
|
||||
def initialize(self, parameter=''):
|
||||
# Note that the run has already been started upon SCO launch, the runId
|
||||
# usually being part of the URI or XML-RPC call arguments.
|
||||
return OK
|
||||
|
||||
def terminate(self, parameter=''):
|
||||
rc = self.commit()
|
||||
if rc == OK:
|
||||
self.storage.stopRun(self.taskId, self.runId)
|
||||
return rc
|
||||
|
||||
def commit(self, parameter=''):
|
||||
return OK
|
||||
|
||||
def setValue(self, element, value):
|
||||
tracks = self.storage.getUserTracks(self.taskId, self.runId, self.userId)
|
||||
prefix, key = self._splitKey(element)
|
||||
data = self._getTrackData(tracks, prefix) or {}
|
||||
update = bool(data)
|
||||
data['key_prefix'] = prefix
|
||||
data.update({key: value})
|
||||
self.storage.saveUserTrack(self.taskId, self.runId, self.userId, data,
|
||||
update=update)
|
||||
return OK
|
||||
|
||||
def setValues(self, mapping={}, **kw):
|
||||
mapping.update(kw)
|
||||
# TODO: optimize, i.e. retrieve existing tracks only once.
|
||||
for key, value in mapping:
|
||||
rc = self.setValue(key, value)
|
||||
if rc != OK:
|
||||
return rc
|
||||
return OK
|
||||
|
||||
def getValue(self, element):
|
||||
tracks = self.storage.getUserTracks(self.taskId, self.runId, self.userId)
|
||||
prefix, key = self._splitKey(element)
|
||||
data = self._getTrackData(tracks, prefix) or {}
|
||||
if key in data:
|
||||
return data[key], OK
|
||||
else:
|
||||
return '', '403'
|
||||
|
||||
def getErrorString(self, errorCode):
|
||||
return ''
|
||||
|
||||
def getDiagnostic(self, code):
|
||||
return ''
|
||||
|
||||
# helper methods
|
||||
|
||||
def _splitKey(self, element):
|
||||
if element.startswith('cmi.interactions.'):
|
||||
parts = element.split('.')
|
||||
return '.'.join(parts[:3]), '.'.join(parts[3:])
|
||||
return '', element
|
||||
|
||||
def _getTrackData(self, tracks, prefix):
|
||||
track = None
|
||||
for tr in reversed(sorted(tracks, key=lambda x: x.timeStamp)):
|
||||
if tr and tr.data.get('key_prefix', None) == prefix:
|
||||
return tr.data
|
||||
return {}
|
96
scorm/interfaces.py
Normal file
96
scorm/interfaces.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
#
|
||||
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
SCORM interface definitions for API_1484_11.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.interface import Interface, Attribute
|
||||
from zope import schema
|
||||
|
||||
|
||||
class IScormAPI(Interface):
|
||||
""" This interface represents a server-side adapter object for a
|
||||
tracking storage and a set of key/meta data that identify a
|
||||
learner session with one or more track objects. IScormAPI objects
|
||||
are stateless, so they don't remember any values between calls.
|
||||
|
||||
In addition to the standard SCORM RTS methods there is a setValues()
|
||||
method that allows setting more than one value in one call,
|
||||
probably during execution of a Commit() call on the client
|
||||
side.
|
||||
|
||||
There is no method corresponding to GetLastError() as the
|
||||
methods immediately return an appropriate CMIErrorCode,
|
||||
i.e. a '0' when OK.
|
||||
|
||||
Note that the names of the methods have been slightly modified
|
||||
to correspond to the Python programming style guides.
|
||||
"""
|
||||
|
||||
taskId = Attribute('Task ID')
|
||||
runId = Attribute('Run ID (integer)')
|
||||
userId = Attribute('User ID')
|
||||
|
||||
def initialize(parameter):
|
||||
""" Corresponds to API.Initialize('').
|
||||
Return CMIErrorCode.
|
||||
"""
|
||||
|
||||
def commit(parameter):
|
||||
""" Corresponds to API.Commit('').
|
||||
Return CMIErrorCode.
|
||||
"""
|
||||
|
||||
def terminate(parameter):
|
||||
""" Corresponds to API.Initialize('').
|
||||
Mark the run as finished.
|
||||
Return CMIErrorCode.
|
||||
"""
|
||||
|
||||
def setValue(element, value):
|
||||
""" Corresponds to API.SetValue(element, value).
|
||||
Return CMIErrorCode.
|
||||
"""
|
||||
|
||||
def setValues(mapping={}, **kw):
|
||||
""" Combine the mapping and kw arguments setting up a series of
|
||||
element-value mappings that will in turn be applied to a
|
||||
series of setValue() calls.
|
||||
Return CMIErrorCode.
|
||||
"""
|
||||
|
||||
def getValue(element):
|
||||
""" Corresponds to API.GetValue(element).
|
||||
Return a tuple with the current value of the element given
|
||||
(a string, '' if not present) and a CMIErrorCode.
|
||||
"""
|
||||
|
||||
def getErrorString(errorCode):
|
||||
""" Corresponds to API.GetErrorString(errorCode).
|
||||
Return the error text belonging to the errorCode
|
||||
(a CMIErrorCode value) given.
|
||||
"""
|
||||
|
||||
def getDiagnostic(code):
|
||||
""" Corresponds to API.GetDiagnostic(code).
|
||||
Return an LMS-specific information text related to the code given;
|
||||
code may but need not be a CMIErrorCode value.
|
||||
"""
|
22
scorm/tests.py
Executable file
22
scorm/tests.py
Executable file
|
@ -0,0 +1,22 @@
|
|||
# $Id$
|
||||
|
||||
import unittest, doctest
|
||||
from zope.testing.doctestunit import DocFileSuite
|
||||
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
"Basic tests for the cybertools.scorm package."
|
||||
|
||||
def testBasics(self):
|
||||
pass
|
||||
|
||||
|
||||
def test_suite():
|
||||
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
return unittest.TestSuite((
|
||||
unittest.makeSuite(Test),
|
||||
DocFileSuite('README.txt', optionflags=flags),
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
Loading…
Add table
Reference in a new issue