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.id', 'q007')
>>> rc = api.setValue('cmi.interactions.0.result', 'correct') >>> 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.id', 'q009')
>>> rc = api.setValue('cmi.interactions.1.result', 'incorrect') >>> 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): >>> for t in sorted(tracks.values(), key=lambda x: x.timeStamp):
... print t.data ... print t.data
{'id': 'q007', 'key_prefix': 'cmi.interactions.0', 'result': 'correct'} {'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'} {'id': 'q009', 'key_prefix': 'cmi.interactions.1', 'result': 'incorrect'}
Using the getValue() method we can retrieve certain values without having Using the getValue() method we can retrieve certain values without having
to care about the storage in different track objects. 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') ('Hello SCORM', '0')
>>> api.getValue('cmi.interactions.0.id') >>> api.getValue('cmi.interactions.0.id')
('q007', '0') ('q007', '0')
@ -66,3 +66,40 @@ We can also query special elements like _count and _children.
>>> api.getValue('cmi.objectives.5.score._children') >>> api.getValue('cmi.objectives.5.score._children')
(('scaled', 'raw', 'min', 'max'), '0') (('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): def setValue(self, element, value):
tracks = self.context.getUserTracks(self.taskId, self.runId, self.userId) tracks = self.context.getUserTracks(self.taskId, self.runId, self.userId)
prefix, key = self._splitKey(element) prefix, key = self._splitKey(element)
data = self._getTrackData(tracks, prefix) or {} track = self._getTrack(tracks, prefix)
update = bool(data) data = track is not None and track.data or {}
data['key_prefix'] = prefix data[key] = value
data.update({key: value}) if track is None:
self.context.saveUserTrack(self.taskId, self.runId, self.userId, data, data['key_prefix'] = prefix
update=update) self.context.saveUserTrack(self.taskId, self.runId, self.userId, data)
else:
self.context.updateTrack(track, data)
return OK return OK
def setValues(self, mapping={}, **kw): def setValues(self, mapping={}, **kw):
mapping.update(kw) mapping.update(kw)
# TODO: optimize, i.e. retrieve existing tracks only once. # 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) rc = self.setValue(key, value)
if rc != OK: if rc != OK:
return rc return rc
@ -104,13 +106,18 @@ class ScormAPI(object):
if element.startswith('cmi.interactions.'): if element.startswith('cmi.interactions.'):
return self._countSubtracks(tracks, base), OK return self._countSubtracks(tracks, base), OK
else: else:
data = self._getTrackData(tracks, '') track = self._getTrack(tracks, '')
return self._countSubelements(data, base), OK if track is None:
return 0, OK
return self._countSubelements(track.data, base), OK
if element.endswith('_children'): if element.endswith('_children'):
base = element[:-len('._children')] base = element[:-len('._children')]
return self._getChildren(base) return self._getChildren(base)
prefix, key = self._splitKey(element) 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: if key in data:
return data[key], OK return data[key], OK
else: else:
@ -130,11 +137,11 @@ class ScormAPI(object):
return '.'.join(parts[:3]), '.'.join(parts[3:]) return '.'.join(parts[:3]), '.'.join(parts[3:])
return '', element return '', element
def _getTrackData(self, tracks, prefix): def _getTrack(self, tracks, prefix):
for tr in reversed(sorted(tracks, key=lambda x: x.timeStamp)): for tr in reversed(sorted(tracks, key=lambda x: x.timeStamp)):
if tr and tr.data.get('key_prefix', None) == prefix: if tr and tr.data.get('key_prefix', None) == prefix:
return tr.data return tr
return {} return None
def _countSubelements(self, data, element): def _countSubelements(self, data, element):
result = set() result = set()

View file

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