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:
parent
8f96dca0a4
commit
5f290c0723
3 changed files with 74 additions and 24 deletions
|
@ -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')
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Add table
Reference in a new issue