some bug fixes concerning update of SCORM tracks

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1910 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-08-12 08:58:34 +00:00
parent 8f96dca0a4
commit 5f290c0723
3 changed files with 74 additions and 24 deletions

View file

@ -29,7 +29,7 @@ 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.comments_from_learner.comment', 'Hello SCORM')
>>> rc = api.setValue('cmi.interactions.1.id', 'q009')
>>> rc = api.setValue('cmi.interactions.1.result', 'incorrect')
@ -40,13 +40,13 @@ track for each interaction and one additional 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': ''}
{'cmi.comments_from_learner.comment': 'Hello SCORM', 'key_prefix': ''}
{'id': 'q009', 'key_prefix': 'cmi.interactions.1', 'result': 'incorrect'}
Using the getValue() method we can retrieve certain values without having
to care about the storage in different track objects.
>>> api.getValue('cmi.comments_from_learner')
>>> api.getValue('cmi.comments_from_learner.comment')
('Hello SCORM', '0')
>>> api.getValue('cmi.interactions.0.id')
('q007', '0')
@ -66,3 +66,40 @@ We can also query special elements like _count and _children.
>>> api.getValue('cmi.objectives.5.score._children')
(('scaled', 'raw', 'min', 'max'), '0')
We may also update existing tracks using the ``setValue()`` method.
>>> rc = api.setValue('cmi.comments_from_learner.location', 'q007')
>>> 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.location': 'q007',
'cmi.comments_from_learner.comment': 'Hello SCORM', 'key_prefix': ''}
{'id': 'q009', 'key_prefix': 'cmi.interactions.1', 'result': 'incorrect'}
With the ``setValues()`` method we may set more than one element with
one call. (This is not a SCORM-compliant call but is provided for efficiency
reasons as it allows us to update a bunch of elements with just one
XML-RPC call.)
>>> data = {'cmi.interactions.2.result': 'correct',
... 'cmi.interactions.2.learner_response': 'my answer',
... }
>>> rc = api.setValues(data)
>>> 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.location': 'q007',
'cmi.comments_from_learner.comment': 'Hello SCORM', 'key_prefix': ''}
{'id': 'q009', 'key_prefix': 'cmi.interactions.1', 'result': 'incorrect'}
{'result': 'correct', 'key_prefix': 'cmi.interactions.2',
'learner_response': 'my answer'}
>>> api.getValue('cmi.interactions.2.result')
('correct', '0')
>>> api.getValue('cmi.interactions.2.learner_response')
('my answer', '0')
>>> api.getValue('cmi.comments_from_learner.comment')
('Hello SCORM', '0')
>>> api.getValue('cmi.comments_from_learner.location')
('q007', '0')

View file

@ -80,18 +80,20 @@ class ScormAPI(object):
def setValue(self, element, value):
tracks = self.context.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.context.saveUserTrack(self.taskId, self.runId, self.userId, data,
update=update)
track = self._getTrack(tracks, prefix)
data = track is not None and track.data or {}
data[key] = value
if track is None:
data['key_prefix'] = prefix
self.context.saveUserTrack(self.taskId, self.runId, self.userId, data)
else:
self.context.updateTrack(track, data)
return OK
def setValues(self, mapping={}, **kw):
mapping.update(kw)
# TODO: optimize, i.e. retrieve existing tracks only once.
for key, value in mapping:
for key, value in mapping.items():
rc = self.setValue(key, value)
if rc != OK:
return rc
@ -104,13 +106,18 @@ class ScormAPI(object):
if element.startswith('cmi.interactions.'):
return self._countSubtracks(tracks, base), OK
else:
data = self._getTrackData(tracks, '')
return self._countSubelements(data, base), OK
track = self._getTrack(tracks, '')
if track is None:
return 0, OK
return self._countSubelements(track.data, base), OK
if element.endswith('_children'):
base = element[:-len('._children')]
return self._getChildren(base)
prefix, key = self._splitKey(element)
data = self._getTrackData(tracks, prefix)
track = self._getTrack(tracks, prefix)
if track is None:
return '', '403'
data = track.data
if key in data:
return data[key], OK
else:
@ -130,11 +137,11 @@ class ScormAPI(object):
return '.'.join(parts[:3]), '.'.join(parts[3:])
return '', element
def _getTrackData(self, tracks, prefix):
def _getTrack(self, tracks, prefix):
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 {}
return tr
return None
def _countSubelements(self, data, element):
result = set()

View file

@ -113,12 +113,8 @@ class TrackingStorage(BTreeContainer):
trackNum = 0
if update:
track = self.getLastUserTrack(taskId, runId, userName)
if track:
trackId = str(track.__name__)
trackNum = int(trackId)
track.data.update(data)
self.indexTrack(trackNum, track)
return trackId
if track is not None:
return self.updateTrack(track, data)
if not trackNum:
self.trackNum += 1
trackNum = self.trackNum
@ -128,6 +124,13 @@ class TrackingStorage(BTreeContainer):
self.indexTrack(trackNum, track)
return trackId
def updateTrack(self, track, data):
trackId = str(track.__name__)
trackNum = int(trackId)
track.update(data)
self.indexTrack(trackNum, track)
return trackId
def indexTrack(self, trackNum, track):
md = track.metadata
for attr in self.indexAttributes:
@ -152,7 +155,8 @@ class TrackingStorage(BTreeContainer):
tracks = self.getUserTracks(taskId, runId, userName)
if tracks:
return sorted(tracks, key=lambda x: x.timeStamp)[-1]
else: return None
else:
return None
def query(self, **kw):
result = None
@ -195,8 +199,10 @@ class Track(Persistent):
self.timeStamp = getTimeStamp()
self.data = data
def update(self, data):
def update(self, newData):
self.timeStamp = getTimeStamp()
data = self.data
data.update(newData)
self.data = data
def __repr__(self):